自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MVP模式在攜程酒店的應(yīng)用和擴展

移動開發(fā) Android
MVP模式是目前客戶端比較流行的框架模式,攜程在很早之前就開始探索使用該模式進行相關(guān)的業(yè)務(wù)功能開發(fā),以提升代碼的規(guī)范性和可維護性,積累了一定的經(jīng)驗。本文將探討一下該模式在實際工程中的優(yōu)點和缺陷,并介紹攜程面對這些問題時的思考,解決方案以及在實踐經(jīng)驗基礎(chǔ)上對該模式的擴展模式MVCPI。

前言

酒店業(yè)務(wù)部門是攜程旅行的幾大業(yè)務(wù)之一,其業(yè)務(wù)邏輯復(fù)雜,業(yè)務(wù)需求變動快,經(jīng)過多年的研發(fā),已經(jīng)是一個代碼規(guī)模龐大的工程,如何規(guī)范代碼,將代碼按照其功能進行分類,將代碼寫到合適的地方對項目的迭代起著重要的作用。

MVP模式是目前客戶端比較流行的框架模式,攜程在很早之前就開始探索使用該模式進行相關(guān)的業(yè)務(wù)功能開發(fā),以提升代碼的規(guī)范性和可維護性,積累了一定的經(jīng)驗。本文將探討一下該模式在實際工程中的優(yōu)點和缺陷,并介紹攜程面對這些問題時的思考,解決方案以及在實踐經(jīng)驗基礎(chǔ)上對該模式的擴展模式MVCPI。

一、從MVC說起

MVC已經(jīng)是非常成熟的框架模式,甚至不少人認為它過時陳舊老氣,在實踐中,很多同事會抱怨,MVC會使得代碼非常臃腫,尤其是Controller很容易變成大雜燴,預(yù)期的可維護性變得很脆弱,由此導(dǎo)致一方面希望有新框架模式可以解決現(xiàn)在的問題,但同時對框架模式又有些懷疑,新的框架模式是否能真正解決現(xiàn)在的問題?會不會重蹈覆轍?會不會過度設(shè)計?會不會掉進一個更深的坑?總之,這些類似“一朝被蛇咬,十年怕井繩”的擔(dān)憂顯得不無道理。但不管如何,我們需要仔細耐心的做工作。

1.1、被誤解的MVC

在MVP模式逐漸流行之前,不管我們有意識或無意識地,我們使用的就是MVC模式。以Android為例,我們來看看MVC是什么樣子。

  1. public class HotelActivity extends Activity {     
  2.       private TextView mNameView;  
  3.       private TextView mAddressView;   
  4.       private TextView mStarView;  
  5.       @Override     
  6.       protected void onCreate(Bundle savedInstanceState) {              
  7.           super.onCreate(savedInstanceState);             
  8.           setContentView(R.layout.activity_main2);     
  9.  
  10.           mNameView = (TextView) findViewById(R.id.hotel_view);               
  11.           mAddressView = (TextView) findViewById(R.id.address_view);           
  12.           mStarView = (TextView) findViewById(R.id.star_view); 
  13.  
  14.           HotelModel hotel = HotelLoader.loadHotelById(1000); 
  15.  
  16.           mHotelNameView.setText(hotel.hotelName);        
  17.           mHotelAddressView.setText(hotel.hotelAdress);         
  18.           mHotelStarView.setText(hotel.hotelStar); 
  19.       } 
  20.  

上面的代碼,概括了Android MVC的基本結(jié)構(gòu),從筆者的經(jīng)驗來看,很多應(yīng)用都存在這樣的代碼風(fēng)格,也就是大部分人認為的MVC:

  • Model:
  1. Hotel,HotelLoader 
  • Controller:
  1. HotelActivity 
  • View:
  1. mHotelNameView 
  2.  
  3. mHotelAddressViewmHotelStarView  

可以試想一下如果這個界面展示的數(shù)據(jù)非常的多話,MainActivity必然會變得非常龐大,就像大部分人所抱怨的那樣。誠然,上面的demo是MVC模式,但是,它僅是從系統(tǒng)框架的角度來看,如果從應(yīng)用框架來看,它不是。下面來看一下,從應(yīng)用框架來看一下MVC正確的結(jié)構(gòu):

1.2、MVC的正確姿勢

應(yīng)用中的MVC應(yīng)該在系統(tǒng)的MVC框架上根據(jù)業(yè)務(wù)的自身的需要進行進一步封裝,也就是說,如果在我們宣稱我們是使用MVC框架模式的時候,代表我們的主要工作是封裝自己的MVC組件。它看起來應(yīng)該是像下面的風(fēng)格:

  1. public class HotelActivity extends Activity { 
  2.  
  3.     private HotelView mHotelView; 
  4.  
  5.     @Override    
  6.     protected void onCreate(Bundle savedInstanceState) {         
  7.         super.onCreate(savedInstanceState);          
  8.         setContentView(R.layout.activity_main2); 
  9.         mHotelView = (HotelView) findViewById(R.id.hotel_view); 
  10.         HotelModel hotel = HotelLoader.loadHotelById(1000); 
  11.         mHotelView.setHotel(hotel);    
  12.      } 
  13.  

跟之前的代碼相比,基本結(jié)構(gòu)是相似的,如下:

  • Model:
  1. Hotel,HotelLoader 
  • Controller:
  1. HotelActivity 
  • View:
  1. mHotelView 

僅僅View層發(fā)生了變化,這是因為,Model和Controller相對是大家容易理解的概念,在面臨任何一個業(yè)務(wù)需求的時候,自然就能產(chǎn)生的近乎本能的封裝(盡管Model的基本封裝大部分工程師都可完成,但不可否認Model的設(shè)計是至關(guān)重要而有難度的);而對View的看法,可能就是“能正確布局和展示就行”。但這正是關(guān)鍵所在:我們需要對界面進行全方位的封裝,包括View。具體來說,一個真正的MVC框架應(yīng)該具備下面的特點:

  • 數(shù)據(jù)都由Model進行封裝
  • View綁定業(yè)務(wù)實體,view.setXXX
  • Controller不管理與業(yè)務(wù)無關(guān)的View

1.3 MVC模式的問題所在

前面說到,很多人抱怨采用MVC模式使得Controller變得很臃腫,我相信,Controller變得臃腫是事實,但其歸結(jié)于采用MVC模式是不正確的,這個鍋不應(yīng)該由MVC來背,因為,這個論點會導(dǎo)致我們走向錯誤的方向從而無法發(fā)現(xiàn)MVC真正的問題所在。為什么這么說呢,那是因為在本人了解到的很多情況下,大家并沒有正確理解MVC框架模式,如采用前文中第一種模式,自然會使得Controller臃腫,但是如果采用第二種模式,Controller的代碼和邏輯也會非常清晰,至少不至于如此多的抱怨。因此如果只是想解決Controller臃腫的話,MVC就夠了,毋庸質(zhì)疑。那MVC的問題是什么呢?我想只有深刻的理解了這個問題,我們才有必要考慮是否需要引入新的框架模式,以及避免新的模式中可能出現(xiàn)的問題。

View強依賴于Model是MVC的主要問題。由此導(dǎo)致很多控件都是根據(jù)業(yè)務(wù)定制,從Android的角度來看,原本可以由一個通用的layout就能實現(xiàn)的控件,由于要綁定實體模型,現(xiàn)在必須要自定義控件,這導(dǎo)致出現(xiàn)大量不必要的重復(fù)代碼。因此有必要將View和Model進行解耦,而MVP的主要思想就是解耦View和Model。由此引入MVP就顯得很自然。

二、 Android MVP

2.1、參考實現(xiàn)

Android 官方提供的MVP參考實現(xiàn),大致思想如下:

1、抽象出IView接口,規(guī)范控件訪問方法,而不限View具體來源

  1. public interface IHotelView { 
  2.  
  3.   public TextView getNameView(); 
  4.  
  5.   public TextView getAddressView(); 
  6.  
  7.   public TextView getStarView(); 
  8.    
  9.  

2、抽象出IPresenter接口,定義IView 和 Model的綁定接口

  1. public interface IHotelPresenter { 
  2.  
  3.   public void setView(IHotelView hotelView); 
  4.   
  5.   public void setData(HotelMotel hotel); 
  6.  
  7.  

3、IPresenter的實現(xiàn)類,實施數(shù)據(jù)和IView的綁定,并負責(zé)相關(guān)的業(yè)務(wù)處理 

  1. public class HotelPresenter implements IHotelPresenter { 
  2.  
  3.   private IHotelView hotelView; 
  4.  
  5.   public void setView(IHotelView hotelView) { 
  6.  
  7.     this.hotelView = hotelView; 
  8.  
  9.   } 
  10.  
  11.   public void setData(HotelModel hotel) { 
  12.  
  13.     hotelView.getNameView().setText(hotel.hotelName); 
  14.  
  15.     hotelView.getAddressView().setText(hotel.hotelAddress); 
  16.  
  17.     hotelView.getStarView().setText(hotel.hotelStart); 
  18.  
  19.   } 
  20.  
  21.  

4、Activity實現(xiàn)IView,角色轉(zhuǎn)變?yōu)閂iew,弱化Controller的功能

  1. public class HotelActivity extends Activity  implements IHotelView { 
  2.     @Override    
  3.     protected void onCreate(Bundle savedInstanceState) {        
  4.         super.onCreate(savedInstanceState);        
  5.         setContentView(R.layout.activity_main2);       
  6.  
  7.         HotelModel hotel = HotelLoader.loadHotelById(1000);        
  8.         IPresenter presenter = new Presenter();           
  9.         presenter.setView(this);  
  10.         presenter.setData(hotel); 
  11.  
  12.     }  
  13.     @Override      
  14.        public TextView getNameView() { 
  15.             return (TextView)findViewById(R.id.hotel_name_view);  
  16.     } 
  17.     @Override    
  18.     public TextView getAddressView() { 
  19.         return (TextView)findViewById(R.id.hotel_address_view);   
  20.     } 
  21.     @Override     
  22.     public TextView getStarView() { 
  23.         return (TextView)findViewById(R.id.hotel_address_view);  
  24.     } 
  25.  

上述代碼,主要的特點可以概括為:

  • 面向接口
  • View – Model 解耦
  • Activity角色轉(zhuǎn)換

就目前了解到的情況來看,很多采用MVP模式的應(yīng)用基本上和android參考實現(xiàn)方案差別不大,說明該模式的應(yīng)用場景也是很廣泛的。

2.2 Android MVP存在的問題

盡管已經(jīng)有了大量的應(yīng)用,但不可否認該模式的還是存在一些問題,這些問題在攜程的使用過程中也得到了體現(xiàn)。比如,上下文丟失問題,生命周期問題,內(nèi)存泄露問題以及大量的自定義接口,回調(diào)鏈變長等問題??梢詺w納為:

  • 業(yè)務(wù)復(fù)雜時,可能使得Activity變成更加復(fù)雜,比如要實現(xiàn)N個IView,然后寫更多個模版方法。
  • 業(yè)務(wù)復(fù)雜時,各個角色之間通信會變得很冗長和復(fù)雜,回調(diào)鏈過長。
  • Presenter處理業(yè)務(wù),讓業(yè)務(wù)變得很分散,不能全局掌握業(yè)務(wù),很難去回答某個業(yè)務(wù)究竟是在哪里處理的。
  • 用Presenter替代Controller是一個危險的做法,可能出現(xiàn)內(nèi)存泄漏,生命周期不同步,上下文丟失等問題。

以下面的這個需求來看幾個具體的示例:

詳情按鈕的展示需要服務(wù)端下發(fā)標記位控制,展示時點擊需要請求一個服務(wù),服務(wù)返回時toast提示用戶

  1. public class HotelPresenter {  
  2.     private IHotelView mHotelView; 
  3.     private Handler handler = new  Handler(getMainLooper());   
  4.     public void setData(HotelModel hotelModel) {    
  5.        View button = mHotelView.getButtonView();    
  6.        int visibility = hotelModel.showButton ? .VISIBLE :GONE;                 
  7.        button.setVisibility(visibility);   
  8.        if (hotelModel.showButton) {             
  9.            button.setOnClickListener(new View.OnClickListener() {                 
  10.            @Override                 
  11.            public void onClick(View v) {                
  12.                sendRequest();              
  13.            }        
  14.         });     
  15.      }  
  16.  
  17.      private void sendRequest() {        
  18.         new Thread() {        
  19.             public void run() { 
  20.                 Thread.sleep(15*1000); 
  21.                 handler.post(new Runnable() {          
  22.                         public void run() {                
  23.                               Toast.makeText(???) //Where is Context? 
  24.                         }  
  25.                 });    
  26.          }  
  27.        }.start();   
  28.      } 
  29.  

上述代碼表明,HotelPresenter可以處理大部分的業(yè)務(wù),但是在最后需要使用上下文的時候,出現(xiàn)了困難,因為脫離了上下文,展示一個Toast都不能實現(xiàn)

為了避免這樣的尷尬,因此改進方案如下:

  1. public class HotelPresenter {   
  2.      private IHotelView mHotelView; 
  3.      private Fragment mFragment; 
  4.      private HotelPresenter(Fragment fragment) {  
  5.        this.mFragment = fragment;  
  6.      }   
  7.      private Handler handler = new Handler(Looper.getMainLooper());    
  8.  
  9.      public void setData(HotelModel hotelModel) {   
  10.          View button = mHotelView.getButtonView();        
  11.          button.setVisibility(hotelModel.showButton ? VISIBLE :GONE);  
  12.          if (hotelModel.showButton) {             
  13.                button.setOnClickListener(new View.OnClickListener() {                  
  14.                       @Override    
  15.                       public void onClick(View v) {  
  16.                              sendRequest(); 
  17.                     } 
  18.             }); 
  19.          } 
  20.      } 
  21.  
  22.      private void sendRequest() { 
  23.         new Thread() { 
  24.             public void run() { 
  25.                 Thread.sleep(15*1000); 
  26.                 handler.post(new Runnable() { 
  27.                     public void run() { 
  28.                          Context context = mFragment.getActivity(); 
  29.                          int duration = LENGTH_SHORT; 
  30.                          //NullPointerException will occur 
  31.                          Toast.makeText(context,"成功”,duration).show();                    
  32.                     }  
  33.                }); 
  34.             }  
  35.        }.start(); 
  36.     } 
  37.  

改進的方案中,考慮到需要使用上下文,因此新增了接口傳入Fragment作為上下文,在Presenter需要時可以使用,但是,由于Fragment生命周期會了變化,可能會導(dǎo)致空指針問題。

于是新的問題又需要解決。主要是兩個思路,一個是為Presenter增加生命周期方法,在Fragment的生命周期方法里調(diào)用Presenter對應(yīng)的生命周期函數(shù),但這就讓Presenter看起來像Fragment的孫子;另外一個就是承認Presenter其實不太合適承擔(dān)Controller的職責(zé),從而提供接口給外部處理;如下:

  1. public class HotelPresenter { 
  2.        private IHotelView mHotelView; 
  3.        private Handler handler = new Handler(Looper.getMainLooper()); 
  4.        public void setData(HotelModel hotelModel) { 
  5.            View button = mHotelView.getButtonView();        
  6.            button.setVisibility(hotelModel.showButton ? VISIBLE :GONE); 
  7.            if (hotelModel.showButton) {            
  8.                 button.setOnClickListener(new View.OnClickListener() {                
  9.                 @Override  
  10.                 public void onClick(View v) { 
  11.                      if (mCallback != null) { 
  12.                           mCallback.onSendButtonClicked(); 
  13.                     } 
  14.                 }); 
  15.            } 
  16.        } 
  17.  
  18.        public interface Callback { 
  19.           public void onSendButtonClicked(); 
  20.        } 
  21.  
  22.        private Callback mCallback; 
  23.        public  void setCallback(Callback  callback) { 
  24.             mCallack = callback; 
  25.        } 

這個方案很穩(wěn)定,似乎成為了最佳的選擇。但是自定接口和回調(diào)始終有那么一點痛。

三、MVP的擴展模式MVCPI

由于前面的分析,MVP參考實現(xiàn)并不是萬能的,攜程酒店并沒有完全采用參考實現(xiàn)方案,而是結(jié)合自身的實踐經(jīng)驗思考之后設(shè)計出來的擴展方案。我們主要考慮了一下的幾個問題:

  • 如何定義View接口?
  • 如何定位Presenter ?
  • 如何對待Controller?
  • 如何解決長長的回調(diào)鏈?

通過對上述問題的思考,提出對應(yīng)的解決方法,規(guī)避前面論述的各種問題,形成了攜程酒店的MVCPI框架模式,并在多個業(yè)務(wù)場景運行,取得了較為滿意的效果。下面,詳細介紹MVCPI模式。

3.1、 IView

和Android 參考實現(xiàn)不一樣的是,我們并沒有采用強類型的接口作為表達View的方式,而是采用弱類型的接口來定義View。具體定義方式如下:

  1. public interface IView { 
  2.     //用于展示酒店名稱的控件  
  3.     int NAME_VIEW = R.id.name_view;  
  4.     //用于展示酒店地址的控件 
  5.     int ADDRESS_VIEW = R.id.address_view;   
  6.     //用于展示酒店星級的控件  
  7.     int STAR_VIEW = R.id.star_view; 
  8.     //用于展示酒店詳情入口的的控件 
  9.     int DETAIL_BUTTON = R.id.detail_button; 
  10.  

上面的接口簡潔的描述了作為業(yè)務(wù)控件的View需要具備的子控間ID,并不需要具體的實現(xiàn)類。因此也不需要Activity去實現(xiàn)這個接口,只需要在layout中申明這幾個ID的即可,極大的簡化了代碼。

3.2、 Presenter

與參考實現(xiàn)的定位不一樣,我們認為由Presenter取代Controller并不是一個好的做法,Presenter應(yīng)是Controller的補充,主要起到View和Model解耦和數(shù)據(jù)綁定的作用,所負責(zé)的控件的上的業(yè)務(wù)還是有Controller決定如何去處理。另外setView接受的參數(shù)是一般的View,而非一個接口類型,內(nèi)部根據(jù)IView定義的ID去查找子控件。如下:

  1. public class CtripHotelPresenter { 
  2.     TextView mNameView;  
  3.     TextView mAddressView; 
  4.     TextView mStarView;  
  5.     Button mDetailButton;  
  6.     public void setView(View view) {      
  7.         mNameView = (TextView)mView.findViewById(IView.NAME_VIEW);         
  8.         mAddressView = (TextView)mView.findViewById(IView.ADDRESS_VIEW);   
  9.         mStarView = (TextView) mView.findViewById(IView.STAR_VIEW);           
  10.         mDetailButton = (Button) mView.findViewById(IView.DETAIL_BUTTON);    
  11.    }  
  12.    public void setData(HotelModel hotel) {        
  13.         mNameView.setText(hotel.hotelName);         
  14.         mAddressView.setText(hotel.hotelAdress);        
  15.         mStarView.setText(hotel.hotelStar);  
  16.         int v = hotel.showButton ? View.VISIBLE : View.GONE;        
  17.         mDetailButton.setVisibility(v); 
  18.     } 
  19.  

3.3、 Interactor

Interactor是我們定義出來的擴展元素,在MVP和MVC中都沒有對應(yīng)的角色。為了闡述它的含義,我們先來看看兩個非常常見的場景。 

 

 

 

回調(diào)鏈過長

在前面介紹過,Presenter自定義接口是很多候選方案中較為合理的選擇,但相比MVC而言,MVP更容易出現(xiàn)如上圖的一種調(diào)用和回調(diào)關(guān)系(甚至更長)。維護這種回調(diào)鏈通常來說是一件非常頭痛的事情,從View的角度來看,很難知道某個事件到最后究竟完成了什么業(yè)務(wù),Acitivity也不知道到要裝配哪些回調(diào)。某個未知的新需求可能需要將該鏈條上的每個環(huán)節(jié)都增加回調(diào)。

下面來是另外一種場景,大家可以腦補一下采用上面的回調(diào)方案,回調(diào)鏈會是什么情況。 

 

 

 

交互集中型界面

在該界面有幾個特點:

  • 幾十種動態(tài)交互需求,
  • 分布于不同的模塊
  • 分布于不同深度的嵌套層次中

經(jīng)過大量版本迭代后,無論產(chǎn)品經(jīng)理,研發(fā)或者測試,都不清楚到底有哪些需求,業(yè)務(wù)邏輯是什么,寫在什么地方等等……

上述兩個場景可以得出兩個結(jié)論:

  • 排查問題非常耗時
  • 增加功能成本高,容易引致其他問題

為了解決上述兩個比較棘手的問題,我們引入了Interactor,用于描述整個界面的交互,一舉解決上述兩個問題。我們認為交互模型是一個功能模塊的重要邏輯單元,相對于實體模型來說,交互模型更加抽象,在大多數(shù)的情況,并不能引起大家的注意,但它確實是如實體一樣的存在,正是因為沒有對交互進行系統(tǒng)的描述,才導(dǎo)致上面兩種突出的問題。盡管抽象,但是交互模型本質(zhì)非常簡單,它有著和實體模型有相似的結(jié)構(gòu),示例如下:

  1. public class HotelOrderDetailListeners { 
  2.     public View.OnClickListener mBackListener; // 返回按鈕點擊事件監(jiān)聽者 
  3.     public View.OnClickListener mShareClickListener;//分享按鈕事件監(jiān)聽者 
  4.     public View.OnClickListener mConsultClickListener;//咨詢按鈕事件監(jiān)聽者 
  5.     …… 
  6.  

通過對界面整體分析后,我們建立如上的交互模型,所有的交互都在交互模型進行注冊,由交互模型統(tǒng)一管理,進而可以對整個界面的交互進行宏觀把控;然后在頁面的所有元素中共享同一個交互模型,進而各個元素不再需要自定義接口和避免建立回調(diào)鏈。最后由Controller負責(zé)組裝,進一步加強Controller的控制能力。

3.4、 MVCPI全貌

最后,整體介紹一下MVCPI的代碼結(jié)構(gòu)

1、首先定義整個界面中有哪些用戶交互,本例中就一個詳情按鈕交互

  1. public class HotelInteractor { 
  2.      //點擊詳情的事件處理器 
  3.      public View.OnClickListener mDetail; 
  4.  

2、Presenter構(gòu)造時需要傳入交互模型,內(nèi)部定義了IView接口,傳入的View中需要包含它定義的ID的控件,在bindData時,詳情按鈕的點擊不是通過匿名內(nèi)部類去處理,而是直接引用交互模型中定義的mDetail

  1. public class HotelPresenter { 
  2.    private View hotelView; 
  3.    private HotelInteractor mInteractor; 
  4.    private Button mDetailButton; 
  5.  
  6.    public HotelPresenter(HotelInteractor interactor) { 
  7.          this.mInteractor = interactor; 
  8.    } 
  9.  
  10.    private interface IView { 
  11.          int DETAIL= R.id.detail_button; 
  12.           …… 
  13.    } 
  14.    public void setView(View hotelView) { 
  15.          this.hotelView  = hotelView;  
  16.          mDetailButton= (Button)findViewById(IView. DETAIL ); 
  17.    } 
  18.    public void setData(HotelModel hotel) { 
  19.         if (hotel.showButton) { 
  20.              mDetailButton.setVisibility(View.Visibile); 
  21.              mDetailButton.setOnClickListener(mInteractor.mDetail); 
  22.         } 
  23.    } 
  24.  

3、Controller負責(zé)界面各個元素(包括交互模型)的初始化和裝配

  1. public class HotelActivity extends Activity { 
  2.     @Override 
  3.     protected void onCreate(Bundle savedInstanceState) { 
  4.         super.onCreate(savedInstanceState); 
  5.         setContentView(R.layout.activity_main2); 
  6.         HotelInteractor interactor = new HotelInteractor(); 
  7.         interactor.mDetail = new View.OnClickListener() { 
  8.              public void onClick(View view) { 
  9.                  viewHotelDetail();//處理詳情業(yè)務(wù);  
  10.             }  
  11.        }; 
  12.         HotelModel model= HotelLoader.loadHotelById(1000);        
  13.         HotelPresenter presenter = new HotelPresenter (interactor);        
  14.         View view= findViewById(R.id.hotel_view);        
  15.         presenter.setView(view); 
  16.         presenter.setData(hotel); 
  17.       } 
  18.  

四、結(jié)論

通過對MVC、MVP的介紹和研究,我們發(fā)現(xiàn)二者的關(guān)系并不是相互取代的關(guān)系,而是一種演化和改進的關(guān)系。經(jīng)實踐證明,MVC仍然具有強大的生命力,試圖用MVP取代MVC幾乎都會失敗。攜程在MVC模式基礎(chǔ)上,結(jié)合MVP思想,加入Interactor元素搭建的MVCPI框架模式,一方面將數(shù)據(jù)綁定邏輯從Controller(或者View)中分離出去,另一方面將交互模型的控制納入進來,進一步加強了Controller的控制能力。無論從代碼的簡潔性,維護性,擴展性來看,都具有較大優(yōu)勢,具有一定的實踐推廣價值。

當然,任何框架模式都不是全能的,MVCPI也存在它不足,如果有好的意見和建議,歡迎加入,一起討論推進框架模式的發(fā)展。 

責(zé)任編輯:龐桂玉 來源: Android開發(fā)中文站
相關(guān)推薦

2022-07-08 09:38:27

攜程酒店Flutter技術(shù)跨平臺整合

2022-10-21 10:40:08

攜程酒店MySQL慢查詢

2015-05-28 14:05:02

2024-09-10 16:09:58

2023-03-14 14:01:00

內(nèi)存優(yōu)化

2024-04-18 09:41:53

2022-07-15 12:58:02

鴻蒙攜程華為

2023-11-24 09:44:07

數(shù)據(jù)攜程

2022-11-29 21:00:39

技術(shù)搜索

2023-07-07 12:26:39

攜程開發(fā)

2024-03-22 15:09:32

2022-04-14 17:53:50

攜程AWS上云

2025-01-03 14:33:41

2024-02-23 12:24:00

引擎數(shù)據(jù)

2022-06-03 08:58:24

APP攜程流暢度

2018-07-20 09:42:23

Elasticsear實戰(zhàn)訂單

2019-06-11 09:45:56

物聯(lián)網(wǎng)應(yīng)用酒店IOT

2023-12-29 09:42:28

攜程開發(fā)

2024-09-25 15:37:46

2014-12-25 17:51:07

點贊
收藏

51CTO技術(shù)棧公眾號