Android十八章:設(shè)計模式SOLID五大原則

設(shè)計模式SOLID五大原則

SOLID每一個字母都代表這一種編程原則,其中

  1. S代表著單一職責(zé)原則
  2. O代表著開閉原則
  3. L代表著里氏替換原則
  4. I代表著接口隔離原則
  5. D代表著依賴倒置原則

單一職責(zé)原則

單一職責(zé)原則就是造成一個類改變的原因一個只有一個。再比如手機的電池是一個類,電池只為手機提供電源的職責(zé)。

在Android開發(fā)中,adapter類職責(zé)只負(fù)責(zé)視圖的顯示,這時候你會說adapter里面還有很多方法比如創(chuàng)建視圖,顯示視圖,提供視圖數(shù)量等。但是根據(jù)Martin的話來說變化的指針只在變化真正發(fā)生時起作用,如果沒有任何征兆,應(yīng)用單一職責(zé)原則或者其他原則是不明智的。就是說adapter創(chuàng)建和顯示視圖是這個類的職責(zé),而不應(yīng)該包括視圖顯示內(nèi)容的計算邏輯,我只要知道結(jié)果就行了。下面看例子:

//UserOrder.java
public class UserOrder {
   private double createTime;

    public double getCreateTime() {
        return createTime;
    }

    public void setCreateTime(double createTime) {
        this.createTime = createTime;
    }
  
}
//UserOrderAdapter.java
public class UserOrderAdapter extends RecyclerView.Adapter<UserOrderAdapter.UserOrderHolder> {
  List<UserOrder> mList;
    public UserOrderAdapter(List<UserOrder> list) {
        this.mList = list;
    }
  
   @Override
    public UserOrderHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_order_rv_item, parent, false);
          return new UserOrderHolder(view);
    }

    @Override
    public void onBindViewHolder(final UserOrderHolder holder, int position) {

        holder.mPayBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              UserOrder userOrder=mList.get(position);
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              holder.mCreatetime.setText(sdf.format(userOrder.getCreateTime()));
            }
        });

    }
   @Override
    public int getItemCount() {
        return mList.size();
    }
  
   public class UserOrderHolder extends RecyclerView.ViewHolder {
      
        TextView mCreatetime;

        public UserOrderHolder(View itemView) {
            super(itemView);
          mCreatetime = (TextView) itemView.findViewById(R.id.user_order_createtime);
          
        }
    }
}

↑這里的onBindViewHolder處理了視圖的邏輯,應(yīng)該把時間格式化的操作放在UserOrder類中。如下

//UserOrder.java
public class UserOrder {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private double createTime;
    public String getCreateTime() {
        return sdf.format(createTime);
    }
    public void setCreateTime(double createTime) {
        this.createTime = createTime;
    }
}
//UserOrderAdapter.java
  @Override
    public void onBindViewHolder(final UserOrderHolder holder, int position) {

        holder.mPayBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              UserOrder userOrder=mList.get(position);
              holder.mCreatetime.setText(userOrder.getCreateTime());//格式化時間的邏輯放在UserOrder類中
            }
        });

    }

總結(jié):一個類只包含一個職責(zé),其他的邏輯放在對應(yīng)的類上處理。

開閉原則

開閉原則就是在每次有新需求都不能在原來的代碼中做修改。你可以一開始的時候就用多態(tài)和接口來實現(xiàn)架構(gòu),讓你的代碼更容易擴展,而不是修改。

比如你要計算一個三角形的面積

//Triangle.java
public class Triangle{
  private double width;
  private double height;
  private double getArea(){
    return width*height/2;
  }
  //setter&getter
}

//AreaManager
public class AreaManager{
  private double calculateArea(ArrayList<Triangle> triangles){
    double area=0;
    for(Triangle triangle:triagnles)
        area+=triangle.getArea();
    return area;
  }
  
}

一開始代碼是這樣子設(shè)計的,但是后續(xù)要繼續(xù)加上圓形,矩形的面積,就要在AreaManager類里面加上計算圓形,矩形的面積方法,不符合我們的開閉原則。所以要寫一個shape接口,讓三角形矩形都實現(xiàn)這個接口,在calculateArea方法里面?zhèn)魅階rrayList<Shape>。不管以后要計算什么邊形的面積只要實現(xiàn)shape接口即可。

public interface Shape{
  double getArea();
}

public class Circle implements Shape{
  public static final double pi=3.14;
  double radius;
  public double getArea(){
    return radius*radius*pi;
  }
  //setter&getter
}

public class AreaManager{
  public double calculateArea(ArrayList<Shape> shapes){
    double area=0;
    for(Shape shape:shapes){
      area+=shape.getArea();
      
    }
    return area;
  }
  
}

總結(jié):在寫計算方法時,考慮到以后有多個方案要怎么設(shè)計。通常是多個方案都實現(xiàn)了一個接口,接口方法就是該方案的邏輯。然后在方法被調(diào)用時傳入接口類作為參數(shù),調(diào)用接口方法。

里氏替換原則

里氏替換原則就是用接口類或者父類來替代子類,而不改變程序的正確性。

舉個例子,下面adapter的構(gòu)造函數(shù)就用了List類型的參數(shù)來替換ArrayList類型。

//MainActivity.java
List<String> mList=new ArrayList<>();
mList.add("str");
UserOrderAdapter adapter=new UserOrderAdapter(mList);

//UserOrderAdapter.java
public class UserOrderAapter extends RecyclerView.Adapter<UserorderHolder>{
  List<String> list;
  public UserOrderAdapter(List<String> list){
    this.list=list;
  }
  
}

下面例子用了一個Arraylist的變量作為返回值。

public List<String> getList(String[] str){
  ArrayList<String> list=new ArrayList<>();
  for(String s:str)
    list.add(s);
  return list;
}

總結(jié),這個原則相當(dāng)簡單你應(yīng)該相當(dāng)熟悉或者你每天都在用了。

接口隔離原則

接口隔離原則有點像單一職責(zé)原則,不過目標(biāo)是接口類應(yīng)該只有一種職責(zé)。

比如自定義一個View需要加上一個點擊事件,

public interface onClickListener{
  void onClick(View v);
}

然后,你又有需求要加上一個長按事件和一個觸摸事件,

public interface onClickListener{
  void onClick(View v);
  void onLongClick(View v);
  void onTouch(View v,MotionEvent ev);
}

在設(shè)置監(jiān)聽這個接口時:你必須重寫這三個方法,可能你用不著后面兩個方法。

new CustomeView.setOnClick(new OnClickListener{
  public void onClick(View v){
    
  }
  public void onLongClick(View v){
    
  }
  public void onTouch(View v,MotionEvent ev){
    
  }
});

這個違背了接口隔離原則,一個實現(xiàn)接口的類應(yīng)該依賴他最小的接口。

所以我們的onClickListener只要寫一個onClick方法就行了。其他的方法另外創(chuàng)建接口來寫。比如OnTouchListener。

總結(jié):我們寫一個接口類里面的方法應(yīng)該是同一個職責(zé)的,不同職責(zé)的接口方法創(chuàng)建新的接口類來實現(xiàn)。

依賴倒置原則

依賴倒置就是依賴抽象,而不依賴具體的實例。

就是在項目中我們的架構(gòu)分為三層,安卓ui→業(yè)務(wù)邏輯→數(shù)據(jù)層,業(yè)務(wù)邏輯具體是判斷寫入或者獲取數(shù)據(jù)是否符合條件

比如我們現(xiàn)在要做網(wǎng)絡(luò)可用時將一個字符串寫入數(shù)據(jù)庫的操作,這樣子activity就持有netmanager和dbmanager的具體實例子,不符合我們的依賴倒置原則。如下:

//MainActivity.java
protected void  onCreate(bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  NetWorkManager netManager=//...
    DbManager dbManager
  if(netManager.isNetWorkConnected){
     dbManager.insert();
  }
}

//NetWorkManager.java
public boolean isNetWorkConnected(){
  
}
//DbManager.java
public void insert(){}

所以我們要做出如下修改:


//MainActivity.java
IDbManager IdbManager;


protected void  onCreate(bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
     IdbManager=new DBManager(this);//實例化BDmanager
     IdbManager.insert();
  
}

//IDbManager.java
public interface IDbManager{
  public void insert();
}
//DbManager.java
public class DbManager{
  INetManager netManager;
  public void setNetManager(INetManager netManager){
    this.netManager=netManager;
  }
  public void insert(){
    if(netManager.isNetworkConnected()){
     //do
    }
  }
  
}

//INetManager.java
public interface INetManager{
  boolean isNetworkConnected();
}
//NetManager.java
public class NetManager{
  public boolean isNetworkConnected(){
    //do
  }
}


這樣子就可以讓activity直接依賴IDbManager的抽象類。

總結(jié):通常我們?yōu)榱烁唵螌嵗橄箢?,我們推薦使用dagger2。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容