设计模式七大原则
设计模式体现了代码的耦合性, 内聚性以及可维护性,可扩展性,重用性,灵活性。
1、代码重用性(即:相同功能的代码,不用多次编写)
2、可读性(即:编程规范性,便于其他程序员的阅读和理解)
3、可扩展性(即:当需要增加新的功能时,非常的方便,称为可维护)
4、可靠性(即:当我们增加新的功能后,对原来的功能没有影响)
5、使程序呈现高内聚,低耦合
的特性
Scott Mayers在其巨著《Effective C++》说过: C++老手和C++新手的区别就是前者手背上有很多伤疤。
一、单一职责原则(Single responsibility)
单一职责原则注意事项和细节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SingleResponsility { public static void main (String[] args) { Vehicle vehicle = new Vehicle(); vehicle.run("布加迪威龙" ); vehicle.fly("波音747" ); } } class Vehicle { public void run (String string) { System.out.println(string + ":是陆地交通工具" ); } public void fly (String string) { System.out.println(string + ":是空中交通工具" ); } }
二、接口隔离原则(Interface Segregation)
1、类A通过接口 Interface1、2 依赖类B,类C通过接口 Interface1、3 依赖类D,如果接口 Interface 对于 类A 和 类C 来说不是最小接口,那么 类B 和 类D 必须去实现他们不需要的方法。
2、将接口 Interface 拆分为独立的几个接口,类A 和 类C 分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
3、接口 Interface 中出现的方法,根据实际情祝拆分为三个接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 public class InterfaceSegregation { public static void main (String[] args) { A a = new A(); a.depend1(new B()); a.depend2(new B()); a.depend3(new B()); C c = new C(); c.depend1(new D()); c.depend4(new D()); c.depend5(new D()); } } interface interface1 { void Operation1 () ; } interface interface2 { void Operation2 () ; void Operation3 () ; } interface interface3 { void Operation4 () ; void Operation5 () ; } class B implements interface1 , interface2 { @Override public void Operation1 () { System.out.println("B 实现了 Operation1" ); } @Override public void Operation2 () { System.out.println("B 实现了 Operation2" ); } @Override public void Operation3 () { System.out.println("B 实现了 Operation3" ); } } class D implements interface1 , interface3 { @Override public void Operation1 () { System.out.println("D 实现了 Operation1" ); } @Override public void Operation4 () { System.out.println("D 实现了 Operation4" ); } @Override public void Operation5 () { System.out.println("D 实现了 Operation5" ); } } class A { public void depend1 (interface1 i) { i.Operation1(); } public void depend2 (interface2 i) { i.Operation2(); } public void depend3 (interface2 i) { i.Operation3(); } } class C { public void depend1 (interface1 i) { i.Operation1(); } public void depend4 (interface3 i) { i.Operation4(); } public void depend5 (interface3 i) { i.Operation5(); } }
三、依赖倒转原则(Dependence Inversion)
1、高层模块不应该依赖低层模块,二者都应该依赖其抽象(缓冲层);
2、抽象不应该依赖细节,细节应该依赖抽象;
3、依赖倒转(倒置)的中心思想是面向接口编程;
4、依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中, 抽象指的是接口或抽象类,细节就是具体的实现类;
5、使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
依赖关系三种传递方式:
接口传递(依赖)
构造方法传递(依赖)
setter方式传递(聚合)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class DependenceInversion { public static void main (String[] args) { Person person = new Person(); person.receive(new Email()); person.receive(new WeChat()); } } interface Info { String getInfo () ; } class Email implements Info { @Override public String getInfo () { return "Receive Email" ; } } class WeChat implements Info { @Override public String getInfo () { return "Receive WeChat" ; } } class Person { public void receive (Info info) { System.out.println(info.getInfo()); } }
四、里氏替换原则(Liskov Substitution)
1、里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院一位姓里的女士提出;
2、如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象;
3、在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
;
4、继承实际上让两个类耦合性增强了,给程序带来侵入性。在适当的情况下,可以通过聚合,组合,依赖来解决问题;
5、继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class LiskovSubstitution { public static void main (String[] args) { A a = new A(); System.out.println("2-1=" + a.func1(2 , 1 )); B b = new B(); System.out.println("2+1=" + b.func1(2 , 1 )); System.out.println("2+1+9=" + b.func2(2 , 1 )); System.out.println("B类使用A类方法:2-1=" + b.func3(2 , 1 )); } } class Base { public int func1 (int num1, int num2) { return num1 - num2; } } class A extends Base {} class B extends Base { @Override public int func1 (int num1, int num2) { return num1 + num2; } public int func2 (int num1, int num2) { return func1(num1, num2) + 9 ; } private A a = new A(); public int func3 (int num1, int num2) { return this .a.func1(num1, num2); } }
五、开闭原则 OCP(Open Closed)
1、开闭原则(Open Closed Principle) 是编程中最基础、最重要的设计原则;
2、一个软件实体,比如类,模块和函数应该对提供方扩展开放,对使用方修改关闭。用抽象构建框架,用实现扩展细节;
3、当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化;
4、编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class OpenClosed { public static void main (String[] args) { Use use = new Use(); use.drawShape(new Triangle()); use.drawShape(new Circle()); use.drawShape(new OtherGraphics()); } } class Use { public void drawShape (Shape shape) { shape.draw(); } } abstract class Shape { public abstract void draw () ; } class Triangle extends Shape { @Override public void draw () { System.out.println("子类实现具体功能:三角形" ); } } class Circle extends Shape { @Override public void draw () { System.out.println("子类实现具体功能:圆形" ); } } class OtherGraphics extends Shape { @Override public void draw () { System.out.println("子类实现具体功能:任何形状" ); } }
六、迪米特法则(Demeter)
1、一个对象应该对其他对象保持最少的了解(最少知道原则 LKP)
。
2、类与类关系越密切,耦合度越大。要求降低类之间耦合,而不是完全解耦。
3、迪米特法则(Demeter Principle),即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息。
4、迪米特法则更简单的定义:只与直接的朋友通信 。
5、直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
1 2 3 4 5 6 7 8 class A { B b; public B m1 () {} public void m2 (B b) {} public void m3 () { B b1 = new B(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 public class Demeter { public static void main (String[] args) { SchoolManager schoolManager = new SchoolManager(); schoolManager.printAllEmployee(new CollegeManager()); } } class CollegeEmployee { private String id; public String getId () { return id; } public void setId (String id) { this .id = id; } } class CollegeManager { public List<CollegeEmployee> getAllEmployee () { List<CollegeEmployee> list = new ArrayList<CollegeEmployee>(); for (int i = 0 ; i < 10 ; i++) { CollegeEmployee emp = new CollegeEmployee(); emp.setId("学院员工id " + i); list.add(emp); } return list; } public void printCollegeEmployee () { List<CollegeEmployee> list1 = this .getAllEmployee(); System.out.println("---学院员工----" ); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } } } class SchoolEmployee { private String id; public String getId () { return id; } public void setId (String id) { this .id = id; } } class SchoolManager { public List<SchoolEmployee> getAllEmployee () { List<SchoolEmployee> list = new ArrayList<SchoolEmployee>(); for (int i = 0 ; i < 5 ; i++) { SchoolEmployee emp = new SchoolEmployee(); emp.setId("学校总部员工id= " + i); list.add(emp); } return list; } void printAllEmployee (CollegeManager sub) { sub.printCollegeEmployee(); List<SchoolEmployee> list2 = this .getAllEmployee(); System.out.println("------学校总部员工------" ); for (SchoolEmployee e : list2) { System.out.println(e.getId()); } } }
七、合成复用原则(Composite Reuse)
合成复用原则 尽量使用组合/聚合的方式,而不是使用继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class CompositeReuse { public static void main (String[] args) { System.out.println("------依赖------" ); B b = new B(); b.Operation1(new A()); System.out.println("------聚合------" ); b.setA(new A()); b.Operation2(); System.out.println("------组合------" ); b.Operation3(); } } class A { void Operation1 () { System.out.println("A Operation1" ); } void Operation2 () { System.out.println("A Operation2" ); } void Operation3 () { System.out.println("A Operation3" ); } } class B { void Operation1 (A a) { a.Operation1(); a.Operation2(); a.Operation3(); } A a; public void setA (A a) { this .a = a; } void Operation2 () { a.Operation1(); a.Operation2(); a.Operation3(); } A a1 = new A(); void Operation3 () { a1.Operation1(); a1.Operation2(); a1.Operation3(); } }
UML(Unified Modeling Language)
IDEA PlantUML表示类与类之间的关系的符号
1 2 3 4 5 6 7 8 9 10 @startuml Class1 <|-- ClassA:泛化 Class2 <-- ClassB:关联 Class3 *-- ClassC:组合 Class4 o-- ClassD:聚合 Class5 <|.. ClassE:实现 Class6 <.. ClassF:依赖 @enduml
依赖(Dependence)
只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了。
类中用到了对方;
类的成员属性;
方法的返回类型;
方法接收的参数类型;
方法中使用到 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /** * @author Yu * 类中用到了对方; * 类的成员属性; * 方法的返回类型; * 方法接收的参数类型; * 方法中使用到; */ public class Dependence { A a;//TODO 类的成员属性 public A save(B b) {//TODO 方法接收的参数类型 //TODO 方法的返回类型 System.out.println(""); A a = new A();//TODO 方法中使用到 return a; } } class A {} class B {}
继承(泛化 Generalization)
泛化关系实际上就是继承关系,依赖关系的特例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Generalization extends Base { @Override public void get(Object oId) { } @Override public void put(Object oName) { } } abstract class Base { abstract public void get(Object oId); abstract public void put(Object oName); }
实现(Realization)
实现关系实际上就是 A类 实现 B接口,依赖关系的特例。
1 2 3 4 5 6 7 8 9 10 public class Implementation implements Base { @Override public void init() { System.out.println("init"); } } interface Base { void init(); }
关联(Association)
类与类之间的关系,依赖关系的特例。
关联具有导航性:即双向关系或单向关系。
1 2 3 4 5 6 7 public class Person { private IDCard idCard; } class IDCard { //private Person person; }
聚合(Aggregation)
表示的是整体和部分的关系,整体与部分可以分开,关联关系的特例。
聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Computer { private Mouse mouse; private Keyboard keyboard; public void setMouse(Mouse mouse) { this.mouse = mouse; } public void setKeyboard(Keyboard keyboard) { this.keyboard = keyboard; } } class Mouse {} class Keyboard {}
组合(Composite)
整体与部分的关系,但是整体与部分不可以分开,关联关系的特例。
级联删除就是组合关系。
1 2 3 4 5 6 7 8 public class Computer { private CPU cpu = new CPU(); private SSD ssd = new SSD(); } class CPU {} class SSD {}