图解Java设计模式之设计模式七大原则
编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序(软件)。具有更好 设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据) 对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责 :职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2 以交通工具案例讲解 package com.example.testdemo.mode.principle;public class SingleResponsibility1 { public static void main(String[] args) { Vehicle vehicle = new Vehicle(); vehicle.run("摩托车"); vehicle.run("汽车"); vehicle.run("飞机"); }}// 交通工具类/** * 方式1 : * 1 。 在方式1的run方法中,违反了单一职责原则 * 2 。 解决的方案非常第二季简单,根据交通工具运行方法不同,分解成不同类即可 */class Vehicle { public void run(String vehicle) { System.out.println(vehicle + " 在公路上运行。。。。。"); }}package com.example.testdemo.mode.principle;public class SingleResponsibility2 { public static void main(String[] args) { ReadVehicle readVehicle = new ReadVehicle(); readVehicle.run("摩托车"); readVehicle.run("汽车"); AirVehicle airVehicle = new AirVehicle(); airVehicle.run("飞机"); WaterVehicle waterVehicle = new WaterVehicle(); waterVehicle.run("轮船"); }}/** * 方案2分析 : * 1 :遵守单一职责原则 * 2 :但是这样改动大,即将类分解,同时修改客户端 * 3 :改进 :直接修改Vehicle类,改动的代码会比较少 =》方案3 * */class ReadVehicle { 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 + "水中运行"); }}package com.example.testdemo.mode.principle;public class SingleResponsibility3 { public static void main(String[] args) { Vehicle2 vehicle2 = new Vehicle2(); vehicle2.run("汽车"); vehicle2.runAir("飞机"); vehicle2.runWater("轮船"); }}/** * 方案3的分析 : * 1 :这种修改方法没有对原来的类做大的修改,只是增加方法 * 2 :这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责 */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 + " 在水中运行。。。。。"); }} 单一职责原则注意事项和细节 (1)客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上 1)类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D。 package com.example.testdemo.mode.principle.segregation;import io.swagger.models.auth.In;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 { @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"); } @Override public void operation4() { System.out.println(" B 实现了 operation4"); } @Override public void operation5() { System.out.println(" B 实现了 operation5"); }}class D implements Interface1 { @Override public void operation1() { System.out.println(" D 实现了 operation1"); } @Override public void operation2() { System.out.println(" D 实现了 operation2"); } @Override public void operation3() { System.out.println(" D 实现了 operation3"); } @Override public void operation4() { System.out.println(" D 实现了 operation4"); } @Override public void operation5() { System.out.println(" D 实现了 operation5"); }}/** * A 类通过接口Interface1 依赖(使用)B类,但是只会用到1,2,3方法 */class A { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend2(Interface1 interface1) { interface1.operation2(); } public void depend3(Interface1 interface1) { interface1.operation3(); }}/** * C 类通过接口Interface1 依赖(使用)D类,但是只会用到1,4,5方法 */class C { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend4(Interface1 interface1) { interface1.operation4(); } public void depend5(Interface1 interface1) { interface1.operation5(); }}
package com.example.testdemo.mode.principle.segregation1;public class Segregation2 { public static void main(String[] args) { // 使用一把 A a = new A(); // A 类通过接口去依赖B类 a.depend1(new B()); a.depend2(new B()); a.depend3(new B()); // C 类通过接口去依赖(使用)D类 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"); }}/** * A 类通过接口Interface1 ,Interface2 依赖(使用)B类,但是只会用到1,2,3方法 */class A { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend2(Interface2 interface1) { interface1.operation2(); } public void depend3(Interface2 interface1) { interface1.operation3(); }}/** * C 类通过接口Interface1 ,Interface3 依赖(使用)D类,但是只会用到1,4,5方法 */class C { public void depend1(Interface1 interface1) { interface1.operation1(); } public void depend4(Interface3 interface1) { interface1.operation4(); } public void depend5(Interface3 interface1) { interface1.operation5(); }} 依赖倒转原则(Dependence Inversion Principle)是指 : 1)方案1 + 分析说明 package com.example.testdemo.mode.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"; }}/** * 完成Person接收消息的功能 * 方式1分析 * 1。简单,比较容易想到 * 2。如果我们获取的对象是微信,短信等等,则新增类,同时Persons也要增加相应的接收方法 * 3。解决思路 :引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖 * 因为Email,微信等等属于接收的范围,他们各自实现IReceiver接口就ok,这样我们就符合依赖倒转原则 */class Person { public void receive(Email email) { System.out.println(email.getInfo()); }} 2)方案2(依赖倒转)+ 分析说明 package com.example.testdemo.mode.principle.inversion.inprove;public class DependecyInversion { public static void main(String[] args) { // 客户端无需改变 Person person = new Person(); person.receive(new Email()); person.receive(new WeiXin()); }}/** * 定义接口 */interface IReceiver { String getInfo();}class Email implements IReceiver{ @Override public String getInfo() { return "电子邮件信息 :hello,world"; }}/** * 增加微信 */class WeiXin implements IReceiver { @Override public String getInfo() { return "微信消息 :hello ok"; }}/** * 方式2 */class Person { /** * 这里是我们对接口的依赖 * @param iReceiver */ public void receive(IReceiver iReceiver) { System.out.println(iReceiver.getInfo()); }} 依赖关系传递的三种方式 : package com.example.testdemo.mode.principle.inversion.inprove;public class Dependecy { public static void main(String[] args) { IOpenAndClose iOpenAndClose = new OpenAndClose(); iOpenAndClose.open(new ChangHong()); IOpenAndClose2 iOpenAndClose2 = new OpenAndClose2(new XiaoMi()); iOpenAndClose2.open(); IOpenAndClose3 iOpenAndClose3 = new OpenAndClose3(); iOpenAndClose3.setTv(new SanXing()); iOpenAndClose3.open(); }}/** * 方式1 :通过接口传递实现依赖 */interface IOpenAndClose { /** * 抽象方法,接收接口 * @param tv */ void open(ITV tv);}/** * ITV接口 */interface ITV { void play();}class ChangHong implements ITV { @Override public void play() { System.out.println("长虹电视机打开"); }}/** * 实现接口 */class OpenAndClose implements IOpenAndClose { @Override public void open(ITV tv) { tv.play(); }}/** * 方式2 :通过构造方法依赖传递 */interface IOpenAndClose2 { /** * 抽象方法 */ void open();}/** * ITV接口 */interface ITV2 { void play();}class XiaoMi implements ITV2 { @Override public void play() { System.out.println("小米电视机打开"); }}class OpenAndClose2 implements IOpenAndClose2 { /** * 成员属性 */ public ITV2 tv; /** * 构造方法 * @param itv2 */ public OpenAndClose2(ITV2 itv2) { this.tv = itv2; } @Override public void open() { this.tv.play(); }}/** * 方式3,通过setter方法传递 */interface IOpenAndClose3 { /** * 抽象方法 */ void open(); void setTv(ITV3 tv);}/** * ITV接口 */interface ITV3 { void play();}class SanXing implements ITV3 { @Override public void play() { System.out.println("三星电视打开"); }}class OpenAndClose3 implements IOpenAndClose3 { private ITV3 itv3; @Override public void open() { this.itv3.play(); } @Override public void setTv(ITV3 tv) { this.itv3 = tv; }} 依赖倒转原则的注意事项和细节 1)继承包含这样一层含义 :父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对象这些已经实现的方法任意修改,就会对整个继承体系造成破坏。 1)里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的一位姓里的女士提出的。 该看个程序,思考下问题和解决思路 package com.example.testdemo.mode.principle.liskov;public class Liskov { public static void main(String[] args) { A a = new A(); System.out.println("11 - 3 = " + a.funcl(11, 3)); System.out.println("1 - 8 = " + a.funcl(1, 8)); System.out.println("-----------------"); B b = new B(); // 这里本意是求出11 - 3 System.out.println("11 - 3 = " + b.funcl(11, 3)); // 1 - 8 System.out.println("1 - 8 = " + b.funcl(1, 8)); System.out.println("11 + 3 + 9 = " + b.func2(11, 3)); }}class A { /** * 返回两个数的差 * * @param num1 * @param num2 * @return */ public int funcl(int num1, int num2) { return num1 - num2; }}/** * B类继承类A * * 增加类一个新功能 :完成两个数相加,然后和9 求和 */class B extends A { /** * 这里,重写类A类的方法,可能是无意识 * @param a * @param b * @return */ @Override public int funcl(int a, int b) { return a + b; } public int func2(int a, int b) { return funcl(a, b) + 9; }} 1)我们发现原来运行正常的相减功能发生类错误。原因就是类B无意中重写父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。 package com.example.testdemo.mode.principle.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(); // 因为B类不再继承A类,因此调用者,不会再funcl是求减法 // 调用完成的功能就会很明确 // 这里本意是求出 11 + 3 System.out.println("11 + 3 = " + b.func1(11, 3)); // 1 + 8 System.out.println("1 + 8 = " + b.func1(1, 8)); System.out.println("11 + 3 + 9 = " + b.func2(11, 3)); // 使用组合仍然可以使用到A类相关方法 // 这里本意是求出 11 - 3 System.out.println("11 - 3 = " + b.func3(11, 3)); }}/** * 创建一个更加基础的基类 */class Base { // 把更加基础的方法和成员写Base类}/** * A 类 */class A extends Base { /** * 返回两个数的差 * @param num1 * @param num2 * @return */ public int func1(int num1, int num2) { return num1 - num2; }}/** * B类 继承了 A * * 增加类一个新功能 :完成两个数相加,然后和9 求和 */class B extends Base { /** * 如果B需要使用A类的方法,使用组合关系 */ private A a = new A(); /** * 这里,重写了A类方法,可能是无意识 * * @param a * @param b * @return */ public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; } /** * 我们仍然想使用A的方法 * @param a * @param b * @return */ public int func3(int a, int b) { return this.a.func1(a,b); }} 1)开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则 看一个画图形的功能。 package com.example.demo.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 { /** * 接收Shape对象,然后根据type,来绘制不同的图形 * @param shape */ public void drawShape(Shape shape) { if (shape.m_type == 1) { drawRectangle(shape); } else if (shape.m_type == 2) { drawCircle(shape); } else if (shape.m_type == 3) { drawTriangle(shape); } } /** * 绘制三角形 * @param shape */ private void drawTriangle(Shape shape) { System.out.println("绘制三角形"); } /** * 绘制圆形 * @param shape */ private void drawCircle(Shape shape) { System.out.println("绘制圆形"); } /** * 绘制矩形 * @param shape */ private void drawRectangle(Shape shape) { System.out.println("绘制矩形"); } } /** * Shape类,基类 */ 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)优点是比较好理解,简单易操作。 思路 : 把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修改 -》 package com.example.demo.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 { /** * 接收Shape对象,调用draw方法 * @param shape */ public void drawShape(Shape shape) { shape.draw(); } } /** * Shape类,基类 */ 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("绘制其他图形"); } } 1)一个对象应该对其他对象保持最少的了解。 1)有一个学校,下属有各个学院和总部,现要求打印出学校总部员工ID和学院员工的id package com.example.demo.demeter; import java.util.ArrayList; import java.util.List; /** * 客户端 */ public class Demeter1 { public static void main(String[] args) { // 创建一个 SchoolManager 对象 SchoolManager schoolManager = new SchoolManager(); // 输出学院的员工id 和 学院总部的员工信息 schoolManager.printAllEmployee(new CollegeManager()); } } /** * 学校总部员工类 */ class Employee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 学院的员工类 */ class CollegeEmployee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 管理学院员工的管理类 */ class CollegeManager { /** * 返回学院的所有员工 * @return */ public List<CollegeEmployee> getAllEmployee() { List<CollegeEmployee> employees = new ArrayList<>(); // 这里我们增加了10个员工到list for (int i = 0; i < 10; i++) { CollegeEmployee collegeEmployee = new CollegeEmployee(); collegeEmployee.setId("学院员工 id = " + i); employees.add(collegeEmployee); } return employees; } } /** * 学校管理类 * * 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager * CollegeEmployee 不是 直接朋友,而是一个陌生类,这样违背了迪米特法则 * */ class SchoolManager { /** * 返回学校总部的员工 * @return */ public List<Employee> getAllEmployee() { List<Employee> list = new ArrayList<>(); // 这里我们增加了5个员工到list for (int i = 0; i < 5; i++) { Employee employee = new Employee(); employee.setId("学校总部员工 id = " + i); list.add(employee); } return list; } /** * 该方法完成输出学校总部和学院员工信息 (id) * @param collegeManager */ void printAllEmployee(CollegeManager collegeManager) { // 分析问题 // 1. 这里的 CollegeEmployee 不是 SchoolManageer的直接朋友 // 2. CollegeEmployee 是以局部变量方式出现在 SchoolManager // 3. 违反了 迪米特法则 // 获取到学院员工 List<CollegeEmployee> allEmployee = collegeManager.getAllEmployee(); System.out.println("-------------学院员工-------------"); for (CollegeEmployee collegeEmployee : allEmployee) { System.out.println(collegeEmployee.getId()); } // 获取到学院总部员工 List<Employee> employee = this.getAllEmployee(); System.out.println("-----------学校总部员工-------------"); for (Employee employee1 : employee) { System.out.println(employee1.getId()); } } } 1)前面设计的问题在于SchoolManager中,CollegeEmployee类并不是SchoolManager类的直接朋友(分析) package com.example.demo.demeter.improve; import java.util.ArrayList; import java.util.List; /** * 客户端 */ public class Demeter1 { public static void main(String[] args) { // 创建一个 SchoolManager 对象 SchoolManager schoolManager = new SchoolManager(); // 输出学院的员工id 和 学院总部的员工信息 schoolManager.printAllEmployee(new CollegeManager()); } } /** * 学校总部员工类 */ class Employee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 学院的员工类 */ class CollegeEmployee { private String id; public String getId() { return id; } public void setId(String id) { this.id = id; } } /** * 管理学院员工的管理类 */ class CollegeManager { /** * 返回学院的所有员工 * @return */ public List<CollegeEmployee> getAllEmployee() { List<CollegeEmployee> employees = new ArrayList<>(); // 这里我们增加了10个员工到list for (int i = 0; i < 10; i++) { CollegeEmployee collegeEmployee = new CollegeEmployee(); collegeEmployee.setId("学院员工 id = " + i); employees.add(collegeEmployee); } return employees; } /** * 输出学院员工的信息 */ public void printEmployee() { // 获取到学院员工 List<CollegeEmployee> allEmployee = getAllEmployee(); System.out.println("----------学院员工-----------"); for (CollegeEmployee collegeEmployee : allEmployee) { System.out.println(collegeEmployee.getId()); } } } /** * 学校管理类 * * 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager * CollegeEmployee 不是 直接朋友,而是一个陌生类,这样违背了迪米特法则 * */ class SchoolManager { /** * 返回学校总部的员工 * @return */ public List<Employee> getAllEmployee() { List<Employee> list = new ArrayList<>(); // 这里我们增加了5个员工到list for (int i = 0; i < 5; i++) { Employee employee = new Employee(); employee.setId("学校总部员工 id = " + i); list.add(employee); } return list; } /** * 该方法完成输出学校总部和学院员工信息 (id) * @param collegeManager */ void printAllEmployee(CollegeManager collegeManager) { // 分析问题 // 1. 将输出学院的员工方法,封装到CollegeManager collegeManager.printEmployee(); // 获取到学院总部员工 List<Employee> employee = this.getAllEmployee(); System.out.println("-----------学校总部员工-------------"); for (Employee employee1 : employee) { System.out.println(employee1.getId()); } } } 1)迪米特法则的核心是降低类之间的耦合 基本介绍 :原则是尽量使用合成/聚合的方式,而不是使用继承。 1)找出应用中可能需要变换之处,把它们独立出来,不要和那些需要变化的代码混在一起。 |
|