Skip to content

前言: 如果你不想写出shi一样的代码,那就一定要熟练应用设计模式来解决一些场景问题。

设计模式六大原则:

设计模式的六大原则是编写高质量、易维护的软件的基础。

1. 单一职责原则 (Single Responsibility Principle, SRP)

单一职责原则要求一个类只负责一项职责或功能。换句话说,一个类只做一件事,这样可以提高类的可读性、可维护性和可测试性。

个人理解: 一个类负责一个职责,不要让类太累

示例:

java
class User {
    private String name;
    private String email;

    // Constructors, getters, setters
}

class UserService {
    public void createUser(User user) {
        // Code to create user
    }

    public void deleteUser(User user) {
        // Code to delete user
    }
}

class EmailService {
    public void sendEmail(String email, String message) {
        // Code to send email
    }
}

2. 开放关闭原则 (Open/Closed Principle, OCP)

开放关闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。通过继承和多态来实现扩展,避免修改已有的代码,从而提高系统的稳定性和可扩展性。

个人理解:对拓展开放 对修改关闭

java
interface Shape {
    void draw();
}

class Circle implements Shape {
    public void draw() {
        // Draw circle
    }
}

class Rectangle implements Shape {
    public void draw() {
        // Draw rectangle
    }
}

class ShapeDrawer {
    public void drawShapes(List<Shape> shapes) {
        for (Shape shape : shapes) {
            shape.draw();
        }
    }
}

3. 里氏替换原则 (Liskov Substitution Principle, LSP)

里氏替换原则要求子类对象能够替换父类对象,且保证原有功能不受影响。子类必须实现父类的所有方法,并且可以扩展新的功能,但不能改变父类的现有行为。

个人理解:父类引用指向子类对象,儿子可以替换父亲

示例

class Bird {
    public void fly() {
        // Bird can fly
    }
}

class Sparrow extends Bird {
    @Override
    public void fly() {
        // Sparrow can fly
    }
}

class Ostrich extends Bird {
    @Override
    public void fly() {
        // Ostrich cannot fly, violates LSP
        throw new UnsupportedOperationException("Ostrich cannot fly");
    }
}

4. 依赖倒置原则 (Dependency Inversion Principle, DIP)

依赖倒置原则要求高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。通过依赖接口或抽象类,而不是具体实现类,可以实现模块间的松耦合。

个人理解 依赖倒置 就是面向接口编程

示例

java
interface IMessageService {
    void sendMessage(String message);
}

class EmailService implements IMessageService {
    public void sendMessage(String message) {
        // Send email
    }
}

class SMSService implements IMessageService {
    public void sendMessage(String message) {
        // Send SMS
    }
}

class Notification {
    private IMessageService messageService;

    public Notification(IMessageService messageService) {
        this.messageService = messageService;
    }

    public void notify(String message) {
        messageService.sendMessage(message);
    }
}

5. 接口隔离原则 (Interface Segregation Principle, ISP)

接口隔离原则要求客户端不应该依赖它不需要的接口。将臃肿的接口拆分为更小、更具体的接口,使得客户端只需要知道它们感兴趣的方法。

个人理解: 接口的粒度要合适

java
interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class MultiFunctionPrinter implements Printer, Scanner {
    public void print() {
        // Print
    }

    public void scan() {
        // Scan
    }
}

class SimplePrinter implements Printer {
    public void print() {
        // Print
    }
}

6. 迪米特法则 (Law of Demeter, LoD)

迪米特法则要求一个对象应当对其他对象有尽可能少的了解,只与直接的朋友通信,不要与陌生的类交流。通过减少类之间的耦合度,可以提高系统的模块化和可维护性。

个人理解:最少知道原则, 对象与对象之间 尽量少引用,不要互相引用

java
class Engine {
    public void start() {
        // Start engine
    }
}

class Car {
    private Engine engine;

    public Car() {
        engine = new Engine();
    }

    public void startCar() {
        engine.start();
    }
}

class Driver {
    private Car car;

    public Driver(Car car) {
        this.car = car;
    }

    public void drive() {
        car.startCar();
    }
}

创建型

1、工厂方法模式

shell
定义一个创建对象的接口,但由子类决定需要实例化哪一个类,工厂方法使得子类实例化的过程推迟,根据入参生成对象的 ⼯⼚方法模式
java
//场景:商场结算,结算方式 有 打折,满减,正常收费 

//抽象父类    结账
public abstract class CashSuper {
    public abstract double acceptCash(double money);
}
java
//正常收费
public class CashNormal extends CashSuper{
    @Override
    public double acceptCash(double money) {
        return money;
    }
}
java
//打折
public class CashRebate extends CashSuper{
    private double rebate = 1.0;
    public CashRebate(double rebate) {
        this.rebate = rebate;
    }
    @Override
    public double acceptCash(double money) {
        return money * rebate;
    }
}
java
//满减
public class CashReturn extends CashSuper{
    //满多少
    private double moneyCondition = 0.0d;
    //减多少
    private double moneyReturn = 0.0d;

    public CashReturn(double moneyCondition, double moneyReturn) {
        this.moneyCondition = moneyCondition;
        this.moneyReturn = moneyReturn;
    }

    //满多少返多少
    @Override
    public double acceptCash(double money) {
        double result = money;
        if (money >= moneyCondition) {
            result = money - Math.floor(money / moneyCondition) * moneyReturn;
            return result;
        }
        return result;
    }
}
java
//工厂    根据参数 创建不同的对象
public class CashFactory {

    public static CashSuper getCashSuper(String type) {
        CashSuper cashSuper = null;
        if (type.equals("正常收费")) {
            cashSuper = new CashNormal();
        } else if (type.equals("打8折")) {
            cashSuper = new CashRebate(0.8);
        } else if (type.equals("满300返100")) {
            cashSuper = new CashReturn(300,100);
        }else{
            throw new IllegalArgumentException("不支持的收费方式");
        }
        return cashSuper;
    }

}
java
public class FactoryTest {
	//测试工厂方法模式 创建对象
    public static void main(String[] args) {
        //CashSuper cashSuper = CashFactory.getCashSuper("打8折");
        CashSuper cashSuper = CashFactory.getCashSuper("满300返100");

        double v = cashSuper.acceptCash(700);

        System.out.println(v);

    }
}

2、抽象工厂模式

shell
提供一个接口 ,可以创建一系列相关或相互依赖的对象,而无需指定他们具体的类
java
// 抽象工厂接口    
public interface  CarFactory {

    Engine createEngine();
    Chassis createChassis();

}
java
// 抽象产品A - 发动机
public interface Engine {
    void start();

}
java
// 抽象产品B - 底盘
public interface Chassis {
    void run();

}
java
// 具体产品A1 - 高端发动机
public class HighEndEngine implements Engine{
    @Override
    public void start() {
        System.out.println("High-end engine starts.");
    }
}
java
// 具体产品B1 - 高端底盘
public class HighEndChassis implements Chassis {
    @Override
    public void run() {
        System.out.println("High-end chassis runs smoothly.");
    }
}
java
// 具体产品A2 - 低端发动机
public class LowEndEngine implements Engine{
    @Override
    public void start() {
        System.out.println("Low-end engine starts.");
    }
}
java
// 具体产品B2 - 低端底盘
public class LowEndChassis implements Chassis{
    @Override
    public void run() {
        System.out.println("Low-end chassis runs.");
    }
}
java
// 具体工厂1 - 生产高端车型
public class HighEndCarFactory implements CarFactory{
    @Override
    public Engine createEngine() {
        return new HighEndEngine();
    }

    @Override
    public Chassis createChassis() {
        return new HighEndChassis();
    }
}
java
// 具体工厂2 - 生产低端车型
public class LowEndCarFactory implements CarFactory{
    @Override
    public Engine createEngine() {
        return new LowEndEngine();
    }

    @Override
    public Chassis createChassis() {
        return new LowEndChassis();
    }
}
java
/**
 * @author junyang
 * @date 2024/1/15
 * @desc  抽象工厂模式  字面意思  工厂都是抽象的
 * 抽象工厂 生产的对象也是抽象的 ,当然 抽象工厂有实现类,
 * 抽象工厂生产的抽象对象 也有实现类
 */
public class TestDemo1 {

    public static void main(String[] args) {
        // 生产高端车型
        CarFactory highEndFactory = new HighEndCarFactory();
        Engine highEndEngine = highEndFactory.createEngine();
        Chassis highEndChassis = highEndFactory.createChassis();

        highEndEngine.start();
        highEndChassis.run();

        // 生产低端车型
        CarFactory lowEndFactory = new LowEndCarFactory();
        Engine lowEndEngine = lowEndFactory.createEngine();
        Chassis lowEndChassis = lowEndFactory.createChassis();

        lowEndEngine.start();
        lowEndChassis.run();
    }
}

3、建造者模式

shell
 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
 适用于构建具有复杂参数的对象
 
 封装一个复杂对象的构建过程,并可以按步骤构造的**建造者模式**
java
//抽象建造者类
public abstract class StudentBuilder {

    public abstract void buildName(String name);
    public abstract void buildAge(int age);
    public abstract Student makeStudent();

}
java
/**
 * @date 2023/12/8
 * @desc 具体建造者类
 */
public class StudentActualBuilder extends StudentBuilder{

    /**
     * 这里使用组合,将 student 组合到实现类中
     */
    private Student student = new Student();

    @Override
    public void buildName(String name) {
        student.setName(name);
    }

    @Override
    public void buildAge(int age) {
        student.setAge(age);
    }

    @Override
    public Student makeStudent() {
        return student;
    }
}
java
/**
 * @date 2023/12/8
 * @desc   要建造的产品
 */
@Data
public class Student {

    private String name;

    private int age;

}
java
/**
 * @date 2023/12/8
 * @desc   指挥者
 */
public class Commander {

    /**
     * 注入StudentBuilder
     */
    private StudentBuilder studentBuilder;

    public void setStudentBuilder(StudentBuilder studentBuilder) {
        this.studentBuilder = studentBuilder;
    }

    public Student makeStudent(String name, int age) {
        this.studentBuilder.buildAge(age);
        this.studentBuilder.buildName(name);
        return this.studentBuilder.makeStudent();
    }

}

案例2 工作中常用得方式

java

/**
 * @date 2023/12/8
 * @desc    建造者模式
 */
public class User {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年龄
     */
    private int age;

    /**
     * 学生类的构造函数
     *
     * @param name 的名字
     * @param age  年龄
     */
    User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 构建器(本质上就是指挥者Commander)
     *
     * @return {@link UserBuilder}
     */
    public static UserBuilder builder() {
        // 构造一个StudentBuilder对象
        return new UserBuilder();
    }

    /**
     * 学生构建器(相当于StudentBuilder及其实现类StudentActualBuilder)
     *
     * @author 第七人格
     * @date 2020/12/02
     */
    public static class UserBuilder {
        private String name;
        private int age;

        public UserBuilder() {
        }

        public UserBuilder name(String name) {
            this.name = name;
            // 返回自身(StudentBuilder),以便链式调用
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            // 返回自身(StudentBuilder),以便链式调用
            return this;
        }

        /**
         * 构建
         *
         * @return {@link User}
         */
        public User build() {
            // 构造一个Student对象,其中的属性直接从外部传入
            return new User(this.name, this.age);
        }

        @Override
        public String toString() {
            return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}

4、原型模式

shell
  通过复制现有的实例来创建新的对象,而不是通过类构造函数实例化。用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象

需求:我们从远程RPC获取了一份数据,假设为数据A,在不影响数A的情况下,我们需要从数据A复制一份数据B,然后在数据B上进行操作。

  • 因为是将数据A复制到数据B,所以可以实现Cloneable,使用原型模式。

  • 又因为不能影响数据A,所以我们需要考虑使用浅拷贝还是深拷贝。

    怎么理解浅拷贝和深拷贝呢?

    **浅拷贝是按位拷贝对象,会创建一个新对象,但不会修改原对象的值。它复制的是对象的引用地址,没有开辟新的栈,所以复制的结果是两个对象指向同一个地址。**因此,当修改其中一个对象的属性时,另一个对象的属性也会跟着改变。

    **深拷贝是复制对象本身,不共享内存。这意味着它会创建一个新的对象,并且复制原对象的所有字段以及字段所指向的动态分配内存。**因此,修改新对象不会影响原对象。

    深拷贝相比于浅拷贝速度较慢并且花销较大,因为它需要复制更多的数据。

    举个例子,浅拷贝就好比钢铁侠的机甲,不管他制造了多少套,但是他们的AI都是贾维斯,都是一个。 而深拷贝,就是一套机甲一个AI。

所以我们上面的需求,需要使用深拷贝。

java
package com.glls.creational.prototype;

import java.io.*;

/**
 * @author junyang
 * @date 2023/12/27
 * @desc 原型模式
 */
public class RpcData implements Cloneable, Serializable {
    private String name;
    private String age;
    private Pet pet;



    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }

    @Override
    public String toString() {
        System.out.println("RpcData{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", pet=" + pet +
                '}');
        return super.toString();
    }

    //这个方法有意思,可以深拷贝一个对象
    public Object deepCopy(Object object) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(object);

        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);

        return oi.readObject();
    }
}
java
package com.glls.creational.prototype;

import java.io.Serializable;

/**
 * @author junyang
 * @date 2024/3/20
 * @desc
 */
public class Pet implements Serializable {
    private String name;
    private String age;
    private String tyep;

    public Pet() {
    }

    public Pet(String name, String age, String tyep) {
        this.name = name;
        this.age = age;
        this.tyep = tyep;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getTyep() {
        return tyep;
    }

    public void setTyep(String tyep) {
        this.tyep = tyep;
    }

    @Override
    public String toString() {
        System.out.println("Pet{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", tyep='" + tyep + '\'' +
                '}');
        return super.toString();
    }
}
java
package com.glls.test.creational.prototype.demo1;

import com.glls.creational.prototype.Pet;
import com.glls.creational.prototype.RpcData;
import org.junit.Test;

import java.io.IOException;

/**
 * @author junyang
 * @date 2024/3/20
 * @desc  原型模式  需要知道 浅拷贝 还是 深拷贝
 */
public class PrototypeTest {
    @Test
    public void test_shallow_copy() throws CloneNotSupportedException {
        System.out.println("==========浅拷贝开始==========");
        RpcData rpcData = new RpcData();
        Pet pet = new Pet("小黑", "1", "dog");
        rpcData.setName("glls");
        rpcData.setAge("18");
        rpcData.setPet(pet);

        RpcData cloneData = (RpcData) rpcData.clone();
        Pet clonePet = cloneData.getPet();

        System.out.println("原型对象和克隆对象是否是同一个对象:" + (rpcData == cloneData));
        System.out.println("原型对象和克隆对象中的宠物是否是同一个对象:" + (pet == clonePet));
        System.out.println("==========浅拷贝结束==========");

    }


    @Test
    public void test_deepCopy_copy() throws IOException, ClassNotFoundException {
        System.out.println("==========深拷贝开始==========");
        RpcData rpcData = new RpcData();
        Pet pet = new Pet("小黑", "1", "dog");
        rpcData.setName("第七人格");
        rpcData.setAge("18");
        rpcData.setPet(pet);

        RpcData cloneData = (RpcData) rpcData.deepCopy(rpcData);
        Pet clonePet = cloneData.getPet();

        System.out.println("原型对象和克隆对象是否是同一个对象:" + (rpcData == cloneData));
        System.out.println("原型对象和克隆对象中的宠物是否是同一个对象:" + (pet == clonePet));
        System.out.println("===========深拷贝结束==========");

    }
}

5、单例模式

shell
确保一个类只有一个实例,并提供全局访问点。
java
package com.glls.creational.singleton;

/**
 * @date 2023/12/7
 * @desc 饿汉式单例模式
 * 优点:在内存中只有一个实例,避免了多线程的同步问题,并且可以保证在任何时候都能获取到实例
 * 缺点:没有lazy loading,当调用getInstance方法时,实例不会被初始化
 * 单例模式的实现方式有两种,一种是懒汉式,一种是饿汉式。
 * 实现要点:
 *
 * ①:项目启动时就创建这个类的实例
 *
 * ②:私有化构造函数,防止被外部实例化
 *
 * ③:对外提供获取实例的静态方法
 */
public class Singleton_01 {
    /**
     * 实现要点①:项目启动时就创建这个类的实例
     */
    private static Singleton_01 INSTANCE = new Singleton_01();

    /**
     * 实现要点②:私有化构造函数,防止被外部实例化
     */
    private Singleton_01() {
    }

    /**
     * 实现要点③:对外提供获取实例的静态方法
     *
     * @return {@link Singleton_01}
     */
    public static Singleton_01 getInstance() {
        return INSTANCE;
    }
}
java
package com.glls.creational.singleton;

import java.io.IOException;

/**
 * @date 2023/12/7
 * @desc
 * 单例模式 - 静态实现
 *  其本质上也是一个饿汉式单例,在类加载的时候,就实例化一个对象交给自己的引用
 *  可以发散想一想Spring中大名鼎鼎的单例池与该实现的差别
 *
 *  饿汉式
 */
public class Singleton_02 {

    private static final Singleton_02 INSTANCE;

    static {
        INSTANCE = new Singleton_02();
    }

    private Singleton_02() {}

    public static Singleton_02 getInstance(){
        return INSTANCE;
    }


    public static void main(String[] args) throws IOException {

        System.in.read();
    }
}
java
package com.glls.creational.singleton;

import java.io.IOException;

/**
 * @date 2023/12/7
 * @desc 懒汉式
 */
public class Singleton_03 {

    /**
     * 实现要点①:项目启动时不创建这个类的实例
     */
    private static Singleton_03 INSTANCE;

    /**
     * 实现要点②:私有化构造函数,防止被外部实例化
     */
    private Singleton_03() {
    }

    /**
     * 实现要点③:对外提供获取实例的静态方法,并且方法上加锁 - synchronized - 保证线程安全
     *  但是 这里  在高并发场景  可能会有空指针
     * @return {@link Singleton_03}
     */
    public static synchronized Singleton_03 getInstance() {
        // 实现点④:在使用时,才延迟加载这个类,这也是懒汉式名字的由来
        return null != INSTANCE ? INSTANCE : new Singleton_03();
    }

    public static void main(String[] args) throws IOException {
        System.in.read();
    }
}
java
package com.glls.creational.singleton;

import java.io.IOException;

/**
 * @date 2023/12/7
 * @desc 单例模式 - 内部类实现
 *
 *
 * 懒汉式    是属于懒汉式的
 */
public class Singleton_04 {

    /**
     * 实现要点①:创建一个静态内部类,该类中有一个静态属性,且为私有的
     *
     *
     */
    private static class SingletonInternalClass {
        private static Singleton_04 INSTANCE = new Singleton_04();
    }

    /**
     * 实现要点②:私有化构造函数,防止被外部实例化
     */
    private Singleton_04() {
    }

    /**
     * 实现要点③:对外提供获取实例的静态方法
     *
     * @return {@link Singleton_03}
     */
    public static Singleton_04 getInstance() {
        return SingletonInternalClass.INSTANCE;
    }


    public static void main(String[] args) throws IOException {
        getInstance();
        System.in.read();
    }
}
java
package com.glls.creational.singleton;

/**
 * @date 2023/12/7
 * @desc 单例模式 - 双重锁校验(DCL)实现    懒汉式
 */
public class Singleton_05 {

    /**
     * 实现要点①:将自己的类作为自己的属性,并且加上volatile关键字
     * <p>
     * 注意:
     * 这里加上volatile关键字的原因是,防止指令重排序,保证单例的唯一性。
     * 因为new这个操作,并不是原子性的,他有3步:
     * 1、分配内存,在jvm堆中分配一段区域
     * 2、初始化对象,在jvm堆中的内存中实例化对象
     * 3、赋值,将对象指向堆中的内存地址
     * 这里如果指令重排序,那么可能2和3的顺序会颠倒,
     * 在多线程下那么就可能出现多个对象,违背了单例模式  ?????
     * 出现多个对象?  还是空指针????
     */
    private static volatile Singleton_05 INSTANCE;

    /**
     * 实现要点②:私有化构造函数,防止被外部实例化
     */
    private Singleton_05() {
    }

    /**
     * 实现要点⑥:对外提供获取实例的静态方法
     *
     * @return {@link Singleton_05}
     */
    public static Singleton_05 getInstance() {
        // 实现要点③:第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
        if (null == INSTANCE) {
            // 实现要点④:锁对象
            synchronized (Singleton_05.class) {
                // 实现要点⑤:第二层检查,防止除了进入的第一个线程的其他线程重复创建对象
                if (null == INSTANCE) {
                    INSTANCE = new Singleton_05();
                }
            }
        }
        return INSTANCE;
    }
}

//枚举方式 创建 单例 是最佳实践

JAVA
//枚举单例 方式1
package com.glls.creational.singleton;

/**
 * @author junyang
 * @date 2024/1/12
 * @desc
 *  枚举 可以作为单例模式的最佳实现
 *  枚举 INSTANCE 会在类加载初始化的时候创建, 类的加载和初始化过程都是线程安全的
 *
 *  枚举可以避免反序列化 破坏单例
 */
public class User {

    private User(){

    }

    //定义一个静态枚举类
    static enum Singleton_06 {
        /**
         * 实现要点①:创建一个单元素的枚举
         */
        INSTANCE;

        private User user;
        /**
         * 实现要带你点③:私有化枚举的构造器,并初始化实例
         */
        private Singleton_06(){
            user = new User();
        }

        public User getInstance(){
            return user;
        }
    }


    //对外暴露一个获取user 的静态方法
    public static User getInstance(){
        return Singleton_06.INSTANCE.getInstance();
    }

}
java
// 枚举单例 方式2
public enum Singleton {
    INSTANCE;

    // 可以添加单例类的其他方法和字段
    public void doSomething() {
        System.out.println("Doing something...");
    }
}

// 使用单例
public class SingletonDemo {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        singleton.doSomething();
    }
}

枚举单例的好处

  • 线程安全: 枚举类型在 Java 中是天然线程安全的,不需要额外的同步措施。
  • 防止反射攻击: 枚举类型不允许通过反射创建实例,从而防止了反射攻击。
  • 防止序列化破坏: 枚举类型在序列化和反序列化过程中保证唯一性,防止了创建新的实例。

结构型

1.代理模式

shell
为其他对象提供一种代理以控制对这个对象的访问。

有静态代理 和 动态代理, 代理对象可以对真实对象进行增强

静态代理,代理类和真实类实现同一个接口, 代理类和真实类是 关联关系, 一般在代理类中, 有一个他们共同接口的属性,会把真实对象 赋值给这个属性,然后 创建真实对象,创建代理对象,将真实对象赋值给代理对象的 共同接口属性,调用代理对象的方法, 在这个方法内 再调用真实对象的 方法。

java
//共同接口
public interface UserService {
    public void add();
}
java
//代理对象类
public class UserServiceImplProxy implements UserService{

    private UserService userService;

    public UserServiceImplProxy() {
    }

    public UserServiceImplProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        System.out.println("开启事务");
        userService.add();
        System.out.println("提交事务");
    }
}
java
//真实对象类
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        // 开启事务
        System.out.println("核心 业务 : 添加用户");
        // 提交事务
    }
}
java
// 模拟  代理对象 增强真实对象   开启 提交事务功能
public class TestTransaction {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceImplProxy userServiceImplProxy = new UserServiceImplProxy(userService);

        userServiceImplProxy.add();
    }

}

动态代理 主要有基于jdk的和基于cglib的

java
//基于jdk的动态代理      真实对象的类  需要有接口
package com.glls.structural.proxy.jdkproxy;

import com.glls.structural.proxy.jdkproxy.demo1.Marry;
import com.glls.structural.proxy.jdkproxy.demo2.House;

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

/**
 * @date 2023/4/21
 * @desc 基于 JDK的动态代理    真实对象 和 代理对象 之间的关系 像兄弟
 * 他们需要实现相同的接口,代理对象 对 真实对象实现的接口中的方法  增强
 *
 */
public class JdkProxy implements InvocationHandler {

    //真实对象   被代理的对象
    private Object target;


    // 根据真实对象 得到代理对象
    public Object getProxy(Object target){
        this.target = target;
        //参数1  类加载器
        //参数2 被代理的 类的接口
        //参数3 InvocationHandler 的对象
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
    }


    //参数1 代理对象实例
    //参数2 反射调用的方法对象
    //参数3 反射调用的方法的参数列表
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("准备工作");

        if(target instanceof House){
            System.out.println("带租户看房");
        }

        //执行 真实对象的核心业务
        Object result = method.invoke(target, args);

        if(target instanceof Marry){
            result = 100;
        }

        System.out.println("收尾工作");
        return result;
    }


    @Test
    public void test(){
        //创建真实对象
        Marry marry = new You();
        //代理类 InvocationHandler
        JdkProxy jdkProxy = new JdkProxy();

        //根据真实对象 得到 代理对象
        Marry proxy = (Marry) jdkProxy.getProxy(marry);

        proxy.marry();


        int money = proxy.money();

        System.out.println(money);
    }




}
java
//基于cglib的动态代理   真实对象的类 不需要有接口
package com.glls.structural.proxy.cglibproxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @date 2023/4/21
 * @desc
 */
public class CglibProxy implements InvocationHandler {

    //真实对象
    private Object target;


    public Object getProxy(Object target){
        //拿到传过来的真实对象
        this.target = target;
        // 字节码增强对象
        Enhancer enhancer = new Enhancer();

        //设置父类   代理对象的父类 是 真实的对象的类
        enhancer.setSuperclass(target.getClass());
        //设置 回调函数
        enhancer.setCallback(this);
        //返回 创建的代理对象
        return enhancer.create();
    }



    @Override
    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {

        System.out.println("准备工作");

        Object result = method.invoke(target, objects);

        System.out.println("收尾工作");

        return result;
    }
    
    
    
        @Test
    public void test(){
        //创建真实对象
        You you = new You();
        //代理类对象
        CglibProxy cglibProxy = new CglibProxy();
        //得到代理对象,  我们清楚代理对象是真实对象的儿子
        You proxy = (You) cglibProxy.getProxy(you);

        //proxy.marry();

        proxy.money();
    }
}

2.适配器模式

shell
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。 适配器模式通过引入适配器类 将原本不兼容的接口转换为符合客户端期望的接口,从而让不同接口的类可以协同工作。
适配器模式基本结构:
1.目标接口     客户端所期望的接口,适配器将适配置者 转为目标接口。
2.适配者类     原本需要适配的类,它定义了不兼容的接口
3.适配器类     将适配者类 转换为 目标接口  使得适配这类可以 与客户端协同工作。

适配器通过实现目标接口,并包装被适配者对象,从而使其能够与客户端交互。 适配器模式 和 代理模式 好像啊 , 代理模式 是 真实对象 和 代理对象 实现相同的接口

场景1 :假设有一个音频播放器,它只能播放 mp3 格式的音频文件,但现在需要支持播放其他格式的音频文件,比如 vlcmp4 格式。这时就可以使用适配器模式来解决这个问题。

java

/**
 * @author junyang
 * @date 2024/6/5
 * @desc
 * 场景:  假设有一个音频播放器,它只能播放 mp3 格式的音频文件,但现在需要支持播放其他格式的音频文件,
 * 比如 vlc 和 mp4 格式。这时就可以使用适配器模式来解决这个问题。
 */


//目标接口
public interface MediaPlayer {
    void play(String audioType, String fileName);
}
java
//适配器类  实现 目标接口 ,同时 注入  被适配的类
package com.glls.structural.adapter.demo3;

/**
 * @author junyang
 * @date 2024/6/5
 * @desc  适配器类:实现目标接口
 */

//适配器 会 关联 适配这类
public class MediaAdapter implements MediaPlayer{


    //适配者, 被适配的类
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType){
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer.playVlc(fileName);
        } else if(audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}
java
package com.glls.structural.adapter.demo3;

/**
 * @author junyang
 * @date 2024/6/5
 * @desc   需要被适配的类
 */

// 适配者接口:高级媒体播放器
public interface  AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}


// 适配者类:Mp4 播放器
public class Mp4Player implements AdvancedMediaPlayer{

    @Override
    public void playVlc(String fileName) {
        // do nothing
    }
    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}


/**
 *
 * // 适配者类:Vlc 播放器
 */
public class VlcPlayer implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }
    @Override
    public void playMp4(String fileName) {
        // do nothing
    }
}
java
//测试
package com.glls.structural.adapter.demo3;

/**
 * @author junyang
 * @date 2024/6/5
 * @desc  其实 这个类 意义不是很大   不要他也可以 , 他是在适配器外面 又套了一层
 */
public class AudioPlayer implements MediaPlayer{
    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing mp3 file. Name: " + fileName);
        } else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}


public class AdapterPatternDemo {
	//客户端 
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
}

场景2:假设有一个旧的英国插头(Adaptee)需要连接到一个新的欧洲插座(Target),但它们的接口不兼容。这时可以使用一个电源适配器(Adapter)来允许连接。

java
//目标接口
interface EuropeanSocket {
    void powerOn();
}
java
//被适配者
class UKPlug {
    public void powerOnUK() {
        System.out.println("UK plug is powered on.");
    }
}
java
//适配器
class UKToEuropeanAdapter implements EuropeanSocket {
    private UKPlug ukPlug;

    public UKToEuropeanAdapter(UKPlug ukPlug) {
        this.ukPlug = ukPlug;
    }

    @Override
    public void powerOn() {
        ukPlug.powerOnUK();
        System.out.println("Adapter converted UK plug to European socket.");
    }
}

// 客户端
public class AdapterPatternExample {
    public static void main(String[] args) {
        UKPlug ukPlug = new UKPlug();
        EuropeanSocket europeanSocket = new UKToEuropeanAdapter(ukPlug);
        
        europeanSocket.powerOn();
    }
}
上述代码中,适配器模式被用来连接一个英国插头(被适配者)到一个欧洲插座(目标接口)。UKPlug 是英国插头类,EuropeanSocket 是欧洲插座接口,UKToEuropeanAdapter 是适配器类,可以让英国插头在欧洲插座中工作。在 main 方法中,创建了一个英国插头对象并通过适配器连接到欧洲插座。

适配器模式使得原本不兼容的类能够协同工作,提高了代码的复用性和灵活性。

3.桥接模式

shell
将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式是一种结构型设计模式,用于将抽象部分与实现部分分离,使它们能够独立变化。桥接模式通过维护抽象类与实现类之间的关联关系,使得它们可以独立地变化而不会相互影响
桥接模式的主要参与者包括:
抽象类(Abstraction): 定义了抽象部分的接口,维护了一个实现类对象的引用。
扩充抽象类(Refined Abstraction): 扩展了抽象类的功能。
实现类接口(Implementor): 定义了实现部分的接口。
具体实现类(Concrete Implementor): 实现了实现类接口的具体类。
java
/**
 * @author junyang
 * @date 2023/12/14
 * @desc  测试桥接模式
 * 需求:
 * 假设我们有一个在线商城,支持多种支付方式(如支付宝、微信支付等)和支付模式(如实时支付、预授权支付等)。
 *
 * 每一种支付方式都有多种支付模式,而每一种支付模式也可以用到多种支付方式上。现在让你设计对接这个支付功能。
 *
 * 分析:
 * 稍微想一想,支付模式和支付方式是两种不同类型的相互组合,所以我们将抽象部分(支付方式)
 * 与它的实现部分(支付模式)分离,将实现部分抽象成单独的类,使它们都可以独立地变化。
 * 可以将支付模式看作支付方式的一个属性,进行桥接。
 *
 *
 *
 */
java
//一般都是 抽象类里面 注入一个接口
package com.glls.structural.bridge.demo1.pay;

import com.glls.structural.bridge.demo1.mode.IPayMode;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc  桥接模式 - 抽象支付方式
 */
public abstract class Pay {

    /**
     * 通过抽象类依赖实现类的⽅式进⾏桥接
     */
    protected IPayMode payMode;

    /**
     * setPayment这个方法只是一种对象的传递方式,当然你也可以使用构造方法传递。
     *
     * @param payMode
     */
    public void setPayMode(IPayMode payMode) {
        this.payMode = payMode;
    }
    public abstract void pay();
}
java
package com.glls.structural.bridge.demo1.pay;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc
 * 桥接模式 - 具体实现子类-支付宝支付方式
 */
public class AliPay extends Pay{
    @Override
    public void pay() {
        payMode.checkAccount();
        System.out.println("调用支付宝支付成功!");
    }
}
java
package com.glls.structural.bridge.demo1.pay;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 桥接模式 - 具体实现子类-微信支付方式
 */
public class WeChatPay extends Pay{

    @Override
    public void pay() {
        payMode.checkAccount();
        System.out.println("调用微信支付成功!");
    }
}
java
//接口
package com.glls.structural.bridge.demo1.mode;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc
 * 桥接模式 - 抽象支付模式
 * 一般支付前会调用风控系统,我们这里简单的校验一下账户就可以了
 */
public interface IPayMode {


    boolean checkAccount();


    String name();
}
java
package com.glls.structural.bridge.demo1.mode;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 桥接模式 - 具体实现子类-预授权支付模式
 */
public class PreAuthMode implements IPayMode{
    @Override
    public boolean checkAccount() {
        System.out.println(this.name() + "-" +"校验账户是否有效");
        return false;
    }

    @Override
    public String name() {
        return "预授权支付模式";
    }
}
java
package com.glls.structural.bridge.demo1.mode;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 桥接模式 - 具体实现子类-实时支付模式
 */
public class RealTimeMode implements IPayMode{

    @Override
    public boolean checkAccount() {
        System.out.println(this.name() + "-" +"校验账户是否有效");
        return false;
    }

    @Override
    public String name() {
        return "实时支付模式";
    }
}
java
package com.glls.structural.bridge.demo1.factory;

import com.glls.structural.bridge.demo1.mode.PreAuthMode;
import com.glls.structural.bridge.demo1.mode.RealTimeMode;
import com.glls.structural.bridge.demo1.pay.AliPay;
import com.glls.structural.bridge.demo1.pay.Pay;
import com.glls.structural.bridge.demo1.pay.WeChatPay;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 工厂模式 - 创建Pay对象
 *
 * 为了方便创建对象,我们可以用到前面学习过的工厂模式
 */
public class PayFactory {

    private PayFactory() {

    }


    public static Pay createPay(String type, String mode) {
        if (type.equalsIgnoreCase("alipay")) {
            AliPay aliPay = new AliPay();
            if (mode.equalsIgnoreCase("pre_auth")) {
                aliPay.setPayMode(new PreAuthMode());
            } else if (mode.equalsIgnoreCase("real_time")) {
                aliPay.setPayMode(new RealTimeMode());
            } else {
                throw new IllegalArgumentException("Invalid payment type: " + type);
            }
            return aliPay;
        } else if (type.equalsIgnoreCase("wechat_pay")) {
            WeChatPay weChatPay = new WeChatPay();
            if (mode.equalsIgnoreCase("pre_auth")) {
                weChatPay.setPayMode(new PreAuthMode());
            } else if (mode.equalsIgnoreCase("real_time")) {
                weChatPay.setPayMode(new RealTimeMode());
            } else {
                throw new IllegalArgumentException("Invalid payment type: " + type);
            }
            return weChatPay;
        } else {
            throw new IllegalArgumentException("Invalid payment type: " + type);
        }
    }
}
java
//测试
package com.glls.test.structural.bridge;

import com.glls.structural.bridge.demo1.factory.PayFactory;
import com.glls.structural.bridge.demo1.pay.Pay;
import org.junit.Test;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc  测试桥接模式
 * 需求:
 * 假设我们有一个在线商城,支持多种支付方式(如支付宝、微信支付等)和支付模式(如实时支付、预授权支付等)。
 *
 * 每一种支付方式都有多种支付模式,而每一种支付模式也可以用到多种支付方式上。现在让你设计对接这个支付功能。
 *
 * 分析:
 * 稍微想一想,支付模式和支付方式是两种不同类型的相互组合,所以我们将抽象部分(支付方式)
 * 与它的实现部分(支付模式)分离,将实现部分抽象成单独的类,使它们都可以独立地变化。
 * 可以将支付模式看作支付方式的一个属性,进行桥接。
 *
 *
 *
 */
public class BridgeTest {

    @Test
    public void testBridge() {
        System.out.println("======alipay,real_time======");
        Pay pay = PayFactory.createPay("alipay", "real_time");
        pay.pay();

        System.out.println("\n======alipay,pre_auth======");
        Pay pay2 = PayFactory.createPay("alipay", "pre_auth");
        pay2.pay();

        System.out.println("\n======wechat_pay,real_time======");
        Pay pay3 = PayFactory.createPay("wechat_pay", "real_time");
        pay3.pay();


        System.out.println("\n======wechat_pay,pre_auth======");
        Pay pay4 = PayFactory.createPay("wechat_pay", "pre_auth");
        pay4.pay();

    }



}

4.装饰器模式

shell
装饰器模式允许动态地向对象添加额外的功能。
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。

案例1 不改变组件结构,增强其功能 show me the code

java
//定义组件接口
// 有个商品  具有添加到购物车的功能
public interface Product {

    /**
     * 添加到购物车
     */
    void addToCart();
}
java
//具体的构建角色 - 需要被装饰(拓展)的类
public class ConcreteProduct implements Product{

    @Override
    public void addToCart() {
        System.out.println("将商品添加到购物车");
    }
}
java
//抽象类装饰器
public abstract class ProductDecorator implements Product {

    /**
     * 抽象接口
     */
    protected Product product;

    public ProductDecorator(Product product) {
        this.product = product;
    }

    @Override
    public void addToCart() {
        product.addToCart();
    }
}
java
//具体装饰子类 - 为商品购买保险
public class InsuranceDecorator extends ProductDecorator {
    public InsuranceDecorator(Product product) {
        super(product);
    }

    @Override
    public void addToCart() {
        // 子类自己的拓展的逻辑
        System.out.println("为商品购买保险");
        // 调用父类的方法
        super.addToCart();
    }
}
java
//具体装饰子类 - 为商品添加礼品包装
public class GiftWrappingDecorator extends ProductDecorator{
    public GiftWrappingDecorator(Product product) {
        super(product);
    }


    @Override
    public void addToCart() {
        // 子类自己的拓展的逻辑
        System.out.println("为商品添加礼品包装");
        // 调用父类的方法
        super.addToCart();
    }
}
java
/**
 * @author junyang
 * @date 2023/12/14
 * @desc
 * 需求背景:
 * 假设我们有一个在线购物网站,其中有一个Product类表示商品。
 * 现在我们需要给商品添加一些额外的功能,比如给商品添加礼品包装、购买保险等。
 * 要求在不必改变原类和使用继承的情况下,动态地扩展这些功能。
 * 分析设计:
 * 为了在不修改原有类的情况下,也可以动态地给商品添加各种附加功能,
 * 我们可以考虑使用继承的方式来进行拓展。但是考虑到直接继承,会因功能的不断扩展,
 * 而导致⼦类膨胀的问题,我们可以再抽象出一层(这里叫做抽象类装饰器),
 * 让具体的子类去实现抽象类装饰器,而不直接继承原有类。
 *
 * 实现要点:
 * 抽象构件⻆⾊(Component) - 定义抽象接⼝
 *
 * 例子中为:Product
 *
 * 具体构件⻆⾊(ConcreteComponent) - 实现抽象接⼝
 *
 * 例子中为:ConcreteProduct
 *
 * 装饰⻆⾊(Decorator) - 定义抽象类并继承接⼝中的⽅法,保证⼀致性
 *
 * 例子中为:ProductDecorator
 *
 * 具体装饰⻆⾊(ConcreteDecorator) - 扩展装饰具体的实现逻辑
 *
 * 例子中为:InsuranceDecorator和GiftWrappingDecorator
 *
 *
 * 总结:
 * 装饰者模式是一种结构型设计模式,它允许你在不修改原始类代码的情况下,动态地给对象添加新的功能。
 */
//测试
public class TestDemo1 {
    public static void main(String[] args) {
        // 原始商品对象
        Product product = new ConcreteProduct();
        // 添加礼品包装的商品对象
        Product giftWrappedProduct = new GiftWrappingDecorator(product);
        // 添加保险的商品对象
        Product insuredProduct = new InsuranceDecorator(giftWrappedProduct);
        // 输出:为商品购买保险 -> 为商品添加礼品包装 -> 将商品添加到购物车
        insuredProduct.addToCart();
    }
}

看到这里,你会觉得 代理模式,适配器模式 和 装饰器模式 好像啊, 代理模式 和 适配器模式长得像, 代理模式和 装饰器模式 功能上有点像,好像都能增强对象功能, 要加以区分

区别:

装饰器模式(Decorator Pattern)和代理模式(Proxy Pattern)是两种常见的设计模式,它们有一些相似之处,但在用途和实现上有一些关键区别:

装饰器模式(Decorator Pattern):

  1. 用途: 装饰器模式允许在不改变原始对象接口的情况下动态地往对象添加新的功能。它通常被用于对对象的功能进行增强或修改,而不需要修改其原始代码。
  2. 关键特点:
    • 装饰器模式通过包装(或者说装饰)原始对象来添加新的行为。
    • 装饰器本身也实现了原始对象的接口,以便对客户端透明。
    • 可以通过嵌套多个装饰器实现对对象功能的多层次扩展。

代理模式(Proxy Pattern):

  1. 用途: 代理模式提供了一种代理对象来控制对另一个对象的访问。代理模式可以用于控制对对象的访问,创建一个远程代理,实现延迟加载等情况。
  2. 关键特点:
    • 代理模式通过引入一个代理对象来代替原始对象,从而控制对原始对象的访问。
    • 代理对象通常负责控制对原始对象的访问(例如权限校验、延迟加载等)。
    • 代理模式在访问对象时增加了一层间接性,客户端通过代理对象访问真实对象。

区别总结:

  • 目的不同: 装饰器模式用于给对象动态添加新的功能,而代理模式用于控制对对象的访问。
  • 结构不同: 装饰器模式通过包装和递归调用实现功能的增强,而代理模式通过引入一个代理对象来控制对对象的访问。
  • 透明性不同: 对于客户端而言,装饰器模式通常是透明的,客户端不需要知道装饰器的存在;而代理模式通常需要客户端显式地使用代理对象。

综上所述,装饰器模式和代理模式在目的、结构和使用方式上有所区别,理解它们的差异可以更好地选择适合场景的设计模式来解决问题。

5.外观模式

shell
外观模式(Facade Pattern)提供一个高层接口,使得子系统更容易使用
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。

主要参与者:

外观类 Facade 提供一个加单的接口给客户端,并封装了整个系统的复杂性。

子系统类:Subsystems 实际执行工作的类,外观类通过调用这些类的方法来完成客户端的请求。

案例1 下面这个案例 使用了 单例模式 + 工厂方法模式 + 外观模式

java
//外观类  
package com.glls.structural.facade.demo1;

/**
 * @author junyang
 * @date 2023/12/21
 * @desc   单例模式
 */
public class PaymentFacade implements PaymentGateway{
    private static volatile PaymentFacade INSTANCE;

    private PaymentFacade() {
    }

    public static PaymentFacade getInstance() {
        if (null == INSTANCE) {
            synchronized (PaymentFacade.class) {
                if (null == INSTANCE) {
                    INSTANCE = new PaymentFacade();
                }
            }
        }
        return INSTANCE;
    }

    @Override
    public void pay(String amount) {
        // 使用工厂获取对象
        PaymentFactory.getGatewayService(amount).pay(amount);
    }


}
java
package com.glls.structural.facade.demo1;

import java.math.BigDecimal;

/**
 * @author junyang
 * @date 2023/12/21
 * @desc 创建支付网关的工厂类    工厂方法模式
 */
public class PaymentFactory {
    private PaymentFactory() {
    }

    public static PaymentGateway getGatewayService(String amount) {
        if (new BigDecimal(amount).compareTo(new BigDecimal("10000")) > 0) {
            return new AliGateway();
        } else {
            return new WeChatGateway();
        }
    }
}
java
package com.glls.structural.facade.demo1;

/**
 * @author junyang
 * @date 2023/12/21
 * @desc
 */
public interface PaymentGateway {
    void pay(String amount);
}

package com.glls.structural.facade.demo1;

/**
 * @author junyang
 * @date 2023/12/21
 * @desc 支付网关 - 支付宝
 */
public class AliGateway implements PaymentGateway{
    @Override
    public void pay(String amount) {
        System.out.println("通过支付宝支付:" + amount);
    }
}


package com.glls.structural.facade.demo1;

/**
 * @author junyang
 * @date 2023/12/21
 * @desc 支付网关 - 微信
 */
public class WeChatGateway implements PaymentGateway{
    @Override
    public void pay(String amount) {
        System.out.println("通过微信支付:" + amount);
    }
}
java
package com.glls.test.structural.facade;

import com.glls.structural.facade.demo1.PaymentFacade;

/**
 * @author junyang
 * @date 2024/1/15
 * @desc
 *
外观模式(Facade Pattern)提供一个高层接口,使得子系统更容易使用
 */
public class TestDemo1 {
    public static void main(String[] args) {
        PaymentFacade.getInstance().pay("100");
        PaymentFacade.getInstance().pay("100000");
    }
}

案例2 外观模式 模拟计算机运行, 假设有一个计算机启动系统,其中包括启动CPU、内存和硬盘。客户端希望通过一个简单的启动系统接口来启动计算机,而不必了解每个子系统的详细启动过程。

java
package com.glls.structural.facade.demo2;


/**
 * @author junyang
 * @date 2024/6/11
 * @desc
 */
public class FacadePatternExample {

    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}




// 外观类
class ComputerFacade {
    private Cpu cpu;
    private Memory memory;
    private HardDrive hardDrive;

    public ComputerFacade() {
        this.cpu = new Cpu();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }

    public void start() {
        cpu.start();
        memory.initialize();
        hardDrive.readData();
        System.out.println("Computer is started and ready to use");
    }
}


// 子系统类
class Cpu {
    public void start() {
        System.out.println("Cpu is starting...");
    }
}

class Memory {
    public void initialize() {
        System.out.println("Memory is initializing...");
    }
}

class HardDrive {
    public void readData() {
        System.out.println("Hard drive is reading data...");
    }
}

6.组合模式

shell
组合模式是一种结构性设计模式,用于将对象组合成树形结构以表示"部分-整体"的层次关系,使得客户端可以统一处理单个对象和组合对象。组合模式让客户端以一致的方式处理个别对象和对象组合。

组合模式的主要参与者包括: 组件(Component): 定义统一的接口,可以代表叶子节点或组合节点。 叶子节点(Leaf): 表示树中的叶子节点,没有子节点。 组合节点(Composite): 表示树中的组合节点,可以包含子节点,并实现与组件接口相关的操作。

组合模式的优势:

  • 统一对待叶子节点和组合节点,简化了客户端代码。
  • 增加新类型的组件相对容易,符合开闭原则。
  • 提供了一个清晰的层次结构,方便管理和操作复杂的对象结构。
java
/**
 * @author junyang
 * @date 2023/12/14
 * @desc 测试组合模式
 * 需求:
 * 我们再将这个需求简单描述一下:
 *
 * 1、我们有2种检验注册的手段 a.手机号 b身份证
 *
 * 2、这两种校验手段在不同的业务场景下,有不同的组合,可以都参与校验,也可以只有一个参与校验。
 *
 * 实现要点:
 * 抽象组件(Component):定义创建对象的接口,允许客户端独立地获取组件对象。
 *
 * 文中示例为:ComponentChecker
 *
 * 叶子节点(Leaf):继承/实现自抽象组件,表示叶子节点对象。
 *
 * 文中示例为:IdCardChecker和PhoneChecker
 *
 * 容器节点(Composite):或者叫组合节点,继承自抽象组件,表示容器节点对象,包含子组件列表。
 *
 * 文中示例为:Composite;子组件列表为:private List children。
 *
 * 总结:
 * 组合模式是一种结构型设计模式,它将对象组织成树形结构,使得客户端可以以统一的方式处理单个对象和组合对象。
 */
java
package com.glls.structural.composite;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 定义组件接口
 */
public interface ComponentChecker {
    /**
     * 校验
     */
    void check();
}
java
//组合节点
package com.glls.structural.composite;

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

/**
 * @author junyang
 * @date 2023/12/14
 * @desc
 */
public class Composite implements ComponentChecker{

    private String name;
    /**
     * 可以存放子节点
     */
    private List<ComponentChecker> children;

    public Composite(String name) {
        this.name = name;
        this.children = new ArrayList<>();
    }

    /**
     * 添加子节点
     *
     * @param child 添加的节点
     */
    public void addChild(ComponentChecker child) {
        children.add(child);
    }


    /**
     * 移除子节点
     *
     * @param child 删除的节点
     */
    public void removeChild(ComponentChecker child) {
        children.remove(child);
    }


    @Override
    public void check() {
        System.out.println("组合节点[" + name + "]准备开始执行\n叶子节点数据为:" + children.toString() + "\n");
        for (ComponentChecker child : children) {
            child.check();
        }
    }

    @Override
    public String toString() {
        return "Composite{" +
                "name='" + name + '\'' +
                ", children=" + children +
                '}';
    }
}
java
//叶子结点
package com.glls.structural.composite;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc  子节点   身份证校验器
 */
public class IdCardChecker implements ComponentChecker{
    private String number;

    public IdCardChecker(String number) {
        this.number = number;
    }
    @Override
    public void check() {
        System.out.println("校验身份证[" + number + "]被执行了...");
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "number='" + number + '\'' +
                '}';
    }
}
java
//叶子结点
package com.glls.structural.composite;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 子节点 - 电话校验器
 */
public class PhoneChecker implements ComponentChecker{

    private String number;

    public PhoneChecker(String number) {
        this.number = number;
    }


    @Override
    public void check() {
        System.out.println("校验电话号码[" + number + "]被执行了...");
    }

    @Override
    public String toString() {
        return "Phone{" +
                "number='" + number + '\'' +
                '}';
    }
}
java
package com.glls.test.structural.composite;

import com.glls.structural.composite.Composite;
import com.glls.structural.composite.IdCardChecker;
import com.glls.structural.composite.PhoneChecker;
import org.junit.Test;

/**
 * @author junyang
 * @date 2023/12/14
 * @desc 测试组合模式
 * 需求:
 * 我们再将这个需求简单描述一下:
 *
 * 1、我们有2种检验注册的手段 a.手机号 b身份证
 *
 * 2、这两种校验手段在不同的业务场景下,有不同的组合,可以都参与校验,也可以只有一个参与校验。
 *
 * 实现要点:
 * 抽象组件(Component):定义创建对象的接口,允许客户端独立地获取组件对象。
 *
 * 文中示例为:ComponentChecker
 *
 * 叶子节点(Leaf):继承/实现自抽象组件,表示叶子节点对象。
 *
 * 文中示例为:IdCardChecker和PhoneChecker
 *
 * 容器节点(Composite): 或者叫组合节点,继承自抽象组件,表示容器节点对象,包含子组件列表。
 *
 * 文中示例为:Composite;子组件列表为:private List children。
 *
 * 总结:
 * 组合模式是一种结构型设计模式,它将对象组织成树形结构,使得客户端可以以统一的方式处理单个对象和组合对象。
 */
public class CompositeTest {

    @Test
    public void testComposite() {
        // 创建根节点,他是一个组合节点
        Composite root = new Composite("根节点");

        // 创建叶子节点1
        IdCardChecker leaf1 = new IdCardChecker("500383*********");
        root.addChild(leaf1);

        // 创建叶子节点2
        PhoneChecker leaf2 = new PhoneChecker("186*********");
        root.addChild(leaf2);

        // 执行操作
        root.check();
    }
}

7.享元模式

java
运用共享技术有效地支持大量细粒度的对象,他比一般的工厂多了一个缓存,当需要一个产品时,先在缓存中查找,如果存在,则直接返回,
如果不存在,则创建一个新的
通过享元模式,程序可以在需要大量相似对象时,以较低的内存消耗来共享细粒度对象,提高系统的性能和效率。
java
// 享元接口
interface Flyweight {
    void operation(String extrinsicState);
}

// 具体享元类
class ConcreteFlyweight implements Flyweight {
    @Override
    public void operation(String extrinsicState) {
        System.out.println("ConcreteFlyweight: " + extrinsicState);
    }
}
java
// 享元工厂类
class FlyweightFactory {
    private static Map<String, Flyweight> flyweights = new HashMap<>();

    public static Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight());
        }
        return flyweights.get(key);
    }
}
java
// 客户端
public class FlyweightPatternExample {
    public static void main(String[] args) {
        Flyweight fw1 = FlyweightFactory.getFlyweight("key1");
        fw1.operation("extrinsic state 1");

        Flyweight fw2 = FlyweightFactory.getFlyweight("key2");
        fw2.operation("extrinsic state 2");

        Flyweight fw3 = FlyweightFactory.getFlyweight("key1");
        fw3.operation("extrinsic state 3"); // 复用了fw1
    }
}

案例2

java
package com.glls.structural.flyweight;

/**
 * @author junyang
 * @date 2023/12/25
 * @desc
 */
public class Product {

    private String id;
    private String name;

    public Product(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
java
package com.glls.structural.flyweight;


import java.util.HashMap;
import java.util.Map;

/**
 * @author junyang
 * @date 2023/12/25
 * @desc
 * 享元工厂
 * 他比一般的工厂多了一个缓存,当需要一个产品时,先在缓存中查找,如果存在,则直接返回,
 * 如果不存在,则创建一个新的
 */
public class ProductFactory {

    private static final Map<String, Product> PRODUCTS = new HashMap<>();
    /**
     * 在这个示例中,我们使用了一个静态的HashMap来存储共享的商品对象。
     * 当需要获取一个商品对象时,首先检查HashMap中是否已经存在该对象,如果不存在,则创建一个新的商品对象并将其添加到HashMap中;
     * 如果已经存在,则直接返回该对象。这样可以避免频繁地创建和销毁商品对象,从而提高查询性能。
     *
     * @param id id
     * @param name name
     * @return {@link Product}
     */
    public static Product createProduct(String id, String name) {
        String key = id + "-" + name;
        if (!PRODUCTS.containsKey(key)) {
            PRODUCTS.put(key, new Product(id, name));
        }
        return PRODUCTS.get(key);
    }
}
java
package com.glls.test.structural.flyweight;

import com.glls.structural.flyweight.Product;
import com.glls.structural.flyweight.ProductFactory;
import org.junit.Test;

/**
 * 享元模式
 */
public class FlyweightTest {
    @Test
    public void test_flyweighty() {
        Product product1 = ProductFactory.createProduct("1", "商品1");
        Product product2 = ProductFactory.createProduct("1", "商品1");
        Product product3 = ProductFactory.createProduct("2", "商品2");

        // 输出 true,表示 product1 和 product2 是同一个对象
        System.out.println(product1 == product2);
        // 输出 false,表示 product1 和 product3 不是同一个对象
        System.out.println(product1 == product3);
    }
}

行为型

1.状态模式

shell
状态模式是一种行为设计模式,它允许一个对象在其内部状态改变时改变其行为。对象看起来似乎修改了它的类。这种模式是一种状态机的实现方式

状态模式的关键参与者包括:

  • 环境(Context): 定义客户感兴趣的接口,维护一个当前状态对象的引用。
  • 状态(State): 定义一个接口,封装与环境的一个特定状态相关的行为。
  • 具体状态(Concrete State): 实现状态接口,每一个具体状态类对应环境的一个具体状态。

状态模式的优势:

  • 将对象的状态封装起来,使得状态之间的转换更加清晰。
  • 减少了大量的条件分支语句,提高了程序的可维护性和扩展性。
  • 符合开闭原则,容易添加新的状态类。

假设我们有一个订单管理系统,订单可以处于不同的状态,比如"待支付"、"已支付"、"已发货"和"已完成"等。针对这种场景,可以使用状态模式来管理订单的状态变化和对应的行为。

实际业务场景分析:

  1. 订单状态管理: 每个订单具有不同的状态,不同状态下订单的行为和属性可能不同。
  2. 状态转换规则: 订单在不同状态下可以执行不同的操作,比如"待支付"状态下可支付订单,"已支付"状态下可发货订单等。
  3. 避免条件语句: 使用状态模式可以避免大量的条件语句来处理不同状态下的操作。

订单状态模式示例:

java
//订单环境类
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc  环境类    定义客户感兴趣的接口,维护一个当前状态对象的引用。
 */
public class Order {

    private State currentState;

    public Order() {
        this.currentState = new PendingPaymentState();
    }

    public void setState(State state) {
        this.currentState = state;
    }

    public void process() {
        currentState.processOrder(this);
    }

}
java
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc  抽象状态类
 */
public interface State {

    void processOrder(Order order);
}
java
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc // 具体状态类:待支付状态
 */
public class PendingPaymentState implements State{

    @Override
    public void processOrder(Order order) {
        System.out.println("Processing order in pending payment state.订单待支付");
        // 可进行支付操作
        order.setState(new PaidState());
    }
}
java
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc // 具体状态类:已支付状态
 */
public class PaidState implements State{
    @Override
    public void processOrder(Order order) {
        System.out.println("Processing order in paid state.订单已支付");
        // 可进行发货操作
        order.setState(new ShippedState());
    }
}
java
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc // 具体状态类:已发货状态
 */
public class ShippedState  implements State{
    @Override
    public void processOrder(Order order) {
        System.out.println("Processing order in shipped state. 订单已发货");
        // 可进行确认收货操作
        order.setState(new DeliveredState());
    }
}
java
package com.glls.behavioral.state;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc // 具体状态类:已完成状态
 */
public class DeliveredState implements State{
    @Override
    public void processOrder(Order order) {
        System.out.println("Processing order in delivered state. 订单已完成");
    }
}
java
//测试
package com.glls.test.behavior.state;

import com.glls.behavioral.state.Order;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc
 */
public class StatePatternExample {
    public static void main(String[] args){
        Order order = new Order();

        order.process(); // 进入待支付状态
        order.process(); // 进入已支付状态
        order.process(); // 进入已发货状态
        order.process(); // 进入已完成状态
    }
}

2.观察者模式

shell
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式简介

观察者模式是一种行为设计模式,它允许一个对象(称为主题)将其状态的变化通知给一系列依赖于它的对象(观察者),使得当主题对象的状态发生变化时,所有观察者都会收到通知并自动更新。

观察者模式的关键参与者包括:

  • 主题(Subject): 维护一组观察者对象,提供方法来添加、删除和通知观察者。
  • 观察者(Observer): 定义一个更新接口,用于在主题状态变化时得到通知。
  • 具体主题(Concrete Subject): 实现主题接口,可以有状态变化,并在状态变化时通知观察者。
  • 具体观察者(Concrete Observer): 实现观察者接口,通过主题的通知来更新自身。

观察者模式的优势:

  • 解耦: 主题和观察者之间的关系解耦,使得彼此的变化不会影响到彼此。
  • 可扩展性: 可以动态添加或移除观察者,使得系统更容易扩展。
  • 通知机制: 主题状态变化时会通知所有观察者,保持观察者和主题状态同步。

观察者模式的实际应用案例:新闻订阅系统

在一个新闻订阅系统中,用户可以订阅不同主题的新闻(比如科技、体育、娱乐等)。每当发布新的新闻时,订阅了该主题的用户都会收到通知。

案例1

java
//主题接口
package com.glls.behavioral.observer.demo1;


/**
 * @author junyang
 * @date 2024/6/11
 * @desc   主题接口     维护一组观察者对象,提供方法来添加、删除和通知观察者。
 */
public interface Subject {

    void register(Observer observer);

    void unregister(Observer observer);

    void notifyObservers();
}
java
package com.glls.behavioral.observer.demo1;

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

/**
 * @author junyang
 * @date 2024/6/11
 * @desc
 */
public class NewsSubject implements Subject{

    private List<Observer> observers = new ArrayList<>();
    private String news;


    @Override
    public void register(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void unregister(Observer observer) {
        observers.remove(observer);
    }


    public void setNews(String news) {
        this.news = news;
        notifyObservers();
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(news);
        }
    }
}
java
//观察者接口
package com.glls.behavioral.observer.demo1;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc 观察者接口
 */
public interface Observer {
    void update(String news);
}
java
package com.glls.behavioral.observer.demo1;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc
 */
public class NewsSubscriber implements Observer{
    private String name;

    public NewsSubscriber(String name) {
        this.name = name;
    }
    @Override
    public void update(String news) {
        System.out.println(name + " received news: " + news);
    }
}
java
package com.glls.test.behavior.observer;

import com.glls.behavioral.observer.demo1.NewsSubject;
import com.glls.behavioral.observer.demo1.NewsSubscriber;

/**
 * @author junyang
 * @date 2024/6/11
 * @desc  测试
 */
public class ObserverPatternExample {

    public static void main(String[] args)
    {
        NewsSubject newsSubject = new NewsSubject();

        NewsSubscriber subscriber1 = new NewsSubscriber("Alice");
        NewsSubscriber subscriber2 = new NewsSubscriber("Bob");

        newsSubject.register(subscriber1);
        newsSubject.register(subscriber2);

        newsSubject.setNews("New technology breakthrough!");

        newsSubject.unregister(subscriber2);

        newsSubject.setNews("Football match updates");
    }

}

在这个示例中,NewsSubject 是具体主题,负责维护订阅者列表并通知订阅者。NewsSubscriber 是具体观察者,当有新新闻时,会接收通知并更新。客户端代码创建了一个新闻主题和两个订阅用户,在新闻发布时,所有订阅了该主题的用户都会收到通知。这个例子展示了观察者模式如何应用于新闻订阅系统中。

3.中介者模式

shell
用一个中介对象来封装一系列对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式概述

中介者模式是一种行为设计模式,旨在减少对象之间的直接通信,而是通过一个中介者对象来协调对象之间的交互。这种模式有助于降低类之间的耦合度,使得各个组件更加独立、易于维护和扩展。

在中介者模式中,对象之间的通信不再直接发生,而是通过中介者对象来进行,中介者负责协调并处理对象之间的消息传递。

中介者模式的实际业务场景

场景描述: 假设有一个简单的飞机订票系统,系统中包括乘客、航空公司和航班信息等对象。当乘客要订票时,需要与航空公司协商,航空公司需要检查航班情况并与乘客确认。

应用案例的实现

在这个场景中,可以使用中介者模式来管理乘客、航空公司和航班信息之间的交互。

4.迭代器模式

shell
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

5.备忘录模式

备忘录模式是一种行为设计模式,它允许对象在不暴露其内部状态的情况下捕获并恢复其状态

假设我们有一个文本编辑器,用户可以输入文本并进行编辑。在某些情况下, 用户可能需要撤销他们的操作或者恢复之前的状态。我们可以使用备忘录模式来实现这个功能。

6.解释器模式

shell
给定一种语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

7.访问者模式

shell
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

8.命令模式

shell
将请求封装成对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

9.责任链模式

shell
它允许你将请求沿着处理链传递,并由处理链中的处理者进行处理。
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止。

假设有一个报销系统,需要根据报销金额的大小来审批,审批人有经理、财务和总经理。不同级别的审批人可以处理不同范围的报销金额,而总经理是最高级别的审批人。

10.模版方法模式

shell
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

11.策略模式

shell
定义一系列算法,把它们一个个封装起来,并且使它们可以互换。本模式使得算法可独立于使用它的客户而变化。