MultiThread/README/设计模式.md

24 KiB
Raw Blame History

设计模式

单例模式

懒汉模式

双重检查锁定DCL

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
        // 构造函数私有化
    }

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类方式

利用 静态内部类(饿汉式)的特性来实现懒汉模式,并且可以保证线程安全。这种方式只有在调用 getInstance() 时才会加载内部类,从而实现懒加载。

public class Singleton {
    private Singleton() {
        // 构造函数私有化
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

适用场景

  • 如果单例对象比较重,且只在程序中某些特定情况下才使用,懒汉模式可以带来性能优势。
  • 在低并发环境或单线程环境下,懒汉模式使用较为简单,不会有多线程安全问题。
  • 对于高并发且需要频繁访问单例的场景,推荐使用静态内部类饿汉模式来避免懒汉模式可能带来的性能问题。

总结

懒汉模式有时为了延迟初始化而牺牲了性能特别是在多线程环境下。为了保证线程安全可以采用双重检查锁定DCL或静态内部类的方式静态内部类方式是最推荐的方式 ,它避免了同步开销,同时也能确保线程安全。

  • 优点
    1. 延迟加载,节省资源。
    2. 内存优化,只有在需要时才实例化。
    3. 减少不必要的初始化开销。
  • 缺点
    1. 传统懒汉模式线程不安全(多个线程可能会创建多个实例)。
    2. 使用同步机制会有性能开销,特别是在多线程环境下。
    3. 代码复杂性较高,容易引入错误。
    4. 不适合高并发场景,特别是频繁访问时会影响性能。

饿汉模式

饿汉模式Eager Initialization是单例模式的一种实现方式在该方式中单例实例在类加载时就已经创建完成而不是在首次使用时才创建。换句话说饿汉模式 的单例实例在类被加载时就会立即初始化,确保了单例模式的唯一性。

public class Singleton {

    // 在类加载时就初始化单例对象
    private static final Singleton INSTANCE = new Singleton();

    // 私有构造函数,防止外部通过 new 创建实例
    private Singleton() {
    }

    // 提供全局访问点
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

适用场景

  • 简单应用:适合没有延迟初始化需求的小型应用,或者单例对象的创建非常轻量,不会影响系统性能的场景。
  • 实例化不耗费太多资源:如果单例对象的初始化过程非常轻量级且无须延迟初始化,可以使用饿汉模式。
  • 类加载顺序固定:如果不在乎单例对象在启动时是否初始化,且单例对象的初始化过程没有严重性能损失,可以选择饿汉模式。

总结

  • 优点:简单、线程安全、性能高(没有锁机制)、实现方便。
  • 缺点:不支持延迟加载、可能会浪费资源(如果单例对象不常用时)。

饿汉模式是单例模式中最简单的实现方式,适用于大多数情况下单例对象的创建开销较小,且不需要延迟初始化的场景。如果单例对象的创建非常轻量且不依赖于后期加载时的资源,饿汉模式是一个不错的选择。

枚举类型实现

枚举类型实现单例模式Enum Singleton是实现单例模式的另一种方式优点是更加简洁、可靠并且避免了许多常见的单例实现方式如懒汉模式和饿汉模式可能出现的问题。使用枚举类型的单例实现方式自 Java 5 引入以来,已经成为了最推荐的单例模式实现方式之一。

当你不需要懒加载或延迟初始化时,使用枚举实现单例模式是最理想的选择。

public enum Singleton {

    // 唯一实例
    INSTANCE;

    // 可以添加其他的方法
    public void someMethod() {
        System.out.println("This is a singleton instance method.");
    }
}

使用示例

public class Main {
    public static void main(String[] args) {
        // 获取单例对象
        Singleton singleton = Singleton.INSTANCE;

        // 调用单例方法
        singleton.someMethod();
    }
}

总结

枚举类型实现单例模式是非常推荐的做法,尤其是在 Java 5 及以后的版本。它简单、线程安全、避免了反射和序列化带来的问题,且保证了单例模式的正确性。对于绝大多数的单例模式实现需求,枚举类型是最安全、最简洁、最优雅的解决方案。

工厂模式

工厂模式Factory Pattern是创建型设计模式的一种它提供了一种创建对象的方式而不需要暴露具体的类实例化过程。工厂模式有三种常见的实现方式

1. 简单工厂模式Simple Factory Pattern

简单工厂模式是工厂模式的一种简单形式,它并不属于设计模式中的结构,它是通过一个工厂类来决定创建哪一个产品类的实例。

  • 特点
    • 由工厂类来决定实例化哪个产品类,不暴露具体的产品创建过程。
    • 客户端不需要知道产品类的具体信息,减少了与产品类的耦合。
  • 优缺点
    • 优点:客户端不需要关心对象的创建细节,降低了代码的耦合度。
    • 缺点:工厂类需要维护一个 ifswitch 来判断创建哪种产品,如果产品种类增加,工厂类会变得非常庞大且难以维护。

示例

class Product {
    void use() {
        System.out.println("Using the product");
    }
}

class ConcreteProductA extends Product {
    @Override
    void use() {
        System.out.println("Using Product A");
    }
}

class ConcreteProductB extends Product {
    @Override
    void use() {
        System.out.println("Using Product B");
    }
}

class SimpleFactory {
    public Product createProduct(String type) {
        if (type.equals("A")) {
            return new ConcreteProductA();
        } else if (type.equals("B")) {
            return new ConcreteProductB();
        } else {
            return null;
        }
    }
}

public class Client {
    public static void main(String[] args) {
        SimpleFactory factory = new SimpleFactory();
        Product product = factory.createProduct("A");
        product.use();
    }
}

2. 工厂方法模式Factory Method Pattern

工厂方法模式是工厂模式的核心。它通过定义一个接口来创建对象,由子类来决定实例化哪一个类。工厂方法模式将简单工厂中的创建产品的逻辑分散到了各个具体的工厂类中。

  • 特点
    • 定义一个创建对象的接口,但让子类来决定具体实例化哪一个产品。
    • 通过继承和多态来解决简单工厂的缺点,避免了在一个类中维护大量的 ifswitch 语句。
  • 优缺点
    • 优点:符合开闭原则,增加新产品时只需要添加新的工厂类,不需要修改现有代码。
    • 缺点:增加了类的数量,系统更为复杂。

示例

interface Product {
    void use();
}

class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using Product A");
    }
}

class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using Product B");
    }
}

abstract class Creator {
    public abstract Product factoryMethod();
}

class ConcreteCreatorA extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

class ConcreteCreatorB extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}

public class Client {
    public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.factoryMethod();
        productA.use();

        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.factoryMethod();
        productB.use();
    }
}

3. 抽象工厂模式Abstract Factory Pattern

抽象工厂模式是工厂模式的进一步扩展,它提供了一个接口,用于创建一组相关或相互依赖的对象,而无需指定具体的类。与工厂方法模式不同,抽象工厂模式更侧重于一组产品的创建,而不是单个产品的创建。

  • 特点
    • 提供一个接口,用来创建相关或依赖的多个产品。
    • 客户端通过抽象工厂来获取一系列的产品对象,而无需知道具体的实现。
  • 优缺点
    • 优点:可以方便地更换产品系列(产品的族),符合开闭原则。
    • 缺点:增加了系统的复杂性,如果新增产品族或产品种类时需要修改现有的代码。

示例

interface ProductA {
    void useA();
}

interface ProductB {
    void useB();
}

class ConcreteProductA1 implements ProductA {
    @Override
    public void useA() {
        System.out.println("Using Product A1");
    }
}

class ConcreteProductB1 implements ProductB {
    @Override
    public void useB() {
        System.out.println("Using Product B1");
    }
}

class ConcreteProductA2 implements ProductA {
    @Override
    public void useA() {
        System.out.println("Using Product A2");
    }
}

class ConcreteProductB2 implements ProductB {
    @Override
    public void useB() {
        System.out.println("Using Product B2");
    }
}

interface AbstractFactory {
    ProductA createProductA();

    ProductB createProductB();
}

class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

class ConcreteFactory2 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        productA1.useA();
        productB1.useB();

        AbstractFactory factory2 = new ConcreteFactory2();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        productA2.useA();
        productB2.useB();
    }
}

总结:

  • 简单工厂模式:由工厂类根据不同的条件决定创建不同的对象,适合产品种类少且变化不大时。
  • 工厂方法模式:通过子类来决定产品的创建,适合产品种类较多时,且产品可能会变化。
  • 抽象工厂模式:提供多个相关的产品的创建接口,适合需要创建一系列相关产品的场景。

策略模式

image-20250131223259043

image-20250131223315954

我们通过策略模式来实现鸭子的飞行和叫声行为,使得它们可以在运行时灵活替换。

飞行行为

飞行接口

public interface FlyBehavior {
    /**
     * 飞的样子
     */
    void fly();
}

飞行实现

public class FlyNoWay implements FlyBehavior {
    /**
     * 飞的样子
     */
    @Override
    public void fly() {
        System.out.println("不能飞。。。");
    }
}

public class FlyWithWings implements FlyBehavior {
    /**
     * 飞的样子
     */
    @Override
    public void fly() {
        System.out.println("可以飞。。。");
    }
}

叫的行为

叫的接口

public interface QuackBehavior {
    /**
     * 怎么叫的
     */
    void quack();
}

叫的实现

public class MuteQuack implements QuackBehavior {
    /**
     * 怎么叫的
     */
    @Override
    public void quack() {
        System.out.println("不会叫...");
    }
}

public class Quack implements QuackBehavior {
    /**
     * 怎么叫的
     */
    @Override
    public void quack() {
        System.out.println("鸭子叫。。。");
    }
}

鸭子

@Setter
public abstract class AbstractDuck {
    // 设置新的叫声行为
    QuackBehavior quackBehavior;
    // 设置新的飞行行为
    FlyBehavior flyBehavior;

    public AbstractDuck() {
    }

    /**
     * 展示什么类型的鸭子
     */
    public abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("鸭子游泳");
    }
}

鸭子继承

public class ModelDuck extends AbstractDuck {

    private final String modelDuck = "橡皮🦆";

    public ModelDuck() {
        super.setFlyBehavior(new FlyNoWay());
        super.quackBehavior = new MuteQuack();
    }

    /**
     * 展示什么类型的鸭子
     */
    @Override
    public void display() {
        System.out.println(modelDuck + "...");
    }

    /**
     *
     */
    @Override
    public void swim() {
        System.out.println(modelDuck + "不会游泳。。。");
    }
}

public class WoodDuck extends AbstractDuck {

    private final String modelDuck = "木头🦆";

    public WoodDuck() {
        super.flyBehavior = new FlyNoWay();
        super.quackBehavior = new MuteQuack();
    }

    /**
     * 展示什么类型的鸭子
     */
    @Override
    public void display() {
        System.out.println(modelDuck + "...");
    }
}

运行

public class Main {
    public static void main(String[] args) {
        ModelDuck modelDuck = new ModelDuck();
        modelDuck.display();
        modelDuck.performFly();
        modelDuck.performQuack();
        modelDuck.swim();

        System.out.println("--------------------------------");

        AbstractDuck woodDuck = new WoodDuck();
        woodDuck.display();
        woodDuck.performFly();
        woodDuck.performQuack();
        woodDuck.swim();
    }
}

image-20250131224206984

观察者模式

观察者模式是一种行为设计模式,它允许一个对象(被观察者)在其状态发生变化时,自动通知所有依赖于它的对象(观察者),而不需要知道这些观察者的具体细节。其主要思想是实现“低耦合”,即对象之间的关系通过接口进行松散连接。

image-20250131222844877

image-20250131222858501

为了避免使用过时的类,我们可以手动实现观察者模式。我们需要定义一个“观察者”接口和一个“被观察者”接口,并在被观察者中维护一个观察者的列表,通知观察者更新状态。

观察者

观察者接口

/**
 * 观察者接口
 */
public interface Observer {

    /**
     * 更新数据
     *
     * @param temperature 温度
     * @param humidity    湿度
     * @param pressure    气压
     */
    void update(float temperature, float humidity, float pressure);
}

观察者的实现

public class Observer1 implements Observer {

    /**
     * 更新数据
     *
     * @param temperature 温度
     * @param humidity    湿度
     * @param pressure    气压
     */
    @Override
    public void update(float temperature, float humidity, float pressure) {
        System.out.println("Observer1 显示数据temperature = " + temperature + ";humidity = " + humidity);
    }
}

public class Observer2 implements Observer {

    /**
     * 更新数据
     *
     * @param temperature 温度
     * @param humidity    湿度
     * @param pressure    气压
     */
    @Override
    public void update(float temperature, float humidity, float pressure) {
        System.out.println("Observer2 显示数据temperature = " + temperature + ";humidity = " + humidity);

    }
}

public class Observer3 implements Observer {

    /**
     * 更新数据
     *
     * @param temperature 温度
     * @param humidity    湿度
     * @param pressure    气压
     */
    @Override
    public void update(float temperature, float humidity, float pressure) {
        System.out.println("Observer3 显示数据temperature = " + temperature + ";humidity = " + humidity);
    }
}

主题

主体接口

/**
 * 被观察者接口
 */
public interface Subject {
    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers();
}

主体实现

import cn.bunny.pattern3.observer.Observer;

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

/**
 * 被观察者类
 */
public class WeatherStationSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private float temperature;
    private float humidity;
    private float pressure;

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

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }

    public void setTemperature(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();
    }
}

运行

import cn.bunny.pattern3.observer.Observer;
import cn.bunny.pattern3.observer.Observer1;
import cn.bunny.pattern3.observer.Observer2;
import cn.bunny.pattern3.observer.Observer3;
import cn.bunny.pattern3.subject.WeatherStationSubject;

public class Main {
    public static void main(String[] args) {
        WeatherStationSubject weatherStationSubject = new WeatherStationSubject();
        Observer observer1 = new Observer1();
        Observer observer2 = new Observer2();
        Observer observer3 = new Observer3();

        // 注册观察者
        weatherStationSubject.registerObserver(observer1);
        weatherStationSubject.registerObserver(observer2);
        weatherStationSubject.registerObserver(observer3);

        // 更新天气温度,观察者会收到通知
        weatherStationSubject.setTemperature(25.0f, 25.0f, 25.0f);
        weatherStationSubject.setTemperature(30.0f, 30.0f, 30.0f);

        // 移出观察者1
        weatherStationSubject.removeObserver(observer1);

        // 更新天气温度,观察者会收到通知
        System.out.println("--------------------------");
        weatherStationSubject.setTemperature(44.0f, 44.0f, 44.0f);
    }
}

image-20250131223737965

装饰者模式

装饰器模式是一种结构型设计模式,允许在不修改对象代码的前提下,动态地给一个对象添加额外的功能。它通过“包装”原始对象,使用一个或多个装饰器类对其功能进行增强或修改。

简单来说,装饰器模式通过“装饰”原有对象来扩展功能,而不是通过继承来修改对象的行为。

汽车案例

假设你有一辆普通的车(基本功能),如果你想让这辆车变成运动型车或豪华车(添加新功能),你可以将这辆车包装在不同的装饰器中。例如:

  • 基本车:一辆普通的车。
  • 运动型车:在基本车上增加运动功能,比如加速性能、运动座椅等。
  • 豪华车:在基本车上增加奢华功能,如高档内饰、音响系统等。

通过使用装饰器模式,你可以自由组合这些功能(运动型 + 豪华型),而不需要修改原有的 Car 类。

image-20250201105537485

相机案例

image-20250201105612849

装饰器模式的结构

  1. Component组件接口:定义了被装饰对象的共同行为,通常是一个接口或抽象类。
  2. ConcreteComponent具体组件:实现了 Component 接口,表示被装饰的对象。
  3. Decorator装饰器:实现 Component 接口,并持有一个 Component 对象,目的是对其进行包装。
  4. ConcreteDecorator具体装饰器:扩展了 Decorator 类,为 Component 添加额外的功能。

优点

  1. 灵活性:装饰器模式可以通过动态地组合多个装饰器来实现功能的拓展,而不需要修改现有类或创建大量的子类。
  2. 遵循开放-封闭原则:类对扩展开放,对修改封闭。你可以通过装饰器添加新功能,而无需修改现有的类代码。
  3. 功能复用:装饰器模式支持组合多个装饰器,通过不同的装饰器组合可以重用已有的功能。
  4. 避免类膨胀:与继承不同,装饰器模式避免了大量的子类创建,能够更灵活地对对象功能进行增强。

缺点

  1. 增加复杂性:由于需要创建多个装饰器类,系统的结构可能变得更加复杂,维护和理解装饰器链条可能变得较为困难。
  2. 多层装饰会导致性能开销:装饰器模式通常会创建多个小对象进行嵌套包装,导致性能开销增加,尤其在装饰器链条很长时。
  3. 难以管理装饰器的组合:如果装饰器有依赖关系或需要特定的顺序才能正确工作,那么组合多个装饰器可能会变得更加困难,增加了使用的难度。

相机

相机接口

public interface Camera {

    /**
     * 操作
     */
    void operation();
}

相机实现

public class TakePhotoCamera implements Camera {
    /**
     * 操作
     */
    @Override
    public void operation() {
        System.out.println("拍照。。。");
    }
}

public abstract class CameraDecorator implements Camera {

    final Camera camera;

    public CameraDecorator(Camera camera) {
        this.camera = camera;
    }
}

public class FilterCameraDecorator extends CameraDecorator {
    public FilterCameraDecorator(Camera camera) {
        super(camera);
    }

    /**
     * 操作
     */
    @Override
    public void operation() {
        camera.operation();
        System.out.println("滤镜功能。。。");
    }
}

运行

public class Main {
    public static void main(String[] args) {
        System.out.println("------------------执行简单的拍照-------------------");
        
        TakePhotoCamera takePhotoCamera = new TakePhotoCamera();
        takePhotoCamera.operation();

        System.out.println("-------------------需要美颜功能------------------");
        
        BeautyCameraDecorator beautyCameraDecorator = new BeautyCameraDecorator(takePhotoCamera);
        beautyCameraDecorator.operation();

        System.out.println("-------------------需要美颜功能之后滤镜------------------");
        
        FilterCameraDecorator filterCameraDecorator = new FilterCameraDecorator(beautyCameraDecorator);
        filterCameraDecorator.operation();
    }
}

image-20250201135138918