設(shè)計模式SOLID五大原則
SOLID每一個字母都代表這一種編程原則,其中
- S代表著單一職責(zé)原則
- O代表著開閉原則
- L代表著里氏替換原則
- I代表著接口隔離原則
- 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。