设计模式原则

DogJay 2018-10-12 后端技术 107人已围观

# DesignPattern-principle 关于设计模式原则,有的按照solid原则总结,有的说六原则,大家都总结的都不一致。学习了这位[仁兄](http://my.csdn.net/zhengzhb)的文章基础上,今天这里把我理解的设计模式几大原则给大家分享一下: >* 单一职责原则 >* 开闭原则 >* 接口分离原则 >* 里氏代换原则 >* 依赖倒置原则 >* 迪米特原则 >* 优先使用组合,而不是使用继承 ## 单一职责原则 -------------------- > 定义:不要存在多于一个导致类变更的原因。通俗的说,即单一职责是说一个类或者一个方法,只做一件事,或者完成一个任务。 问题由来:类T负责两个不同的职责:职责Z1,职责Z2。当由于职责Z1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责Z2功能发生故障。 解决:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责Z1功能,T2完成职责Z2功能。这样,当修改类T1时,不会使职责Z2发生故障风险;同理,当修改T2时,也不会使职责Z1发生故障风险。 举例说明: 用一个类描述动物行走的 ```java public class Animal { public void run(String str) { System.out.println("行走 " + str); } } public class Client { public static void main(String[] args) { Animal animal = new Animal(); animal.run("牛"); animal.run("羊"); } } ``` 运行了一下后,发现不对哈。鱼等水生的动物是不能行走的,那么怎么办呢?所以我们要修改一下,遵循单一职责原则,让陆生动物使用 Terrestrial 这个类;而水生动物使用 Aquatic 这个类。 ```java class Terrestrial{ public void run(String animal){ System.out.println(animal+"陆地行走"); } } class Aquatic{ public void run(String animal){ System.out.println(animal+"水中游行"); } } public class Client { public static void main(String[] args) { Terrestrial terrestrial = new Terrestrial(); terrestrial.breathe("牛"); terrestrial.breathe("羊"); terrestrial.breathe("猪"); Aquatic aquatic = new Aquatic(); aquatic.breathe("鱼"); } } ``` 遵循单一职责原的优点有: >* 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多; >* 提高类的可读性,提高系统的可维护性; >* 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。 >* 需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。 ## 开闭原则 -------------------- > 定义:是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 开闭原则的核心是:对扩展开放,对修改关闭。 **“可变性的封装原则”意味着两点:** - 一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里面。继承应当被看做是封装变化的方法,而不应当被认为是从一般的对象生成特殊的对象方法。 - 一种可变性不应当与另一种可变性混合在一起。所有的类图的继承结构一般不会超过两层,不然就意味着将两种不同的可变性混合在一起。 此处我借鉴了[花郎V](http://www.cnblogs.com/loulijun/archive/2012/03/14/2394055.html)这个仁兄的文章,很感谢哈。 ```java public interface IBoy { // 年龄 public int getAge(); // 姓名 public String getName(); // 长相 public String getFace(); } public class StrongerBoy implements IBoy { private String name; private int age; private String face; public StrongerBoy(String name, int age, String face, String figure) { this.name = name; this.age = age; this.face = face; } @Override public int getAge() { return age; } @Override public String getFace() { return face; } @Override public String getName() { return name; } } public class Mans { private final static ArrayList boys = new ArrayList(); // 静态初始化块 static { boy.add(new StrongerBoy("谢霆锋", 30, "帅气")); boy.add(new StrongerBoy("冯小刚", 60, "成熟")); } public static void main(String args[]) { System.out.println("----------美女在这里----------"); for (IBoy boy : boys) { System.out.println("姓名:" + boy.getName() + " 年龄:" + boy.getAge() + " 长相:" + boy.getFace()); } } } } ``` 这个程序写的不错哈,我运行了一下,感觉不错。此时问题来了,如果要加个外国名人怎么办?修改 Iboy 这个接口吗,这样做就符合不了开闭原则了。所以,我这里想到了扩展,但是如何扩展呢? 可以定义一个 IForeigner 接口继承自 IBoy,在 IForeigner 接口中添加国籍属性 getCountry(),然后实现这个接口即可,然后就只需要在场景类中做稍微修改就可以了。 ```java public interface IForeigner extends IBoy { // 国籍 public String getCountry(); } public class ForeignerBoy implements IForeigner { private String name; private int age; private String country; private String face; private String figure; public ForeignerBoy(String name, int age, String country, String face, String figure) { this.name = name; this.age = age; this.country = country; this.face =face; this.figure = figure; } @Override public String getCountry() { // TODO Auto-generated method stub return country; } @Override public int getAge() { // TODO Auto-generated method stub return age; } @Override public String getFace() { // TODO Auto-generated method stub return face; } @Override public String getName() { // TODO Auto-generated method stub return name; } } boys.add(new ForeignerBoy("richale",28,"美国","阳光")); ``` 设计原则是死的,也要根据实际的需求,我们要灵活使用这个开闭原则。 ## 接口分离原则 -------------------- > 定义:客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。 通俗的说就是:接口中的方法要尽量的少,不要使接口过于臃肿,不要有过多不相关的逻辑方法。参考于[李天炜](http://tianweili.github.io/blog/2015/02/10/interface-segregation-principle/)的文章 ```java public interface I { public void method1(); public void method2(); public void method3(); } public class B implements I{ @Override public void method1() { System.out.println("类B实现了接口I的方法1"); } @Override public void method2() { System.out.println("类B实现了接口I的方法2"); } @Override public void method3() {//类B并不需要接口I的方法3功能,但是由于实现接口I,所以不得不实现方法3 //在这里写一个空方法 } } public class D implements I{ @Override public void method2() { System.out.println("类D实现了接口I的方法2"); } @Override public void method3() { System.out.println("类D实现了接口I的方法3"); } @Override public void method1() {//类D并不需要接口I的方法1功能,但是由于实现接口I,所以不得不实现方法1 //在这里写一个空方法 } } //类A通过接口I依赖类B public class A { public void depend1(I i){ i.method1(); } } //类C通过接口I依赖类D public class C { public void depend1(I i){ i.method3(); } } public class Client { public static void main(String[] args) { A a = new A(); I i1 = new B(); a.depend1(i1); C c = new C(); I i2 = new D(); c.depend1(i2); } } ``` 运行结果: ``` java 类B实现了接口I的方法1 类D实现了接口I的方法3 ``` 从以上代码可以看出,如果接口过于臃肿,不同业务逻辑的抽象方法都放在一个接口内,则会造成它的实现类必须实现自己并不需要的方法,这种设计方式显然是不妥当的。所以我们要修改上述设计方法,把接口I拆分成3个接口,使得实现类只需要实现自己需要的接口即可。只贴出修改后的接口和实现类的代码,修改代码如下: ```java public interface I1 { public void method1(); } public interface I2 { public void method2(); } public interface I3 { public void method3(); } public class B implements I1,I2{ @Override public void method1() { System.out.println("类B实现了接口I的方法1"); } @Override public void method2() { System.out.println("类B实现了接口I的方法2"); } } public class D implements I2,I3{ @Override public void method2() { System.out.println("类D实现了接口I的方法2"); } @Override public void method3() { System.out.println("类D实现了接口I的方法3"); } } ``` ### 与单一职责原则的区别 到了这里,有些人可能觉得接口隔离原则与单一职责原则很相似,其实不然。 1. 单一职责原则注重的是职责;而接口隔离原则注重对接口依赖的隔离。 2. 单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口,主要针对抽象,针对程序整体框架的构建。 3. 接口尽量小 4. 接口高内聚 5. 接口设计是有限度的 **注意事项:** > 原则是软件大师们经验的总结,在软件设计中具有一定的指导作用,但不能按部就班哈。对于接口隔离原则来说,接口尽量小,但是也要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是如果过小,则会造成接口数量过多,使设计复杂化,所以一定要适度。 ## 里氏代换原则 -------------------- 里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA大会上发表的一篇文章《[Data Abstraction and Hierarchy](http://www.rendezvouswithdestiny.net/index_files/LiskovSub.pdf)》里面提出来的,主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中的蕴涵的原理。2002年,软件工程大师Robert C. Martin,出版了一本《[Agile Software Development Principles Patterns and Practices](http://book.douban.com/subject/1459003/)》,在文中他把里氏代换原则最终简化为一句话:"Subtypes must be substitutable for their base types",也就是说,子类必须能够替换成它们的基类。 >定义1:如果对每一个类型为 T1的对象 Object1,都有类型为 T2 的对象Object2,使得以 T1定义的所有程序 P 在所有的对象 Object1 都代换成 Object2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。 >定义2:所有引用父类的地方必须能正常地使用其子类的对象。 问题由来:有一功能P1,由类Superclass完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由