本文共 20629 字,大约阅读时间需要 68 分钟。
懂了设计模式,你就懂了面向对象分析和设计 (OOA/D)的精要
单一职责原则
接口隔离原则
依赖倒转(倒置)原则
里氏替换原则
开闭原则
迪米特法则
合成复用原则
/** * @author 孙一鸣 on 2020/2/2 */public class 单一职责原则01 { 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+"在路上跑"); }}
方案二:
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 单一职责原则01 { public static void main(String[] args) { AirVehicle airVehicle = new AirVehicle(); airVehicle.run("飞机"); }}//天空工具类class AirVehicle{ public void run(String vehicle){ System.out.println(vehicle+"在天上飞"); }}//陆地工具类class RodaVehicle{ public void run(String vehicle){ System.out.println(vehicle+"在路上跑"); }}//海洋工具类class WaterVehicle{ public void run(String vehicle){ System.out.println(vehicle+"在水上游"); }}
方案三:
//交通工具类class Vehicle{ public void run(String vehicle){ System.out.println(vehicle+"跑"); } public void Roadrun(String vehicle){ System.out.println(vehicle+"在路上跑"); } public void Waterrun(String vehicle){ System.out.println(vehicle+"在水里游"); } public void Airrun(String vehicle){ System.out.println(vehicle+"在天上飞"); }}
1.这种修改方法没有对原来的类做大的修改,只是增加方法
2. 这里虽然没有在类这个级别上遵守单- -职责原则,但是在方法级别上,仍然是遵守单- -职责1)降低类的复杂度, 一个类只负责-项职责。
2)提高类的可读性,可维护性 3)降低变更引起的风险 4)通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单- -职责原则;只有类中方法数量足够少,可以在方法级别保持单- -职责原则类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。
按隔离原则应当这样处理:将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
不遵守接口隔离原则
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 接口隔离原则 { public static void main(String[] args) { A a = new A(); a.run(new D());//此时不需要实现类D的D E方法 C c=new C(); c.run(new B());//此时不需要实现类B的B C方法 }}interface Interafce{ void A(); void B(); void C(); void D(); void E();}class D implements Interafce{ @Override public void A() { System.out.println("D类实现Father接口方法A"); } @Override public void B() { System.out.println("D类实现Father接口方法B"); } @Override public void C() { System.out.println("D类实现Father接口方法c"); } @Override public void D() { System.out.println("D类实现Father接口方法D"); } @Override public void E() { System.out.println("D类实现Father接口方法E"); }}class B implements Interafce{ @Override public void A() { System.out.println("c类实现Father接口方法A"); } @Override public void B() { System.out.println("c类实现Father接口方法B"); } @Override public void C() { System.out.println("c类实现Father接口方法c"); } @Override public void D() { System.out.println("c类实现Father接口方法D"); } @Override public void E() { System.out.println("c类实现Father接口方法E"); }}/** 类A的参数传入了接口,实例化对象时,假如想使用C类的方法,将C类传入,此时C类已经实现接口* */class A{ void runA(Interafce d){ d.A(); } void runB(Interafce d){ d.B(); } void runC(Interafce d){ d.C(); }}class C{ void run(Interafce d){ d.A(); } void runD(Interafce d){ d.D(); } void runE(Interafce d){ d.E(); }}
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 接口隔离原则 { public static void main(String[] args) { A a = new A(); a.runA(new D()); a.runB(new D()); a.runC(new D()); System.out.println("***************"); C c=new C(); c.runA(new B()); c.runD(new B()); c.runE(new B()); }}interface Interafce{ void A();}interface Interafce2{ void B(); void C();}interface Interafce3{ void D(); void E();}class D implements Interafce,Interafce2{ @Override public void A() { System.out.println("D类实现Father接口方法A"); } @Override public void B() { System.out.println("D类实现Father接口方法B"); } @Override public void C() { System.out.println("D类实现Father接口方法c"); }}class B implements Interafce,Interafce3{ @Override public void A() { System.out.println("c类实现Father接口方法A"); } @Override public void D() { System.out.println("c类实现Father接口方法D"); } @Override public void E() { System.out.println("c类实现Father接口方法E"); }}/** 类A的参数传入了接口,实例化对象时,假如想使用C类的方法,将C类传入,此时C类已经实现接口* */class A{ void runA(Interafce d){ d.A(); } void runB(Interafce2 d){ d.B(); } void runC(Interafce2 d){ d.C(); }}class C{ void runA(Interafce d){ d.A(); } void runD(Interafce3 d){ d.D(); } void runE(Interafce3 d){ d.E(); }}
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 依赖倒转原则 { public static void main(String[] args) { Person person = new Person(); person.Recive(new Email()); }}class Person { void Recive(Email email) { System.out.println("接收到的"); email.getInfo(); }}class Email { void getInfo() { System.out.println("信息内容: 你好我是小明!"); }}
完成Person接收消息的功能
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 依赖倒转原则 { public static void main(String[] args) { Person person = new Person(); person.Recive(new Email()); person.Recive(new Weixin()); }}/** 定义接口**/interface IReceiver{ public void getInfo();}class Email implements IReceiver{ public void getInfo() { System.out.println("邮件信息内容: 你好我是小明!"); }}class Weixin implements IReceiver{ public void getInfo() { System.out.println("微信信息内容: 你好我是小红!"); }}class Person { void Recive(IReceiver iReceiver) { System.out.println("接收到的"); iReceiver.getInfo(); }}
接口传递
构造方法传递
setter方式传递
package DesignPattern.Test;import com.sym.JVM.T;/** * @author 孙一鸣 on 2020/2/2 */public class 依赖关系传递 { public static void main(String[] args) { MyOpenAndClose myOpenAndClose =new MyOpenAndClose(); myOpenAndClose.open(new XiaoMi()); }}/** 开机与关机的接口* */interface OpenAndClose{ /* * 开机:参数传入一个接口 电视 * 抽象方法,接收接口 * */ public void open(TV tv);}/* *电视接口 */interface TV{ public void play(); //表示电视机已打开}class XiaoMi implements TV{ @Override public void play() { System.out.println("小米电视已打开。。"); }}class MyOpenAndClose implements OpenAndClose{ @Override public void open(TV tv) { tv.play(); }}
package DesignPattern.Test;import com.sym.JVM.T;/** * @author 孙一鸣 on 2020/2/2 */public class 依赖关系传递 { public static void main(String[] args) { MyOpenAndClose myOpenAndClose = new MyOpenAndClose(new XiaoMi()); myOpenAndClose.open(); }}/* * 开机与关机的接口 * */interface OpenAndClose { /* * 开机:参数传入一个接口 电视 * 抽象方法,接收接口 * */ public void open();}/* *电视接口 */interface TV { public void play(); //表示电视机已打开}class XiaoMi implements TV { @Override public void play() { System.out.println("小米电视已打开。。"); }}class MyOpenAndClose implements OpenAndClose { public TV tv; public MyOpenAndClose(TV tv) { this.tv = tv; } @Override public void open() { this.tv.play(); }}
package DesignPattern.Test;import com.sym.JVM.T;/** * @author 孙一鸣 on 2020/2/2 */public class 依赖关系传递 { public static void main(String[] args) { MyOpenAndClose myOpenAndClose = new MyOpenAndClose(); myOpenAndClose.setTv(new XiaoMi()); myOpenAndClose.open(); }}/* * 开机与关机的接口 * */interface OpenAndClose { /* * 开机:参数传入一个接口 电视 * 抽象方法,接收接口 * */ public void open(); public void setTv(TV tv);}/* *电视接口 */interface TV { public void play(); //表示电视机已打开}class XiaoMi implements TV { @Override public void play() { System.out.println("小米电视已打开。。"); }}class MyOpenAndClose implements OpenAndClose { private TV tv; @Override public void open() { this.tv.play(); } @Override public void setTv(TV tv) { this.tv = tv; }}
如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序
P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可
以通过聚合,组合,依赖 来解决问题。package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 里氏替换原则 { public static void main(String[] args) { TestA a = new TestA(); System.out.println("11-3=" + a.function1(11, 3)); System.out.println("1-8=" + a.function1(1, 8)); System.out.println("-----------------------"); TestB b = new TestB(); System.out.println("11-3=" + b.function1(11, 3)); System.out.println("1-8=" + b.function1(1, 8)); System.out.println("11+3+9=" + b.function2(11, 3)); }}class TestA{ public int function1(int a,int b){ return a-b; }}class TestB extends TestA{ public int function1(int a,int b){ return a + b; } public int function2(int a,int b){ return function1(a,b)+9; }}
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 里氏替换原则 { public static void main(String[] args) { TestA a = new TestA(); System.out.println("11-3=" + a.function1(11, 3)); System.out.println("1-8=" + a.function1(1, 8)); System.out.println("-----------------------"); TestB b = new TestB(); System.out.println("11-3=" + b.function3(11, 3)); System.out.println("1-8=" + b.function3(1, 8)); System.out.println("11+3+9=" + b.function2(11, 3)); }}class Base{ //把更加基础的方法和成员写到Base类}class TestA extends Base{ public int function1(int a,int b){ return a-b; }}class TestB extends Base{ //函数function1重写了TestA的方法 public int function1(int a,int b){ return a + b; } public int function2(int a,int b){ return function1(a,b)+9; } //我们仍然想要使用TestA的方法,使用组合关系 private TestA a=new TestA(); public int function3(int a,int b){ return this.a.function1(a,b); }}
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 开闭原则 { public static void main(String[] args) { Editor editor = new Editor(); editor.drawShape(new Rectangle()); editor.drawShape(new Circle()); }}//基类class Shape{ int type;}//矩形class Rectangle extends Shape{ Rectangle(){ super.type=1;//设置矩形的识别码为1 }}//圆形class Circle extends Shape{ Circle(){ super.type=2;//设置圆形的识别码为2 }}//绘制图形【使用方】class Editor{ //接收Shape对象,然后根据type识别码,绘制不同图形 public void drawShape(Shape shape){ if (shape.type==1){ drawRectangle(); }else if (shape.type==2){ drawCircle(); }else if (shape.type==3){ } } private void drawRectangle() { System.out.println(" 绘制矩形 "); } private void drawCircle() { System.out.println(" 绘制圆形 "); }}
方式1的优缺点
package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 开闭原则 { public static void main(String[] args) { Editor editor = new Editor(); editor.drawShape(new sanjiao()); }}//基类class Shape{ int type;}//新添加三角形class sanjiao extends Shape{ sanjiao(){ super.type=3;//设置三角形的识别码为3 }}//绘制图形【使用方】class Editor{ //接收Shape对象,然后根据type识别码,绘制不同图形 public void drawShape(Shape shape){ if (shape.type==3){ drawSanjiao(); } } private void drawSanjiao(){ System.out.println(" 绘制三角形 "); }}
改进的思路分析
思路:把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可, 这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可, 使用方的代码就不需要修 -> 满足了开闭原则package DesignPattern.Test;/** * @author 孙一鸣 on 2020/2/2 */public class 开闭原则 { public static void main(String[] args) { Editor editor = new Editor(); editor.drawShape(new Rectangle()); editor.drawShape(new Circle()); editor.drawShape(new sanjiao()); }}//基类abstract class Shape{ int type; public abstract void draw();}//矩形class Rectangle extends Shape{ Rectangle(){ super.type=1;//设置矩形的识别码为1 } @Override public void draw() { System.out.println(" 绘制矩形 "); }}//圆形class Circle extends Shape{ Circle(){ super.type=2;//设置圆形的识别码为2 } @Override public void draw() { System.out.println(" 绘制圆形 "); }}//新添加三角形class sanjiao extends Shape{ sanjiao(){ super.type=3;//设置三角形的识别码为3} @Override public void draw() { System.out.println(" 绘制三角形 "); }}//绘制图形【使用方】class Editor{ //接收Shape对象,然后根据type识别码,绘制不同图形 public void drawShape(Shape shape){ shape.draw(); }}
此时我们再要添加一个新的绘制图形时,我们需要改变的只有:
//新添加其他class Other extends Shape{ @Override public void draw() { System.out.println(" 绘制其他图形 "); }} editor.drawShape(new Other());
使用方并没有做任何代码上的变动.满足了OCP原则
有一个学校,下属有各个学院和总部,现要求打印出学校总部员工ID和学院员工的id
package DesignPattern.Test;import java.util.ArrayList;import java.util.List;/** * @author 孙一鸣 on 2020/2/3 *///客户端public class 迪米特法则 { public static void main(String[] args) { //创建了一个 SchoolManager 对象 SchoolManager schoolManager = new SchoolManager(); //输出学院的员工id 和 学校总部的员工信息 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 ListgetAllEmployee() { List list = new ArrayList (); for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list CollegeEmployee emp = new CollegeEmployee(); emp.setId("学院员工id= " + i); list.add(emp); } return list; }}//学校管理类//分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则class SchoolManager { //返回学校总部的员工 public List getAllEmployee() { List list = new ArrayList (); for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list Employee emp = new Employee(); emp.setId("学校总部员工id= " + i); list.add(emp); } return list; } //该方法完成输出学校总部和学院员工信息(id) void printAllEmployee(CollegeManager sub) { //分析问题 //1. 这里的 CollegeEmployee 不是 SchoolManager的直接朋友 //2. CollegeEmployee 是以局部变量方式出现在 SchoolManager //3. 违反了 迪米特法则 //获取到学院员工 List list1 = sub.getAllEmployee(); System.out.println("------------学院员工------------"); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } //获取到学校总部员工 List list2 = this.getAllEmployee(); System.out.println("------------学校总部员工------------"); for (Employee e : list2) { System.out.println(e.getId()); } }}
出现问题:学校总部员工管理类SchoolManager 中以局部变量方式使用了 学院的员工类CollegeEmployee,违反了 迪米特法则,这里的 CollegeEmployee 不是SchoolManager的直接朋友
1)前面设计的问题在于SchoolManager中,CollegeEmployee类并不是SchoolManager类的直接朋友 (分析)
2) 按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合 3) 对代码按照迪米特法则 进行改进package DesignPattern.Test;import java.util.ArrayList;import java.util.List;/** * @author 孙一鸣 on 2020/2/3 *///客户端public class 迪米特法则 { public static void main(String[] args) { //创建了一个 SchoolManager 对象 SchoolManager schoolManager = new SchoolManager(); //输出学院的员工id 和 学校总部的员工信息 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 ListgetAllEmployee() { List list = new ArrayList (); for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list CollegeEmployee emp = new CollegeEmployee(); emp.setId("学院员工id= " + i); list.add(emp); } return list; } public void printEmployee(){ //获取到学院员工 List list1 = this.getAllEmployee(); System.out.println("------------学院员工------------"); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } }}//学校管理类//分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则class SchoolManager { //返回学校总部的员工 public List getAllEmployee() { List list = new ArrayList (); for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list Employee emp = new Employee(); emp.setId("学校总部员工id= " + i); list.add(emp); } return list; } //该方法完成输出学校总部和学院员工信息(id) void printAllEmployee(CollegeManager sub) { //分析问题 //1. 这里的 CollegeEmployee 不是 SchoolManager的直接朋友 //2. CollegeEmployee 是以局部变量方式出现在 SchoolManager //3. 违反了 迪米特法则 //获取到学院员工 sub.printEmployee(); //获取到学校总部员工 List list2 = this.getAllEmployee(); System.out.println("------------学校总部员工------------"); for (Employee e : list2) { System.out.println(e.getId()); } }}
转载地址:http://wlthn.baihongyu.com/