设计模式——从零到一,从入门到精通

本文深入探讨了设计模式中的七大原则,包括单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则和合成复用原则,并通过实例代码详细解释了每种原则的运用。此外,文章还介绍了23种设计模式,如代理模式、命令模式和职责链模式,阐述了它们在智能生活项目和OA系统采购审批等场景中的应用。通过这些设计模式,开发者可以更好地实现代码的可扩展性和维护性,遵循软件设计的最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

1、设计模式得诞生

软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在 1990 年代从建筑设计领域引入到计算机科学的。

2、设计模式好处

  • 大厦 VS 简易房
  • 拿实际工作经历来说, 当一个项目开发完后,如果客户提出增新功能,怎么办?。(可扩展性,使用设计模式,软件具有很好的扩展性)
  • 如果项目开发完后,原来程序员离职,你接手维护该项目怎么办? (维护性[可读性、规范性])
  • 目前程序员门槛越来越高,一线 IT 公司(大厂),都会问你在实际项目中使用过什么设计模式,怎样使用的,解决了什么问题。
  • 如果想成为合格软件工程师,那就花时间来研究下设计模式是非常必要的

第一章、设计模式七大原则

七大原则包括:
1、单一职责原则
2、接口隔离原则
3、依赖倒转(倒置)原则
4、里氏替换原则
5、开闭原则
6、迪米特法则
7、合成复用原则

1、单一职责原则

1.1、基本介绍

对类来说的,即一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2

1.2、代码分析

方案一

package com.tang.designpattern.principle.singleresponsibility;

import com.tang.designpattern.domain.pojo.Vehicle;

/**
 * @author Dream
 * @date 2022/4/12 14:26
 * 方案一分析:
 * 1、在方式 1 的 run 方法中,违反了单一职责原则
 * 2、解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可
 */
public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.run("飞机");
    }
}
package com.tang.designpattern.domain.pojo;

/**
 * @author Dream
 * @date 2022/4/12 14:27
 * 车辆类
 */
public class Vehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + " 在公路上运行....");
    }
}

方案一分析:
1、在方式 1 的 run 方法中,违反了单一职责原则
2、解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可

方案二

package com.tang.designpattern.principle.singleresponsibility;

import com.tang.designpattern.domain.pojo.AirVehicle;
import com.tang.designpattern.domain.pojo.RoadVehicle;

/**
 * @author Dream
 * @date 2022/4/12 14:30
 * 方案二分析:
 * 1、遵守单一职责原则
 * 2、但是这样做的改动很大,即将类分解,同时修改客户端
 * 3、改进:直接修改 Vehicle 类,改动的代码会比较少=>方案 3
 */
public class SingleResponsibility2 {
    public static void main(String[] args) {
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("摩托车");
        roadVehicle.run("汽车");

        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("飞机");

    }
}
package com.tang.designpattern.domain.pojo;

/**
 * @author Dream
 * @date 2022/4/12 14:31
 * 公路交通工具类
 */
public class RoadVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "公路运行");
    }
}
package com.tang.designpattern.domain.pojo;

/**
 * @author Dream
 * @date 2022/4/12 14:31
 * 天空交通工具类
 */
public class AirVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "天空运行");
    }
}

方案二分析:
1、遵守单一职责原则
2、但是这样做的改动很大,即将类分解,同时修改客户端
3、改进:直接修改 Vehicle 类,改动的代码会比较少=>方案 3

方案三

package com.tang.designpattern.principle.singleresponsibility;

import com.tang.designpattern.domain.pojo.Vehicle2;

/**
 * @author Dream
 * @date 2022/4/12 14:37
 * 方案三分析:
 * 1、这种修改方法没有对原来的类做大的修改,只是增加方法
 * 2、这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责
 */
public class SingleResponsibility3 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Vehicle2 vehicle2	= new Vehicle2();
        vehicle2.run("汽车");
        vehicle2.runWater("轮船");
        vehicle2.runAir("飞机");

    }
}
package com.tang.designpattern.domain.pojo;

/**
 * @author Dream
 * @date 2022/4/12 14:36
 */
public 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、这种修改方法没有对原来的类做大的修改,只是增加方法
2、这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责

1.3、单一职责原则注意事项和细节

1、降低类的复杂度,一个类只负责一项职责。
2、提高类的可读性,可维护性
3、降低变更引起的风险
4、通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

2、接口隔离原则

2.1、基本介绍

1、客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
类图
2、类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,如果接口 Interface1 对于类 A 和类 C来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法。
3、按隔离原则应当这样处理:
将接口 Interface1 拆分为独立的几个接口(这里我们拆分成 3 个接口),类 A 和类 C 分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则

2.2、代码分析

改进前

package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:43
 */
public class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface1 i) {
        i.operation2();
    }

    public void depend3(Interface1 i) {
        i.operation3();

    }
}
	
package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:41
 */
public 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");
    }
}

package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:45
 */
public class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend4(Interface1 i) {
        i.operation4();
    }

    public void depend5(Interface1 i) {
        i.operation5();
    }

}

package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:43
 */
public 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");
    }
}

package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:40
 */
public interface Interface1 {
    void operation1();

    void operation2();

    void operation3();

    void operation4();

    void operation5();
}

package com.tang.designpattern.principle.interfacesegregation.before;

/**
 * @author Dream
 * @date 2022/4/12 15:45
 */
public class Segregation1 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.depend1(b);
    }
}

方案分析

接口Interface1对于类A和类C不是最小的 因为对于A来说operation4,operation5方法是多余的,而对于C来说operation2,operation3是多余的。

改进后

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 16:03
 */
public class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }


    public void depend2(Interface2 i) {
        i.operation2();
    }


    public void depend3(Interface2 i) {
        i.operation3();
    }

}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 15:52
 */
public 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");
    }
}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 16:03
 */
public class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }


    public void depend4(Interface3 i) {
        i.operation4();
    }


    public void depend5(Interface3 i) {
        i.operation5();
    }
}


package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 16:02
 */
public 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");
    }
}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 15:51
 */
public interface Interface1 {
    void operation1();
}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 15:51
 */
public interface Interface2 {
    void operation2();

    void operation3();

}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 15:51
 */
public interface Interface3 {
    void operation4();

    void operation5();

}

package com.tang.designpattern.principle.interfacesegregation.after;

/**
 * @author Dream
 * @date 2022/4/12 16:05
 */
public class Segregation2 {
    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());

    }
}

方案分析

1、类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,如果接口 Interface1 对于类 A 和类 C来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法
2、将接口 Interface1 拆分为独立的几个接口,类 A 和类 C 分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
3、接口 Interface1 中出现的方法,根据实际情况拆分为三个接口
在这里插入图片描述

3、依赖倒转原则

3.1、基本介绍

依赖倒转原则(Dependence Inversion Principle)是指:

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  2. 抽象不应该依赖细节,细节应该依赖抽象
  3. 依赖倒转(倒置)的中心思想是面向接口编程
  4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
3.2、代码分析

实现方案 1

package com.tang.designpattern.principle.dependenceinversion;

/**
 * @author Dream
 * @date 2022/4/12 16:28
 */
public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}

package com.tang.designpattern.principle.dependenceinversion;

/**
 * @author Dream
 * @date 2022/4/12 16:28
 */
public class Email {
    public String getInfo() {
        return "电子邮件信息: hello,world";
    }
}

package com.tang.designpattern.principle.dependenceinversion;

/**
 * @author Dream
 * @date 2022/4/12 16:29
 */
public class Person {
    public void receive(Email email) {
        System.out.println(email.getInfo());
    }
}

方案分析

分析

  1. 简单,比较容易想到
  2. 如果我们获取的对象是 微信,短信等等,则新增类,同时 Perons 也要增加相应的接收方法
  3. 解决思路:引入一个抽象的接口 IReceiver, 表示接收者, 这样 Person 类与接口 IReceiver 发生依赖
    因为 Email, WeiXin 等等属于接收的范围,他们各自实现 IReceiver 接口就 ok, 这样我们就符号依赖倒>转原则

方案 2

package com.tang.designpattern.principle.dependenceinversion.case2;

/**
 * @author Dream
 * @date 2022/4/12 16:34
 */
public class DependecyInversion {
    public static void main(String[] args) {
        //客户端无需改变
        Person person = new Person();
        person.receive(new Email());
        person.receive(new WeiXin());

    }
}

package com.tang.designpattern.principle.dependenceinversion.case2;

/**
 * @author Dream
 * @date 2022/4/12 16:35
 */
public class Email implements IReceiver{
    @Override
    public String getInfo() {
        return "电子邮件信息: hello,world";
    }
}

package com.tang.designpattern.principle.dependenceinversion.case2;

/**
 * @author Dream
 * @date 2022/4/12 16:34
 */
public interface IReceiver {
    public String getInfo();
}

package com.tang.designpattern.principle.dependenceinversion.case2;

/**
 * @author Dream
 * @date 2022/4/12 16:35
 */
public class Person {
    //这里我们是对接口的依赖
    public void receive(IReceiver receiver) {
        System.out.println(receiver.getInfo());
    }

}

package com.tang.designpattern.principle.dependenceinversion.case2;

/**
 * @author Dream
 * @date 2022/4/12 16:35
 */
public class WeiXin implements IReceiver{
    @Override
    public String getInfo() {
        return "微信信息: hello,ok";
    }
}

方案二分析

引入一个抽象的接口 IReceiver表示发生信息的行为 接着Email, WeiXin 等等都可以发送信息,他们各自实现 IReceiver 接口就 ok, 这样我们就符号依赖倒转原则

3.3、依赖关系传递的三种方式和应用案例
  1. 接口传递
package com.tang.designpattern.principle.dependenceinversion.case3.byinterface;

/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public class ChangHong implements ITV{
    @Override
    public void play() {
        System.out.println("长虹电视机,打开");
    }
}

package com.tang.designpattern.principle.dependenceinversion.case3.byinterface;

/**
 * @author Dream
 * @date 2022/4/12 16:46
 */
public class DependencyPass {
    public static void main(String[] args) {
        //通过接口进行依赖传递
        ChangHong changHong = new ChangHong();
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.open(changHong);
    }
}
package com.tang.designpattern.principle.dependenceinversion.case3.byinterface;

/**
 * @author Dream
 * @date 2022/4/12 16:46
 */
public interface IOpenAndClose {
    public void open(ITV tv); //抽象方法,接收接口
}

package com.tang.designpattern.principle.dependenceinversion.case3.byinterface;

/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public interface ITV {
    public void play();
}

package com.tang.designpattern.principle.dependenceinversion.case3.byinterface;

/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public class OpenAndClose implements IOpenAndClose{
    @Override
    public void open(ITV tv) {
        tv.play();
    }
}

  1. 构造方法传递
package com.tang.designpattern.principle.dependenceinversion.case3.constructionmethod;


/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public class ChangHong implements ITV {
    @Override
    public void play() {
        System.out.println("长虹电视机,打开");
    }
}

package com.tang.designpattern.principle.dependenceinversion.case3.constructionmethod;
/**
 * @author Dream
 * @date 2022/4/12 16:46
 */
public class DependencyPass {
    public static void main(String[] args) {
        //通过接口进行依赖传递
        ChangHong changHong = new ChangHong();
        //通过构造器进行依赖传递
        OpenAndClose openAndClose = new OpenAndClose(changHong);
        openAndClose.open();
    }
}

package com.tang.designpattern.principle.dependenceinversion.case3.constructionmethod;

/**
 * @author Dream
 * @date 2022/4/12 16:52
 */
public interface IOpenAndClose {
    public void open(); //抽象方法
}

package com.tang.designpattern.principle.dependenceinversion.case3.constructionmethod;

/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public interface ITV {
    public void play();
}

package com.tang.designpattern.principle.dependenceinversion.case3.constructionmethod;

/**
 * @author Dream
 * @date 2022/4/12 16:53
 */
public class OpenAndClose implements IOpenAndClose {
    //成员
    public ITV tv;

    //构造器
    public OpenAndClose(ITV tv) {
        this.tv = tv;
    }

    @Override
    public void open() {
        this.tv.play();
    }
}

  1. setter 方式传递
package com.tang.designpattern.principle.dependenceinversion.case3.bysetter;

/**
 * @author Dream
 * @date 2022/4/12 16:47
 */
public class ChangHong implements ITV {
    @Override
    public void play() {
        System.out.println("长虹电视机,打开");
    }
}

package com.tang.designpattern.principle.dependenceinversion.case3.bysetter;

/**
 * @author Dream
 * @date 2022/4/12 16:46
 */
public class DependencyPass {
    public static void main(String[] args) {
        ChangHong changHong = new ChangHong();
        //通过 setter 方法进行依赖传递
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.setTv(changHong);
        openAndClose.open();

    }
}

package com.tang.designpattern.principle.dependenceinversion.case3.bysetter;

/**
 * @author Dream
 * @date 2022/4/12 17:01
 */
public interface IOpenAndClose {
    // 抽象方法
    public void open();

    public void setTv(ITV tv);
}

package com.tang.designpattern.principle.dependenceinversion.case3.bysetter;

/**
 * @author Dream
 * @date 2022/4/12 17:01
 */
public interface ITV {
    public void play();
}

package com.tang.designpattern.principle.dependenceinversion.case3.bysetter;

/**
 * @author Dream
 * @date 2022/4/12 17:02
 */
public class OpenAndClose implements IOpenAndClose{
    private ITV tv;
    @Override
    public void open() {
        this.tv.play();
    }

    @Override
    public void setTv(ITV tv) {
        this.tv = tv;
    }
}

3.4、依赖倒转原则的注意事项和细节
  1. 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
  2. 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
  3. 继承时遵循里氏替换原则

4、里氏替换原则

4.1、基本介绍
  1. 里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。

  2. 如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。

  3. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法

  4. 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。.

4.2、问题及思考

来看一段代码所带来的问题

package com.tang.designpattern.principle.liskovsubstitution;

/**
 * @author Dream
 * @date 2022/4/18 10:10
 */
public class A {
    // 返回两个数的差
    public int func1(int num1, int num2) {
        return num1 - num2;
    }

}

package com.tang.designpattern.principle.liskovsubstitution;

/**
 * @author Dream
 * @date 2022/4/18 10:11
 */
public class B extends A {
    //这里,重写了 A 类的方法,  可能是无意识
    public int func1(int a, int b) {
        return a + b;
    }

    public int func2(int a, int b) {
        return func1(a, b) + 9;
    }

}

package com.tang.designpattern.principle.liskovsubstitution;

/**
 * @author Dream
 * @date 2022/4/18 10:10
 */
public class Liskov {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        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));//这里本意是求出 11-3
        System.out.println("1-8=" + b.func1(1, 8));// 1-8 System.out.println("11+3+9=" + b.func2(11, 3));

    }
}

4.3、解决方案
  1. 我们发现原来运行正常的相减功能发生了错误。原因就是类 B 无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
  2. 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替.
  3. 改进方案
    在这里插入图片描述
  4. 代码实现
package com.tang.designpattern.principle.liskovsubstitution.solve;

import com.tang.designpattern.principle.liskovsubstitution.solve.Base;

/**
 * @author Dream
 * @date 2022/4/18 10:21
 */
public class A extends Base {
    // 返回两个数的差
    public int func1(int num1, int num2) {
        return num1 - num2;
    }

}

package com.tang.designpattern.principle.liskovsubstitution.solve;

/**
 * @author Dream
 * @date 2022/4/18 10:21
 */
public class B extends Base {
    //如果 B 需要使用 A 类的方法,使用组合关系
    private A a = new A();

    //这里,重写了 A 类的方法,  可能是无意识
    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);
    }

}

package com.tang.designpattern.principle.liskovsubstitution.solve;

/**
 * @author Dream
 * @date 2022/4/18 10:20
 */
public class Base {
    //把更加基础的方法和成员写到 Base 类
}

package com.tang.designpattern.principle.liskovsubstitution.solve;

/**
 * @author Dream
 * @date 2022/4/18 10:20
 */
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 类,因此调用者,不会再 func1 是求减法
        //调用完成的功能就会很明确
        System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出 11+3
        System.out.println("1+8=" + b.func1(1, 8));// 1+8 System.out.println("11+3+9=" + b.func2(11, 3));


        //使用组合仍然可以使用到 A 类相关方法
        System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出 11-3

    }
}

5、开闭原则

5.1、基本介绍
  1. 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则

  2. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。

  3. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

  4. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

5.2、代码分析
  1. 类图
    在这里插入图片描述
  2. 代码
package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:31
 */
public class Circle extends Shape {
    Circle() {
        super.m_type = 2;
    }

}

package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:29
 */
public 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(" 绘制三角形 ");
    }

}

package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:28
 */
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());
    }
}

package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:30
 */
public class Rectangle extends Shape {
    Rectangle() {
        super.m_type = 1;
    }

}

package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:30
 */
public class Shape {
    int m_type;
}

package com.tang.designpattern.principle.openclosed;

/**
 * @author Dream
 * @date 2022/4/18 10:31
 */
public class Triangle extends Shape {
    Triangle() {
        super.m_type = 3;
    }

}

  1. 优缺点
  • 优点是比较好理解,简单易操作。
  • 缺点是违反了设计模式的 ocp 原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
  • 比如我们这时要新增加一个图形种类 三角形,我们需要做如下修改,修改的地方较多
  1. 改进的思路分析
  • 思路:把创建 Shape 类做成抽象类,并提供一个抽象的 draw 方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承 Shape,并实现 draw 方法即可,使用方的代码就不需要修 -> 满足了开闭原则

  • 代码

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:42
 */
public class Circle extends Shape{
    Circle() {
        super.m_type = 2;
    }

    @Override
    public void draw() {
        System.out.println(" 绘制圆形 ");
    }
}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:41
 */
public class GraphicEditor {
    //接收 Shape 对象,调用 draw 方法
    public void drawShape(Shape s) {
        s.draw();
    }

}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:41
 */
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());

    }
}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:43
 */
public class OtherGraphic extends Shape{
    OtherGraphic() {
        super.m_type = 4;
    }

    @Override
    public void draw() {
        System.out.println(" 绘制其它图形 ");
    }
}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:42
 */
public class Rectangle extends Shape{
    Rectangle() {
        super.m_type = 1;
    }

    @Override
    public void draw() {
        // TODO Auto-generated method stub
        System.out.println(" 绘制矩形 ");
    }
}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:41
 */
public abstract class Shape {
    int m_type;
    public abstract void draw();//抽象方法
}

package com.tang.designpattern.principle.openclosed.after;

/**
 * @author Dream
 * @date 2022/4/18 10:43
 */
public class Triangle extends Shape{
    Triangle() {
        super.m_type = 3;
    }

    @Override
    public void draw() {
        System.out.println(" 绘制三角形  ");
    }
}

6、迪米特法则

6.1、基本介绍
  1. 一个对象应该对其他对象保持最少的了解

  2. 类与类关系越密切,耦合度越大

  3. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息

  4. 迪米特法则还有个更简单的定义:只与直接的朋友通信

  5. 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。

6.2、应用实例
  1. 有一个学校,下属有各个学院和总部,现要求打印出学校总部员工 ID 和学院员工的 id
  2. 编程实现上面的功能, 看代码演示
  3. 代码演示
package com.tang.designpattern.principle.demeterprinciple.before;

/**
 * @author Dream
 * @date 2022/4/22 13:35
 * 学院的员工类
 */
public class CollegeEmployee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

}

package com.tang.designpattern.principle.demeterprinciple.before;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Dream
 * @date 2022/4/22 13:36
 */
public class CollegeManager {
    //返回学院的所有员工
    public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        for (int i = 0; i < 10; i++) { //这里我们增加了 10 个员工到 list
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工 id= " + i);
            list.add(emp);
        }
        return list;
    }
}

package com.tang.designpattern.principle.demeterprinciple.before;

/**
 * @author Dream
 * @date 2022/4/22 13:34
 */
public class Demeter1 {
    public static void main(String[] args) {
        //创建了一个 SchoolManager 对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院的员工 id  和	学校总部的员工信息
        schoolManager.printAllEmployee(new CollegeManager());

    }
}

package com.tang.designpattern.principle.demeterprinciple.before;

/**
 * @author Dream
 * @date 2022/4/22 13:35
 * 学校总部员工类
 */
public class Employee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}


package com.tang.designpattern.principle.demeterprinciple.before;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Dream
 * @date 2022/4/22 13:36
 */
public class SchoolManager {
    //返回学校总部的员工
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<Employee>();

        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<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());
        }
    }

}

6.3、应用实例改进
  1. 前面设计的问题在于 SchoolManager 中,CollegeEmployee 类并不是 SchoolManager 类的直接朋友 (分析)
  2. 按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合
  3. 对代码按照迪米特法则 进行改进. (看老师演示)
  4. 代码演示
package com.tang.designpattern.principle.demeterprinciple.after;

/**
 * @author Dream
 * @date 2022/4/22 13:35
 * 学院的员工类
 */
public class CollegeEmployee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

}

package com.tang.designpattern.principle.demeterprinciple.after;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Dream
 * @date 2022/4/22 13:36
 */
public class CollegeManager {
    //返回学院的所有员工
    public List<CollegeEmployee> getAllEmployee() {
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        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<CollegeEmployee> list1 = getAllEmployee();
        System.out.println("------------学院员工------------");

        for (CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }
    }

}

package com.tang.designpattern.principle.demeterprinciple.after;

/**
 * @author Dream
 * @date 2022/4/22 13:34
 */
public class Demeter1 {
    public static void main(String[] args) {
        //创建了一个 SchoolManager 对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院的员工 id  和	学校总部的员工信息
        schoolManager.printAllEmployee(new CollegeManager());

    }
}

package com.tang.designpattern.principle.demeterprinciple.after;

/**
 * @author Dream
 * @date 2022/4/22 13:35
 * 学校总部员工类
 */
public class Employee {
    private String id;

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}


package com.tang.designpattern.principle.demeterprinciple.after;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Dream
 * @date 2022/4/22 13:36
 */
public class SchoolManager {
    //返回学校总部的员工
    public List<Employee> getAllEmployee() {
        List<Employee> list = new ArrayList<Employee>();

        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. 将输出学院的员工方法,封装到 CollegeManager
        sub.printEmployee();

        //获取到学校总部员工
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------学校总部员工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }

}

6.4、迪米特法则注意事项和细节
  1. 迪米特法则的核心是降低类之间的耦合
  2. 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系

7、合成复用原则

7.1、基本介绍

原则是尽量使用合成/聚合的方式,而不是使用继承

7.2、代码分析
package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:17
 */
public class Black implements Color{
    @Override
    public String color() {
        return "黑色";
    }
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:18
 */
public class Car {
    protected Color color;

    public Car(Color color) {
        this.color = color;
    }

    public void move() {
        System.out.println(color.color() + "汽车移动");
    }
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:16
 */
public interface Color {
    String color();
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:22
 */
public class CRPTest {
    public static void main(String[] args) {
        White white = new White();
        Black black = new Black();
        Red red = new Red();

        Car car1 = new GasolineCar(white);
        Car car2 = new GasolineCar(black);
        Car car3 = new GasolineCar(red);
        Car car4 = new ElectricCar(white);
        Car car5 = new ElectricCar(black);
        Car car6 = new ElectricCar(red);
        car1.move();
        car2.move();
        car3.move();
        car4.move();
        car5.move();
        car6.move();
    }
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:22
 */
public class ElectricCar extends Car {
    public ElectricCar(Color color) {
        super(color);
    }

    @Override
    public void move() {
        System.out.println(color.color() + "电动汽车移动");
    }
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:19
 */
public class GasolineCar extends Car {

    public GasolineCar(Color color) {
        super(color);
    }

    @Override
    public void move() {
        System.out.println(color.color() + "汽油汽车移动");
    }
}


package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:18
 */
public class Red implements Color{
    @Override
    public String color() {
        return "红色";
    }
}

package com.tang.designpattern.principle.compositereuse;

/**
 * @author Dream
 * @date 2022/4/22 14:16
 */
public class White implements Color{
    @Override
    public String color() {
        return "白色";
    }
}
7.3、合成复用原则的实现方法

合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

以汽车分类管理程序为例:
分析:汽车按“动力源"划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这这两种分类,其组合就很多。

第二章、23种设计模式

设计模式类型:
设计模式分为三种类型,共 23 种

  1. 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
  2. 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  3. 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
    注意:不同的书籍上对分类和名称略有差别

1.设计模式——代理模式

1.1.代理模式的基本介绍

(1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

(2)被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象
(3)代理模式有不同的形式,主要有三种 静态代理,动态代理(JDK代理,接口代理)和Cglib代理

1.2.静态代理

基本介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

应用实例

具体要求
(1)定义一个接口:ITeacherDao
(2)目标对象TeacherDao实现ITeacherDao
(3)使用静态代理方式,就需要在代理对象TeacherDaoProxy中也实现ITeacherDao
(4)调用的时候通过调用代理对象的方法来调用目标对象
(5)特别提醒:代理独享与目标对象要实现相同的接口,然会通过调用相同的方法来调用目标对象的方法。

(6)类图如下
在这里插入图片描述

代码示例:

package com.tang.DesignPatterns.Proxy.StaticProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:23
 */

public interface ITeacherDao {
    void teach();
}
package com.tang.DesignPatterns.Proxy.StaticProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:24
 */
/**
 * 被代理对象
 */
public class TeacherDaoImpl implements ITeacherDao{
    @Override
    public void teach() {
        System.out.println("开始授课中....");
    }
}

package com.tang.DesignPatterns.Proxy.StaticProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:24
 */

/**
 * 代理对象
 */
public class TeacherDaoProxy implements ITeacherDao{
    private ITeacherDao teacherDao;


    public TeacherDaoProxy(ITeacherDao teacherDao) {
        this.teacherDao = teacherDao;
    }

    @Override
    public void teach() {
        System.out.println("开始代理中");
        teacherDao.teach();
        System.out.println("执行其他增强方法");
    }
}

package com.tang.DesignPatterns.Proxy.StaticProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:25
 */

public class Client {
    public static void main(String[] args) {
        TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(new TeacherDaoImpl());
        teacherDaoProxy.teach();
    }
}

静态代理优缺点

(1)优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
(2)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很对代理类
一旦接口增加方法,目标对象与代理都要维护

1.3.动态代理

基本介绍

(1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
(2)代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
(3)动态代理也叫做:JDK代理,接口代理

JDK生产代理对象的API

(1)代理类所在包:java.long,reflect.Proxy
(2)JDK实现代理只需要使用newProXyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object new ProxyInstance(ClassLoader loader,Class<?>[] interface,InvocationHandler h)
(3)类图如下
在这里插入图片描述

示例代码:

package com.tang.DesignPatterns.Proxy.DynamicProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:23
 */

public interface ITeacherDao {
    void teach();
}

package com.tang.DesignPatterns.Proxy.DynamicProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:24
 */

/**
 * 被代理对象
 */
public class TeacherDaoImpl implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("开始授课中....");
    }
}

package com.tang.DesignPatterns.Proxy.DynamicProxy;/*
 *   @author Dram
 *   @create 2021-07-19 14:30
 */

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("JDK动态代理开始...");
                Object returnVal = method.invoke(target, args);
                System.out.println("JDK动态代理结束...");
                return returnVal;
            }
        });
    }

}

package com.tang.DesignPatterns.Proxy.DynamicProxy;/*
 *   @author Dram
 *   @create 2021-07-19 11:25
 */

import com.tang.DesignPatterns.Proxy.StaticProxy.TeacherDaoProxy;

public class Client {
    public static void main(String[] args) {
        TeacherDaoImpl teacherDao = new TeacherDaoImpl();

        ProxyFactory proxyFactory = new ProxyFactory(teacherDao);

        ITeacherDao proxyInstance = (ITeacherDao) proxyFactory.getProxyInstance();

        proxyInstance.teach();

    }
}

优缺点
优点:
(1)不需要代理对象实现接口,只需要被代理对象实现接口
(2)接口方法扩展,只需维护被代理对象。不关心代理对象。代理对象是在内存中生成的。
缺点:
(1)利用反射机制。原理复杂。

1.4.Cglib代理

基本介绍

(1)静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何接口,这个时候可使用目标对象子类来实现代理。这就是Cglib代理
(2)Cglib代理也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书籍也将Cglib代理归属动态代理。
(3)Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
(4)在AOP编程中如何选择代理模式:
1.目标对象需要实现接口,用JDK代理
2.目标对象不需要实现接口,用Cglib代理
(5)Cglib包的底层时通过使用字节码处理框架ASM来转换字节码并生成新的类
(6)类图:

在这里插入图片描述

代码示例

package com.tang.DesignPatterns.Proxy.Cglib;/*
 *   @author Dram
 *   @create 2021-07-19 14:30
 */


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ProxyFactory implements MethodInterceptor {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxyInstance(){
        //1.创建工具类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(target.getClass());
        //3.设置回调函数
        enhancer.setCallback(this);
        //4.创建子类对象,即代理对象
        return enhancer.create();

    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("这里可以插入执行关键代码之前的逻辑");
        //执行目标对象方法
        Object returnVal = methodProxy.invokeSuper(o, objects);
        System.out.println("这里可以插入执行关键代码之后的逻辑");
        return returnVal;
    }
}

package com.tang.DesignPatterns.Proxy.Cglib;/*
 *   @author Dram
 *   @create 2021-07-19 11:24
 */


/**
 * 被代理对象
 */
public class TeacherDao{
    public String teach() {
        System.out.println(" 老师授课中	, 我是 cglib 代理,不需要实现接口 ");
        return "hello";
    }
}
 public class Client {
    public static void main(String[] args) {
        //创建目标对象
		TeacherDao target = new TeacherDao();
		//获取到代理对象,并且将目标对象传递给代理对象
		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();

		//执行代理对象的方法,触发 intecept  方法,从而实现 对目标对象的调用
		String res = proxyInstance.teach(); System.out.println("res=" + res);


    }
}

优点

不需要被代理对象实现接口

2.命令模式

2.1.智能生活项目需求
  1. 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作。
  2. 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个 App,分别控制,我们希望只要一个 app
    就可以控制全部智能家电。
  3. 要实现一个 app 控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给 app 调用,这时 就可以考虑使用命令模式。
  4. 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
  5. 在我们的例子中,动作的请求者是手机 app,动作的执行者是每个厂商的一个家电产品
2.2.命令模式基本介绍
  1. 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
    我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
  2. 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
  3. 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
  4. 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
    Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象
2.3.命令模式的原理类图

在这里插入图片描述

  • 对原理类图的说明-即(命名模式的角色及职责)
  1. Invoker 是调用者角色
  2. Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
  3. Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
  4. ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现 execute
2.4.代码示例

Command

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:49
 */
public interface Command {
    //执行动作(操作)
    public void execute();
    //撤销动作(操作)
    public void undo();
}

LightOffCommand

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:50
 */
public class LightOffCommand implements Command {
    // 聚合LightReceiver
    LightReceiver light;

    // 构造器
    public LightOffCommand(LightReceiver light) {
        super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        light.off();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        light.on();
    }
}

LightOnCommand

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:49
 */
public class LightOnCommand implements Command {
    //聚合LightReceiver
    LightReceiver light;

    //构造器
    public LightOnCommand(LightReceiver light) {
        super();
        this.light = light;
    }

    @Override
    public void execute() {
        //调用接收者的方法
        light.on();
    }

    @Override
    public void undo() {
        //调用接收者的方法
        light.off();
    }
}

LightReceiver

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:51
 */
public class LightReceiver {
    public void on() {
        System.out.println(" 电灯打开了.. ");
    }

    public void off() {
        System.out.println(" 电灯关闭了.. ");
    }

}

NoCommand

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:51
 */
public class NoCommand implements Command {
    @Override
    public void execute() {

    }

    @Override
    public void undo() {

    }

}

RemoteController

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:51
 */
public class RemoteController {
    // 开 按钮的命令数组
    Command[] onCommands;
    Command[] offCommands;
    // 执行撤销的命令
    Command undoCommand;

    // 构造器,完成对按钮初始化
    public RemoteController() {
        onCommands = new Command[5];
        offCommands = new Command[5];
        for (int i = 0; i < 5; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    // 给我们的按钮设置你需要的命令
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    // 按下开按钮
    public void onButtonWasPushed(int no) { // no 0
        // 找到你按下的开的按钮, 并调用对应方法
        onCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = onCommands[no];

    }

    // 按下开按钮
    public void offButtonWasPushed(int no) { // no 0
        // 找到你按下的关的按钮, 并调用对应方法
        offCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = offCommands[no];

    }

    // 按下撤销按钮
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}

TVOffCommand

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:50
 */
public class TVOffCommand implements Command {
    // 聚合TVReceiver
    TVReceiver tv;
    // 构造器
    public TVOffCommand(TVReceiver tv) {
        super();
        this.tv = tv;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        tv.off();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        tv.on();
    }
}

TVOnCommand

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:50
 */
public class TVOnCommand implements Command {
    // 聚合TVReceiver
    TVReceiver tv;
    // 构造器
    public TVOnCommand(TVReceiver tv) {
        super();
        this.tv = tv;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        tv.on();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        tv.off();
    }
}

TVReceiver

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:51
 */
public class TVReceiver {
    public void on() {
        System.out.println(" 电视机打开了.. ");
    }
    public void off() {
        System.out.println(" 电视机关闭了.. ");
    }
}

Client

package com.tang.designpattern.pattern.command;

/**
 * @author Dream
 * @date 2022/9/8 10:52
 */
public class Client {
    public static void main(String[] args) {
        //使用命令设计模式,完成通过遥控器,对电灯的操作

        //创建电灯的对象(接受者)
        LightReceiver lightReceiver = new LightReceiver();

        //创建电灯相关的开关命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        //需要一个遥控器
        RemoteController remoteController = new RemoteController();

        //给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        System.out.println("--------按下灯的开按钮-----------");
        remoteController.onButtonWasPushed(0);
        System.out.println("--------按下灯的关按钮-----------");
        remoteController.offButtonWasPushed(0);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();


        System.out.println("=========使用遥控器操作电视机==========");

        TVReceiver tvReceiver = new TVReceiver();

        TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver);
        TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);

        //给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作
        remoteController.setCommand(1, tvOnCommand, tvOffCommand);

        System.out.println("--------按下电视机的开按钮-----------");
        remoteController.onButtonWasPushed(1);
        System.out.println("--------按下电视机的关按钮-----------");
        remoteController.offButtonWasPushed(1);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();
    }
}

2.5.代码解释说明

Command 以及实现类是一个命令的具体实现。它与接收者绑定。最终都会调用接收者的方法
RemoteController 相当于是一个遥控器。它与命令绑定。通过调用该类方法,来调用命令的execute方法来让接收者执行命令。

2.6.命令模式的注意事项和细节
  1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
  2. 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
  3. 容易实现对请求的撤销和重做
  4. 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
  5. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
  6. 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制

3.章职责链模式

3.1.基本介绍
  1. 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
  2. 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
  3. 这种类型的设计模式属于行为型模式
3.2.职责链模式的原理类图

在这里插入图片描述

对原理类图的说明-即(职责链模式的角色及职责)

  1. Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
  2. ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
  3. Request , 含义很多属性,表示一个请求
3.3.职责链模式解决 OA 系统采购审批
  1. 应用实例要求
    编写程序完成学校 OA 系统的采购审批项目:需求采购员采购教学器材
    如果金额 小于等于 5000, 由教学主任审批如果金额 小于等于 10000, 由院长审批
    如果金额 小于等于 30000, 由副校长审批如果金额 超过 30000 以上,有校长审批
  2. 思路分析和图解(类图)
    在这里插入图片描述
  3. 代码实现
package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 13:58
 */
public abstract class Approver {
    /**
     * 下一个处理者
     */
    Approver approver;
    /**
     * 名字
     */
    String name;


    public Approver(String name) {
        this.name = name;
    }

    //下一个处理者
    public void setApprover(Approver approver) {
        this.approver = approver;
    }

    //处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
    public abstract void processRequest(PurchaseRequest purchaseRequest);

}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 14:00
 */
public class Client {
    public static void main(String[] args) {
        //创建一个请求
        PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
        //创建相关的审批人
        DepartmentApprover departmentApprover = new DepartmentApprover("张主任");
        CollegeApprover collegeApprover = new CollegeApprover("李院长");
        ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校");
        SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");
        //需要将各个审批级别的下一个设置好 (处理人构成环形: )
        departmentApprover.setApprover(collegeApprover);
        collegeApprover.setApprover(viceSchoolMasterApprover);
        viceSchoolMasterApprover.setApprover(schoolMasterApprover);
        schoolMasterApprover.setApprover(departmentApprover);
        departmentApprover.processRequest(purchaseRequest);
        // viceSchoolMasterApprover.processRequest(purchaseRequest);
    }
}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 14:01
 */
public class CollegeApprover extends Approver {
    public CollegeApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) {
            System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
        } else {
            approver.processRequest(purchaseRequest);
        }
    }
}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 14:02
 */
public class DepartmentApprover extends Approver {
    public DepartmentApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() <= 5000) {
            System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
        } else {
            approver.processRequest(purchaseRequest);
        }
    }
}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 13:59
 */
public class PurchaseRequest {
    /**
     * 请求类型
     */
    private int type = 0;
    /**
     * 请求金额
     */
    private float price = 0.0f;
    private int id = 0;

    //构造器
    public PurchaseRequest(int type, float price, int id) {
        this.type = type;
        this.price = price;
        this.id = id;

    }

    public int getType() {
        return type;
    }

    public float getPrice() {
        return price;
    }

    public int getId() {
        return id;
    }


}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 14:03
 */
public class SchoolMasterApprover extends Approver {
    public SchoolMasterApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() > 30000) {
            System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
        } else {
            approver.processRequest(purchaseRequest);
        }
    }
}

package com.tang.designpattern.pattern.chainOfResponsibility;

/**
 * @author Dream
 * @date 2022/9/13 14:03
 */
public class ViceSchoolMasterApprover extends Approver {

    public ViceSchoolMasterApprover(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) {
            System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
        } else {
            approver.processRequest(purchaseRequest);
        }
    }
}


3.4.职责链模式的注意事项和细节
  1. 将请求和处理分开,实现解耦,提高系统的灵活性
  2. 简化了对象,使对象不需要知道链的结构
  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  4. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat
    对 Encoding 的处理、拦截器
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值