设计模式 – 七大基本原则 设计原则核心思想
找出应用中可能需要变化之处 ,把它们独立出来,不要和那些不需要变化的代码混在一起。
针对接口编程 ,而不是针对实现编程。
为了交互对象之间的松耦合 设计而努力
一、单一职责原则(SRP)
对类来说的,即一个类应该只负责一项职责 。
如类 A 负责两个不同职责:职责 1,职责 2。
当职责 1 需求变更而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2
可以分别在类 的级别上与方法 的级别上遵循单依职责原则
但是最好是在类的级别上, 在方法级别上一般都是一个类的方法比较少 或者逻辑真的很简单 的时候
核心思想是少用 if else 语句, 否则会增加代码耦合性
降低类的复杂度,一个类只负责一项职责。
提高类的可读性,可维护性
降低变更引起的风险
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;
只有类中方法数量足够少,可以在方法级别保持单一职责原则
违反单一职责原则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.atguigu.principle.singleresponsibility;public class SingleResponsibility1 { public static void main (String[] args) { Vehicle vehicle = new Vehicle (); vehicle.run("摩托车" ); vehicle.run("汽车" ); vehicle.run("飞机" ); } } class Vehicle { public void run (String vehicle) { System.out.println(vehicle + " 在公路上运行...." ); } }
类级别实现单一职责原则 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 package com.atguigu.principle.singleresponsibility;public class SingleResponsibility2 { public static void main (String[] args) { RoadVehicle roadVehicle = new RoadVehicle (); roadVehicle.run("摩托车" ); roadVehicle.run("汽车" ); AirVehicle airVehicle = new AirVehicle (); airVehicle.run("飞机" ); } } class RoadVehicle { public void run (String vehicle) { System.out.println(vehicle + "公路运行" ); } } class AirVehicle { public void run (String vehicle) { System.out.println(vehicle + "天空运行" ); } } class WaterVehicle { public void run (String vehicle) { System.out.println(vehicle + "水中运行" ); } }
方法级别实现单一职责原则 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 package com.atguigu.principle.singleresponsibility;public class SingleResponsibility3 { public static void main (String[] args) { Vehicle2 vehicle2 = new Vehicle2 (); vehicle2.run("汽车" ); vehicle2.runWater("轮船" ); vehicle2.runAir("飞机" ); } } class Vehicle2 { public void run (String vehicle) { System.out.println(vehicle + " 在公路上运行...." ); } public void runAir (String vehicle) { System.out.println(vehicle + " 在天空上运行...." ); } public void runWater (String vehicle) { System.out.println(vehicle + " 在水中行...." ); } }
二、接口隔离原则 (ISP)
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口 上
一个接口可能有许多需要实现的方法, 但是一个类如果通过一个接口来依赖另一个类的时候可能就回需要实现那个它不需要的方法
这时候我们就要将一个大接口拆分成几个小接口, 让它们物尽其用, 让一个类只依赖那个它们需要的最小接口即可
违反实现接口隔离原则 类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,
如果接口 Interface1 对于类 A 和类 C来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法
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 package com.atguigu.principle.segregation;public class Segregation1 { public static void main (String[] args) { } } interface Interface1 { void operation1 () ; void operation2 () ; void operation3 () ; void operation4 () ; void operation5 () ; } class B implements Interface1 { public void operation1 () { System.out.println("B 实现了 operation1" ); } public void operation2 () { System.out.println("B 实现了 operation2" ); } public void operation3 () { System.out.println("B 实现了 operation3" ); } public void operation4 () { System.out.println("B 实现了 operation4" ); } public void operation5 () { System.out.println("B 实现了 operation5" ); } } class D implements Interface1 { public void operation1 () { System.out.println("D 实现了 operation1" ); } public void operation2 () { System.out.println("D 实现了 operation2" ); } public void operation3 () { System.out.println("D 实现了 operation3" ); } public void operation4 () { System.out.println("D 实现了 operation4" ); } public void operation5 () { System.out.println("D 实现了 operation5" ); } } class A { public void depend1 (Interface1 i) { i.operation1(); } public void depend2 (Interface1 i) { i.operation2(); } public void depend3 (Interface1 i) { i.operation3(); } } class C { public void depend1 (Interface1 i) { i.operation1(); } public void depend4 (Interface1 i) { i.operation4(); } public void depend5 (Interface1 i) { i.operation5(); } }
遵循接口隔离原则 将接口 Interface1 拆分 为独立的几个接口(这里我们拆分成 3 个接口 ),
类 A 和类 C 分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
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 96 97 98 package com.atguigu.principle.segregation.improve;public class Segregation1 { 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 { public void operation1 () { System.out.println("B 实现了 operation1" ); } public void operation2 () { System.out.println("B 实现了 operation2" ); } public void operation3 () { System.out.println("B 实现了 operation3" ); } } class D implements Interface1 , Interface3 { public void operation1 () { System.out.println("D 实现了 operation1" ); } public void operation4 () { System.out.println("D 实现了 operation4" ); } 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(); } }
三、依赖倒转原则(DIP)
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
依赖倒转(倒置)的中心思想是面向接口编程
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,
把展现细节的任务交给他们的实现类去完成
每个子类都应该有它的上层接口或抽象, 抽象指的就是接口或者抽象类 , 细节就是实现类
为了增强代码的稳定性, 尽量每个子类(底层模块)都有抽象类或接口, 两者都有更稳定
抽象的作用相当于在实现类和变量引用中加了一层缓冲,
这样我们在对细节进行修改或扩展的时候就可以避免在变量引用中进行修改导致代码冗杂
违反依赖倒转原则 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 package com.atguigu.principle.inversion;public class DependecyInversion { public static void main (String[] args) { Person person = new Person (); person.receive(new Email ()); } } class Email { public String getInfo () { return "电子邮件信息: hello,world" ; } } class Person { public void receive (Email email ) { System.out.println(email.getInfo()); } }
使用接口传递实现依赖倒转 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 package com.atguigu.principle.inversion.improve;public class DependecyInversion { public static void main (String[] args) { Person person = new Person (); person.receive(new Email ()); person.receive(new WeiXin ()); } } interface IReceiver { public String getInfo () ; } class Email implements IReceiver { public String getInfo () { return "电子邮件信息: hello,world" ; } } class WeiXin implements IReceiver { public String getInfo () { return "微信信息: hello,ok" ; } } class Person { public void receive (IReceiver receiver ) { System.out.println(receiver.getInfo()); } }
使用构造器或 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 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 96 97 98 package com.atguigu.principle.inversion.improve;public class DependencyPass { public static void main (String[] args) { ChangHong changHong = new ChangHong (); OpenAndClose openAndClose = new OpenAndClose (); openAndClose.setTv(changHong); openAndClose.open(); } } interface IOpenAndClose { public void open () ; public void setTv (ITV tv) ; } interface ITV { public void play () ; } class OpenAndClose implements IOpenAndClose { private ITV tv; public void setTv (ITV tv) { this .tv = tv; } public void open () { this .tv.play(); } } class ChangHong implements ITV { @Override public void play () { System.out.println("长虹电视机,打开" ); } }
四、里氏代换原则(LSP)
继承所带来的问题:
继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有 的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
继承在给程序设计带来便利的同时,也带来了弊端 。比如使用继承会给程序带来侵入性 ,程序的可移植性降低, 增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且 父类修改后,所有涉及到子类的功能都有可能产生故障
如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,
使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
换句话说,所有引用基类的地方必须能透明地使用其子类的对象 。
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来 解决问题
就是说继承的时候子类可能会无意间重写父类方法, 结果导致使用的时候出错
解决方法是将继承关系拆解 , 子类不再是子类, 父类不再是父类
将他们需要的方法提出来到一个第三类, 原子类和原父类均继承这个第三类,
然后通过聚合, 组合, 依赖 等方式重新构建这两个类的关系, 根本思路是减少这两个类的耦合性
违反里氏代换原则 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 package com.atguigu.principle.liskov;public class Liskov { public static void main (String[] args) { A a = new A (); System.out.println("11-3=" + a.func1(11 , 3 )); System.out.println("1-8=" + a.func1(1 , 8 )); System.out.println("-----------------------" ); B b = new B (); System.out.println("11-3=" + b.func1(11 , 3 )); System.out.println("1-8=" + b.func1(1 , 8 )); System.out.println("11+3+9=" + b.func2(11 , 3 )); } } class A { public int func1 (int num1, int num2) { return num1 - num2; } } class B extends A { public int func1 (int a, int b) { return a + b; } public int func2 (int a, int b) { return func1(a, b) + 9 ; } }
遵循里氏代换原则 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 package com.atguigu.principle.liskov.improve;public class Liskov { public static void main (String[] args) { A a = new A (); System.out.println("11-3=" + a.func1(11 , 3 )); System.out.println("1-8=" + a.func1(1 , 8 )); System.out.println("-----------------------" ); B b = new B (); System.out.println("11+3=" + b.func1(11 , 3 )); System.out.println("1+8=" + b.func1(1 , 8 )); System.out.println("11+3+9=" + b.func2(11 , 3 )); System.out.println("11-3=" + b.func3(11 , 3 )); } } class Base { } class A extends Base { public int func1 (int num1, int num2) { return num1 - num2; } } class B extends Base { private A a = new A (); public int func1 (int a, int b) { return a + b; } public int func2 (int a, int b) { return func1(a, b) + 9 ; } public int func3 (int a, int b) { return this .a.func1(a, b); } }
五、开闭原则(OCP) 开闭原则是编程中最基础、最重要 的设计原则
所有其他原则以及设计模式都是为了实现开闭原则
一个软件实体如类,模块和函数应该**对扩展开放(对提供方方法)**,对修改关闭(对使用方方法)。
用抽象构建框架,用实现扩展细节
就是在后期修改时, 尽量不要去修改原来的代码, 只是在原来的代码上进行添加及扩展
如果改了代码谁知道会对哪个功能产生影响, 这样就可以减少耦合性
违反开闭原则 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 package com.atguigu.principle.ocp;public class Ocp { public static void main (String[] args) { GraphicEditor graphicEditor = new GraphicEditor (); graphicEditor.drawShape(new Rectangle ()); graphicEditor.drawShape(new Circle ()); graphicEditor.drawShape(new Triangle ()); } } class GraphicEditor { public void drawShape (Shape s) { if (s.m_type == 1 ) drawRectangle(s); else if (s.m_type == 2 ) drawCircle(s); else if (s.m_type == 3 ) drawTriangle(s); } public void drawRectangle (Shape r) { System.out.println(" 绘制矩形 " ); } public void drawCircle (Shape r) { System.out.println(" 绘制圆形 " ); } public void drawTriangle (Shape r) { System.out.println(" 绘制三角形 " ); } } class Shape { int m_type; } class Rectangle extends Shape { Rectangle() { super .m_type = 1 ; } } class Circle extends Shape { Circle() { super .m_type = 2 ; } } class Triangle extends Shape { Triangle() { super .m_type = 3 ; } }
遵循开闭原则 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 package com.atguigu.principle.ocp.improve;public class Ocp { public static void main (String[] args) { GraphicEditor graphicEditor = new GraphicEditor (); graphicEditor.drawShape(new Rectangle ()); graphicEditor.drawShape(new Circle ()); graphicEditor.drawShape(new Triangle ()); graphicEditor.drawShape(new OtherGraphic ()); } } class GraphicEditor { public void drawShape (Shape s) { s.draw(); } } abstract class Shape { int m_type; public abstract void draw () ; } class Rectangle extends Shape { Rectangle() { super .m_type = 1 ; } @Override public void draw () { System.out.println(" 绘制矩形 " ); } } class Circle extends Shape { Circle() { super .m_type = 2 ; } @Override public void draw () { System.out.println(" 绘制圆形 " ); } } class Triangle extends Shape { Triangle() { super .m_type = 3 ; } @Override public void draw () { System.out.println(" 绘制三角形 " ); } } class OtherGraphic extends Shape { OtherGraphic() { super .m_type = 4 ; } @Override public void draw () { System.out.println(" 绘制其它图形 " ); } }
六、迪米特法则(最少知道原则)
一个对象应该对其他对象保持最少的 了解
类与类关系越密切,耦合度越大 迪米特法则又叫最少知道原则 ,即一个类对自己依赖的类知道的越少越好。
对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息 迪米特法则更简单的定义:只与直接朋友 通信
直接朋友 :每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间 是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返 回值 中的类 为直接朋友,而出现在局部变量 中的类不是直接朋友。
陌生的类最好不要以局部变量的形式出现在类的内部。
两个类总会有耦合, 我们只是尽可能降低它们之前的耦合
迪米特法则的本质是避免类中出现非直接朋友关系的耦合
违反迪米特法则 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 96 97 98 99 100 101 102 package com.atguigu.principle.demeter;import java.util.ArrayList;import java.util.List;public class Demeter1 { public static void main (String[] args) { SchoolManager schoolManager = new SchoolManager (); schoolManager.printAllEmployee(new CollegeManager ()); } } class Employee { private String id; public void setId (String id) { this .id = id; } public String getId () { return id; } } class CollegeEmployee { private String id; public void setId (String id) { this .id = id; } public String getId () { return 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; } } class SchoolManager { public List<Employee> getAllEmployee () { List<Employee> list = new ArrayList <Employee>(); for (int i = 0 ; i < 5 ; i++) { Employee emp = new Employee (); emp.setId("学校总部员工id= " + i); list.add(emp); } return list; } void printAllEmployee (CollegeManager sub) { List<CollegeEmployee> list1 = sub.getAllEmployee(); System.out.println("------------学院员工------------" ); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } List<Employee> list2 = this .getAllEmployee(); System.out.println("------------学校总部员工------------" ); for (Employee e : list2) { System.out.println(e.getId()); } } }
遵循迪米特法则 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 96 97 98 99 100 101 102 103 104 105 package com.atguigu.principle.demeter.improve;import java.util.ArrayList;import java.util.List;public class Demeter1 { public static void main (String[] args) { System.out.println("~~~使用迪米特法则的改进~~~" ); SchoolManager schoolManager = new SchoolManager (); schoolManager.printAllEmployee(new CollegeManager ()); } } class Employee { private String id; public void setId (String id) { this .id = id; } public String getId () { return id; } } class CollegeEmployee { private String id; public void setId (String id) { this .id = id; } public String getId () { return 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 printEmployee () { List<CollegeEmployee> list1 = getAllEmployee(); System.out.println("------------学院员工------------" ); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } } } class SchoolManager { public List<Employee> getAllEmployee () { List<Employee> list = new ArrayList <Employee>(); for (int i = 0 ; i < 5 ; i++) { Employee emp = new Employee (); emp.setId("学校总部员工id= " + i); list.add(emp); } return list; } void printAllEmployee (CollegeManager sub) { sub.printEmployee(); List<Employee> list2 = this .getAllEmployee(); System.out.println("------------学校总部员工------------" ); for (Employee e : list2) { System.out.println(e.getId()); } } }
七、合成 / 聚合复用原则(CARP)
尽量使用合成/聚合的方式,而不是使用继承