【Android】六大设计原则
六大设计原则
为了提高代码的可维护性,可扩展性,减少开发人员开发和维护的成本,减少系统出错;我们需要做到高内聚,低耦合;那么这里有六大设计原则,快来学习一下把;
单一职责原则(SRP)
介绍:
一个类应该只有一个引起它变化的原因。每一个类应该只负责一个职责。
违反常见问题:
- 当一个类承担过多职责时,耦合度比较高,当一个实则需要修改时,可能会导致其他职责被迫修改;
- 由于职责较多代码量较大,开发人员维护困难;
- 难以进行单个功能的测试;
例子:
public class SRP {private int elpee;private int money;public SRP(int elpee, int money) {this.elpee = elpee;this.money = money;}public int getMoney() {return money;}public int getreport() {return 12;}
}
修改后:
public class SRP {private int elpee;private int money;public SRP(int elpee, int money) {this.elpee = elpee;this.money = money;}public int getMoney() {return money;}
}
class getmoney{public int getMoney(SRP S) {return S.getMoney();}
}
class getreport{public int getrepoet(SRP S) {return 23;}
}
修改前后的区别在于为不同的功能创建了不同的类,这样的话如果要修改类,不需要需要SRP这个类,只需要修改对应的功能类就好,还方便测试等;
可能违反SRP的情况:
- 当一个类承担了很多职责,比如ui,数据库,业务逻辑处理;
- 当一个类被修改多次;
- 方法很多的时;
扩展点:
我们可以引入多层次的划分,例如:
- 业务逻辑层:具体的业务和数据处理;
- 数据访问层:与数据库文件等数据存储相关操作;
- 服务层:封装业务逻辑层,数据访问层的交互;
根据不同层次的划分,使用单一职责原则更加得心应手;
开放-关闭原则(OCP)
介绍:
软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。当有需求变化时,软件系统应该拓展新功能,而不是修改现有的代码;
优点:
- 有利于代码的稳定性和可维护性。当有新功能时,我们不修改现有的类或者方法,而是通过继承或者接口去添加新的模块来实现功能;
- 减少代码量的重复。只需要扩展特定的模块,不需要在系统其他地方去增加代码量,减少代码量;
- 减少修改风险。减少因为修改代码而破坏已有功能;
如何实现:
开放-关闭原则通常依赖于多态性、继承和接口编程,而非依赖具体实现;在不修改原有代码的基础上进行扩展;
这里选择了策略模式来实现OCP原则:
策略模式:定义一系列算法(策略),并将每种算法封装到独立的类中,使它们可以互相替换;客户端(使用者)可以在运行时选择不同的策略,而不用修改原有代码。
class OrderService {public double calculateDiscount(String type, double price) {if ("normal".equals(type)) {return price;} else if ("vip".equals(type)) {return price * 0.9;} else if ("svip".equals(type)) {return price * 0.8;}return price;}
}
现在需要新增功能,那么就是需要新增else if语句,如果在软件系统其他地方用到,那么就会新增代码量;那么使用ocp原则这么写:
public interface dis {public double discount(double count);
}public class vip implements dis{@Overridepublic double discount(double count) {return 0.9*count;}}class vvip implements dis{@Overridepublic double discount(double count) {return 0.8*count;}}
class discount{public double getdiscout(dis dis,double money){return dis.discount(money);}
}
定义一个接口,功能通过实现这个接口的方法来完成,通过一个类,参数是接口,通过多态来调用不同类中的方法来实现功能;
里氏替换原则(LSP)
介绍:
- 定义:任何基类可以出现的地方,子类一定可以出现。;
- 和开闭原则的关联:里氏替换原则其实是对开闭原则的补充,开闭原则的重要一步就是抽象化,基类和子类的继承就是抽象化的一种表现,那么LSP就是对实现抽象化的规范;
- 为什么要这么要求:这么要求的情况下,子类才是对基类做到了扩展,有利于代码的维护和减少了错误;
这就要求我们尽量不要重写和重载基类的方法,否则有可能造成基类的方法原有功能的故障;(有时候我们会使用final
来强制遵循);不仅如此,假如改变了基类,那么所有的子类有可能会发生故障;
如何实现:
public class LSP {public void say(){System.out.println("所有人都好可爱");}
}
class lsp extends LSP{@Overridepublic void say(){System.out.println("刘浩存真可爱");}
}
这里将say方法进行了重写,那么我们将无法使用基类的功能,所有人都好可爱;
解决办法有三种:
class lsp extends LSP{@Overridepublic void say(){super.say();System.out.println("刘浩存真可爱");}}
保留父类的方法和功能,子类进行扩展;
-
重新写构造一个方法功能,不重写,写一个
Liusay()
方法; -
抽象出一个新类,使得父类和子类都去继承这个类;把原来的继承关系去掉,采用依赖,组合等关系代替;
理解里氏替换原则:
核心:不改变父类的功能,而是对父类的功能进行扩展;
- 不改变父类的功能:可以实现抽象方法,但不能覆盖父类的非抽象方法;
- 可以增加新的方法功能;
- 当子类重载父类的方法时,后置条件(参数)要比父类更宽松,使得能使用父类这个功能的条件也能使用子类的方法;
- 当子类实现父类的抽象方法时,前置条件(返回值)要比父类更严格;可以代替而且还更加精确了;
依赖倒置原则(DIP)
介绍
- 定义:高层模块不应该依赖底层模块,而是依赖于抽象;抽象不应该依赖于细节,细节依赖于抽象;
优点
- 降低了耦合度;
- 增加了可维护性和可拓展性;
实例:
public class DIP {public void dog(){System.out.println("我是一只可爱小狗");}
}
class dip {public void animal(DIP D){D.dog();}
}
这里dip
类依赖于低层模块DIP
,违反了依赖倒置原则;
interface jiekou{void animal();
}
public class DIP implements jiekou{@Overridepublic void animal() {System.out.println("我是一只可爱小狗");}
}
class cat implements jiekou{@Overridepublic void animal() {System.out.println("我是一只可爱小(>^ω^<)喵");}
}
class dip {public void say(jiekou a){a.animal();}
}
这里即使新增加低层,也蛮符合单一职责原则的;
这里有一个接口,低层实现这个接口,那么高层则可以依赖接口而不是依赖细节;这里细节指的是具体的实现;
迪米特法则(lOD)
介绍
- 也称最少知识原则,一个对象尽可能少的了解其他对象的细节;一个对象不应该过多的依赖其他对象的内部结构和实现细节。对象之间应该尽可能通过有限的,直接的接口进行,不是一长串的调用链;
- 只与朋友通信,不与陌生人通信;
这里的朋友是指:
- 自身:自己的方法和属性;
- 成员对象:;
- 方法参数;
- 创建的对象;
- 全局变量或实例对象;
实例:
public class LOD {public lod getlhc(){return new lod();}
}
class lod{public void getljx(){}
}
class lods{public void get(LOD lod){lod.getlhc().getljx();}
}
这里lods
类直接依赖于lod
,不应该暴露lod
类,违反了迪米特法则;
public class LOD {private lod lod;public void getlhc(){lod.getljx();}
}
class lod{public void getljx(){}
}
class lods{public void get(LOD lod){lod.getlhc();}
}
这里使用了方法对lod
类的对象进行了封装,直接调用,而不是新new
了一个对象,把对象暴露了出来违法法则;
接口隔离原则(ISP):
介绍:
一个类对另一个类的依赖应该建立在最小的接口上;
接口尽量小而精;不要建立庞大的接口,接口中的方法最好少而精;
尽量为一个类定制一个接口,而不是用一个庞大接口使依赖它的所有类通用;
例子:
比如ISP类代表汽车,需要实现三个方法 void chesu()
;void qiyou()
; void gonglv()
;
但是isp类代表风扇,需要实现两个方法 shansu()
;void gonglv()
;
interface dianqi{void chesu();void qiyou();void gonglv();
}
public class ISP implements dianqi{@Overridepublic void chesu() {}@Overridepublic void qiyou() {}@Overridepublic void gonglv() {}
}
class isp implements dianqi{public void shansu() {}@Overridepublic void chesu() {//不需要}@Overridepublic void qiyou() {//不需要}@Overridepublic void gonglv() {}
}
这样的接口就很臃肿;而且实现了不需要的两个方法;
interface dianqi{void gonglv();
}
interface ISPI{void chesu();void qiyou();
}
interface shan{void shnsu();
}
这样设计的接口合理且精确;
我们有几个注意事项:
- 接口尽量小,但是要有限度;接口的粒度较小,可以增加灵活性;但接口的数量过多,也会使设计复杂化;
- 建立最小的依赖关系,给需要依赖的类定制方法;
优点:
降低耦合度,提高可维护性和灵活性;
本次介绍到此结束,谢谢大家!