===== 总结 ===== * 找出变化之处,封装起来,不要把它们和不需要变化的代码混在一起(例如:策略模式) * 针对接口编程,而不是针对实现编程(例如:策略模式) * 多用组合,少用继承(例如:策略模式) * 设计具有松耦合关系的对象(例如:观察者模式) * 对扩展开放,对修改封闭(例如:装饰者模式) * 要依赖抽象,不要依赖具体类(依赖倒置原则。例如:工厂模式) * 变量的声明类型尽量是抽象类或接口 * 只和你的密友聊天,且聊的尽可能少(最少知识原则,也叫戴尔特法则Law of Demeter) * 别调用我们,我们会调用你。即底层组件将自己挂钩到系统中,由高层组件决定何时和如何调用底层组件。 * 一个类应该只有一个引起变化的原因。该原则体现了面向对象中的内聚概念。(单一责任原则) ---- ===== 单一职责原则 ===== 主要针对类的设计 接口要做到单一原则,类的设计尽量做到只有一个原因引起变化 ---- ===== 里氏替换原则 ===== 任何基类可以出现的地方,子类一定可以出现 如果子类不能完整的实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”。则建议断开父子继承关系,采用依赖,聚合,组合等关系代替。(如下图ToyGun) {{:pasted:20151227-190743.png}} 如果某个子类有新特性时,如何保证不违背“里氏替换原则”呢? 【子类方法的参数】一定要是【父类方法的参数】的父类 public class test { public static void main(String[] args) { Soldier f = new Soldier(); SuperSoldier s = new SuperSoldier(); HashMap map = new HashMap(); f.shoot(map); // 给士兵一把机枪,士兵可以射击。(合理) s.shoot(map); // 给特种兵一把机枪,特种兵可以射击。(合理) // 反例 f.shoot1(map); // 父类和子类调用的结果不一样,违反里氏原则。(不合理) s.shoot1(map); // 父类和子类调用的结果不一样,违反里氏原则。(不合理) } } class Soldier{ public void shoot(HashMap map){ // 每个士兵拿着“机枪”可以射击 System.out.println("soldier shoot ...."); } public void shoot1(Map map){ System.out.println("soldier shoot ...."); } } class SuperSoldier extends Soldier{ public void shoot(Map map){ // 特种兵只要有“枪”就可以射击 System.out.println("SuperSoldier shoot ...."); } public void shoot1(HashMap map){ System.out.println("SuperSoldier shoot ...."); } } ---- ===== 依赖倒置 ===== * 2个具体实现类不应该相互依赖,而是应该通过接口或抽象类将他们关联起来。 依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。 在实际编程中,我们一般需要做到如下3点: * 低层模块(被调用的功能模块)尽量都要有抽象类或接口,或者两者都有。 * 变量的声明类型尽量是抽象类或接口。 * 使用继承时遵循里氏替换原则。 ---- ===== 接口隔离原则 ===== 主要针对类之间的依赖耦合关系。 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。如果一个类依赖一个臃肿的接口(接口中有该类根本不需要的方法,但是该类必须实现此方法)是不合理的 * 一个接口只服务于一个子模块或业务逻辑 ---- ===== 迪米特法则 最少知识法则 ===== * 只和朋友类交流,除了朋友类,其他类一改不能出现在当前类中 * 朋友类:出现在方法的参数返回值的类叫朋友类 * 交流越少越好,朋友类中最好封装所有复杂的操作,我调用的越少越好 ---- ===== 开闭原则 ===== 扩展开放,对修改关闭 如果要增加新的功能,有如下2种方法 - 要采取新增一个功能类或功能模块,并适当修改上层调用处的逻辑 - 非常好的做法 - 修改现有类或模块 - 导致的结果是对之前已经稳定的程序重新进行测试,破坏程序的稳定性