JAVA设计模式原则
- 设计模式的目的
- 代码的可读性
- 代码的可重用性
- 代码的可扩展性
- 代码的可靠性
- 使代码呈现高内聚,低耦合的特性
- 设计模式的七大原则
- 接口隔离原则
- 职责单一原则
- 里氏替换原则
- 迪米特法则
- 合成复用原则
- 依赖倒装原则
- 开闭原则
- 职责单一原则
- 基本介绍
对于类来说一个类应该只负责一项职责
- 注意事项和细节
- 降低类的复杂度,一个类只负责一项职责
- 提高了的可读性,可维护性
- 降低变更引起的风险
- 通常情况下我们应该遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,才可以在方法级别保持单一职责原则。
- 基本介绍
- 接口隔离原则
-
基本介绍
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
-
案例一没有遵守接口隔离原则
package designPattern.segregation; /** * 接口隔离原则未改造之前 * @author Administrator * */ public class Segregation1 { public static void main(String[] args) { A a = new A(); a.depend1(new B());//A类通过接口去依赖B类 a.depend2(new B()); a.depend3(new B()); C c = new C(); c.depend1(new D());//C类通过接口去依赖D类 c.depend4(new D()); c.depend5(new D()); } } 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"); } } 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(); } }
-
案例二遵守接口隔离原则
package designPattern.segregation.improve; /** * 接口隔离原则改造之后 * @author Administrator * */ public class Segregation1 { public static void main(String[] args) { A a = new A(); a.depend1(new B());//A类通过接口去依赖B类 a.depend2(new B()); a.depend3(new B()); C c = new C(); c.depend1(new D());//C类通过接口去依赖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(); } }
-
- 依赖倒转原则
-
基本介绍
1.高模块不应该依赖低模块,二者应该依赖其抽象
2.抽象不应该依赖细节,细节应该依赖抽象
3.依赖倒转原则的核心思想是面向接口编程
4.使用接口和抽象类的目的就是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
-
案例一没有遵守依赖倒装原则
package designPattern.inversion; /** * 依赖倒置的传统方法 * @author Administrator * */ public class DependcyInversion { public static void main(String[] args) { Person person=new Person(); person.receive(new Email()); } } class Email{ public String getInfo() { return "电子邮件:hello world"; } } /** * 这种方式 * 1.比较简单的容易实现比较容易想到 * 2.如果改变了相应的接受对象就必须新增加一个新的对象person类也必须增加新的方法 * @author Administrator * */ class Person{ public void receive(Email email) { System.out.println(email.getInfo()); } }
-
案例二遵守了依赖倒转原则
package designPattern.inversion.improve; /** * 依赖倒置的改进之后的方法 * @author Administrator * */ public class DependcyInversion { 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{ public String getInfo() { return "电子邮件:hello world"; } } class WeiXin implements IReceiver{ public String getInfo() { return "微信消息:hello world"; } } class Person{ public void receive(IReceiver receiver) { System.out.println(receiver.getInfo()); } }
-
依赖倒转原则细节和注意事项
- 低模块尽量都有抽象类和接口,或者两者都有,程序稳定性更好。
- 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
- 继承遵守里氏替换原则
-
- 里氏替换原则
-
基本介绍
1.里氏替换原则由麻省理工的一位姓里的女士提出
2.所有引用基类的地方必须能透明地使用其子类对象
3.在使用继承时,遵守里氏替换原则,在子类中尽量不要重写父类的方法
4.里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题
-
案例一没有遵守里氏替换原则
package designPattern.liskov; /** * 没有遵守里氏替换原则 * 通常的解决办法就是原来的父类和子类都继承一个更通俗的基类, * 原有的继承关系去掉采用依赖,聚合,组合等关系代替 * @author Administrator * */ public class Liskov { public static void main(String[] args) { A a=new A(); System.out.println("11-8="+a.func1(11, 8)); System.out.println("1-8="+a.func1(1, 8)); 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)); } } //A类 class A { //func1方法返回两个数的差 public int func1(int num1,int num2) { return num1-num2; } } //B类继承了A类 class B extends A{ //重写了父类的func1的方法 public int func1(int a,int b) { return a+b; } public int func2(int a,int b) { return func1(a, b)+9; } }
-
案例二遵守里氏替换原则
package designPattern.liskov.improve; /** * 遵守里氏替换原则 * @author Administrator * */ public class Liskov { public static void main(String[] args) { A a=new A(); System.out.println("11-8="+a.func1(11, 8)); System.out.println("1-8="+a.func1(1, 8)); B b=new B(); //因为B类不在继承A类,因此调用者不会再认为func1是求减法了 //调用完成的功能就很明确了 System.out.println("11-3="+b.func3(11, 3)); System.out.println("1-8="+b.func3(1, 8)); System.out.println("11+3+9="+b.func2(11, 3)); } } //最基本的基类 class Base{ } //A类 class A extends Base{ //func1方法返回两个数的差 public int func1(int num1,int num2) { return num1-num2; } } //B类继承了A类 class B extends Base{ //如果B类需要是用A类的方法,则需要使用组合 private A a=new A(); //重写了父类的func1的方法 public int func1(int a,int b) { return a+b; } public int func2(int a,int b) { return func1(a, b)+9; } //我们这样使用A的方法就降低了对象之间的耦合性 public int func3(int a,int b) { return this.a.func1(a, b); } }
-
- 开闭原则
-
基本介绍
- 1.开闭原则是编程中最基础。最重要的设计原则
- 2.一个软件实体类,模块和函数应该对外开放(提供方),对修改关闭(使用方)。用抽象构建框架,用实现扩展细节
- 3.当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
- 4.编程中遵循其他原则,以及设计模式的目的就是遵循开闭原则。
-
案例一不适用开闭原则
package designPattern.ocp; /** * 方式一的优缺点 *1.优点是比较好理解,简单易操作 *2.缺点就违反了设计模式的ocp(开闭原则),即对扩展开放,对修改关闭。即当我们 *给类增加新功能的时候,尽量不修改代码,或尽可能少的修改代码。 *3.比如我们这时候要新增加一个图形种类,我们需要修改的地方太多。 * @author Administrator * */ public class Ocp { public static void main(String[] args) { GraphicEditor graphicEditor=new GraphicEditor(); graphicEditor.drawRectangle(new Rectangle()); graphicEditor.drawCircle(new Circle()); graphicEditor.drawTriangle(new Triangle()); } } //这是一个用于绘图的类 class GraphicEditor{ //接受shape对象,然后根据type,来绘制不同的图形 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; } }
-
案例二使用开闭原则进行改进
package designPattern.ocp.improve; /** * 利用了开闭原则之后的改进 * @author Administrator * */ 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) { //接受shape对象调用drawShape方法 s.drawShape(); } } abstract class Shape { //绘制图形的方法 public abstract void drawShape();//c抽象方法 } class Rectangle extends Shape{ @Override public void drawShape() { System.out.println("绘制矩形"); } } class Circle extends Shape{ @Override public void drawShape() { System.out.println("绘制圆形"); } } //新增三角形 class Triangle extends Shape{ @Override public void drawShape() { System.out.println("绘制三角形"); } }
-
- 迪米特法则
-
基本介绍
- 一个对象应该对其他对象保持最少的了解
- 类与类的关系越密切,耦合度越大
- 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息。
- 迪米特法则还有一个简单的定义:只与直接的朋友联系
- 直接朋友:每个对象都会跟其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。
-
案例一没有遵守迪米特法则
package designPattern.demeter; import java.util.ArrayList; import java.util.List; /** * 没有采用迪米特法则 * @author Administrator * */ //客户端 public class Demeter { public static void main(String[] args) { //创建一个SchoolManager对象 SchoolManager schoolManager=new SchoolManager(); //输出学校的员工id和学院总部的员工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{ 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; } } //分析SchoolManager的直接类有Employee,CollegeManager //CollegeEmployee不是SchoolManager的直接朋友而是一个陌生类,这样的话违背了迪米特法则 //管理学校员工的管理了 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> list = sub.getAllEmployee(); System.out.println("--------分公司员工----------------"); for (CollegeEmployee collegeEmployee : list) { System.out.println(collegeEmployee.getId()); } System.out.println("--------学院总部员工----------------"); List<Employee> list2 = getAllEmployee(); for (Employee employee : list2) { System.out.println(employee.getId()); } } }
-
案例二遵守了迪米特法则
package designPattern.demeter.improve; import java.util.ArrayList; import java.util.List; /** * 使用迪米特法则之后的改进 * @author Administrator * */ //客户端 public class Demeter { public static void main(String[] args) { SchoolManager schoolManager=new SchoolManager(); 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{ 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> list = getAllEmployee(); System.out.println("--------学院员工----------------"); for (CollegeEmployee collegeEmployee : list) { System.out.println(collegeEmployee.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.printCollegeEmployee(); System.out.println("--------学院总部员工----------------"); List<Employee> list2 = getAllEmployee(); for (Employee employee : list2) { System.out.println(employee.getId()); } } }
-
迪米特法则注意事项和细节
- 迪米特法则的核心是降低类之间的耦合
- 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类之间(对象间)的耦合关系,并不是要求完全没有依赖关系。
-
- 合成复用原则
- 基本介绍
- 原则是尽量使用合成/聚合的方式,而不是使用继承
- UML类图
- 基本介绍
- 设计原则的核心思想
- 找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起
- 针对接口编程而不是针对实现编程
- 为了交互对象之间的松耦合设计而努力