通过Demo案例的形式弄懂Java中的设计模式

发布于:2025-02-11 ⋅ 阅读:(17) ⋅ 点赞:(0)

本文通过Java代码的形式手写设计模式案例,包含多个设计模式,例如常见的责任链模式,单例模式,工厂模式,模板模式,策略模式等,让你能轻松学懂设计模式以及能够手写出来


工厂模式

什么是工厂模式

我们通过工厂来创建我们的对象

我们工厂模式其实分起来也就两种

工厂方法就是一个工厂对应一个商品

抽象工厂就是一个工厂可以对应多种商品

我们把生产商品的具体细节,写到我们的工厂类中,我们使用的时候直接在方法里面new一个工厂使用对象就好了


流程介绍

我们要有产品,而我们的工厂是负责生产处我们的产品的,所以说生产产品的创建我们写到工厂类里面

我们使用工厂类,是为了得到工厂类里面创建的产品对象

工厂模式案例(抽象工厂)

定义产品接口
// 产品接口
interface Product {
    void use();
}

创建具体的产品类

我们有产品A和产品B

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

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

创建工厂类

我们在工厂类里面,创建我们的产品对象

// 工厂类
class ProductFactory {
    public static Product createProduct(String type) {
        if (type.equals("A")) {
            return new ConcreteProductA();
        } else if (type.equals("B")) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("Unknown product type");
        }
    }
}

使用工厂类

使用工厂类,传一个参数我们来得到我们想要的产品对象

public class FactoryPatternExample {
    public static void main(String[] args) {
        // 使用工厂创建产品A
        Product productA = ProductFactory.createProduct("A");
        productA.use();  // 输出: Using Product A

        // 使用工厂创建产品B
        Product productB = ProductFactory.createProduct("B");
        productB.use();  // 输出: Using Product B
    }
}

适配器模式

什么是适配器模式

适配器模式通常用于将一个类的接口转换成客户端期望的另一个接口,使得原本不兼容的类可以一起工作

适配器模式的核心思想是:

  • 通过一个适配器类,将不兼容的接口转换为兼容的接口
  • 客户端代码只需要与目标接口交互,而不需要关心具体的实现细节
  • 这种模式非常适合在集成旧系统或第三方库时使用

ps:不要局限于接口or类,因为适配器模式是一个思想,可以适配接口or类


流程介绍

我们想要弄一个新的接口,但是这个新的接口要适配我们的旧的接口

可以理解成,通过适配器类让新的接口和旧的接口适配,从而实现我们调用新的接口可以使用旧接口里的东西,而不用直接调用旧接口


适配器模式案例

定义目标接口(也就是客户端需要的新接口)

Printer接口是我们要提供给我们客户端使用的接口

// 目标接口
interface Printer {
    void print(String document);
}

需要适配的类

LegactPrinter是我们的旧的类,我们要让他适配新的接口

// 旧的类
class LegacyPrinter {
    public void printDocument(String document) {
        System.out.println("Legacy Printer is printing: " + document);
    }
}

创建适配器类

我们的适配器类连接了我们要提供给客户端的接口Printer

同时也有一个对象,这个对象是我们的需要适配的类

然后我们在实现的接口方法里面,实现调用我们的旧的类的方法

这样子就实现了,我们可以在新的接口调用我们的旧的类的方法

// 适配器类
class PrinterAdapter implements Printer {
    private LegacyPrinter legacyPrinter;

    public PrinterAdapter(LegacyPrinter legacyPrinter) {
        this.legacyPrinter = legacyPrinter;
    }

    @Override
    public void print(String document) {
        // 调用 LegacyPrinter 的方法
        legacyPrinter.printDocument(document);
    }
}

客户端代码(使用新接口)

我们把旧的类对象当道适配器接口实现类里面

这样子就能完成旧的类对象对新接口的适配了

public class AdapterPatternExample {
    public static void main(String[] args) {
        // 创建旧的 LegacyPrinter 对象
        LegacyPrinter legacyPrinter = new LegacyPrinter();

        // 创建适配器,将 LegacyPrinter 适配到 Printer 接口
        Printer printer = new PrinterAdapter(legacyPrinter);

        // 客户端通过 Printer 接口调用
        printer.print("Hello, Adapter Pattern!");
    }
}

观察者模式

什么是观察者模式

关注者可以收到状态变化通知

其实我感觉这个就类似于发布订阅

观察者模式的核心思想是:

  • 被观察者(Subject)维护一个观察者列表,并在状态变化时通知所有观察者。
  • 观察者(Observer)通过实现统一的接口,可以动态地注册或移除。
  • 这种模式非常适合实现事件处理系统、发布-订阅系统等场景

流程介绍

我们有被观察者类和观察者类,当我们的被观察者的XX发横变化时,我们要及时通知给我们的观察者让他们接收到反馈

假设我们有一个天气站,它可以监测温度变化。当温度变化时,需要通知多个显示设备(如手机显示、电视显示等)更新显示内容


观察者模式案例

定义观察者接口

定义观察者接口Observer

有一个update()更新温度方法

// 观察者接口
interface Observer {
    void update(int temperature);
}

定义被观察者接口

定义被观察者接口Subject

有三个方法

注册,移除和通知观察者

// 被观察者接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

被观察者实现类

它连接了Subject接口,也就是被观察者接口,实现了里面的注册,移除和通知观察者方法

维护一个观察者列表List<Observer>

同时它有个自己的方法,设置温度并通知观察者setTemperature,里面调用了我们的通知

在温度变化时通知所有观察者,让观察者更新温度(也就是观察者的update()方法)

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

// 具体的被观察者类
class WeatherStation implements Subject {
    private int temperature;
    private List<Observer> observers = new ArrayList<>();

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

    // 设置温度并通知观察者
    public void setTemperature(int temperature) {
        this.temperature = temperature;
        notifyObservers();
    }
}

观察者实现类

有通知手机更新温度的类PhoneDisplay

通知电视更新温度的类PhoneDisplay

他们都连接Observer观察者接口

// 具体的观察者类:手机显示
class PhoneDisplay implements Observer {
    @Override
    public void update(int temperature) {
        System.out.println("Phone Display: Current temperature is " + temperature + "°C");
    }
}

// 具体的观察者类:电视显示
class TVDisplay implements Observer {
    @Override
    public void update(int temperature) {
        System.out.println("TV Display: Current temperature is " + temperature + "°C");
    }
}

客户端代码

我们注册我们的观察者,并更新温度

public class ObserverPatternExample {
    public static void main(String[] args) {
        // 创建被观察者对象
        WeatherStation weatherStation = new WeatherStation();

        // 创建观察者对象
        Observer phoneDisplay = new PhoneDisplay();
        Observer tvDisplay = new TVDisplay();

        // 注册观察者
        weatherStation.registerObserver(phoneDisplay);
        weatherStation.registerObserver(tvDisplay);

        // 模拟温度变化
        weatherStation.setTemperature(25);  // 通知所有观察者
        weatherStation.setTemperature(30);  // 通知所有观察者

        // 移除一个观察者
        weatherStation.removeObserver(tvDisplay);

        // 再次模拟温度变化
        weatherStation.setTemperature(35);  // 只通知 phoneDisplay
    }
}

代码运行结果

展示了我们的温度的变化,我们温度变化的时候,我们的观察者会及时接收然后做出行为

Phone Display: Current temperature is 25°C
TV Display: Current temperature is 25°C
Phone Display: Current temperature is 30°C
TV Display: Current temperature is 30°C
Phone Display: Current temperature is 35°C

装饰器模式

什么是装饰器模式

对现有的类进行包裹和封装

期望在不改变类对象以及类定义的情况下,为对象添加额外的功能

装饰器模式的核心思想是:

  • 动态扩展对象的功能,而无需修改原始类的代码。
  • 通过组合而非继承来实现功能的扩展。
  • 每个装饰器类都持有一个组件对象的引用,并可以在调用组件方法前后添加额外的行为

ps:其实就类似于动态地为对象添加功能,类似于代理模式实现代码增强


流程介绍

我们可以通过装饰器类,在代码运行中让原本的组件可以实现一些其他功能,动态进行修改

假设我们有一个简单的咖啡店系统,咖啡是基础组件,而调料(如牛奶、糖、巧克力等)是装饰器

我们可以通过装饰器模式动态地为咖啡添加调料,并计算总价格


装饰器模式Demo

定义组件接口
// 组件接口
interface Coffee {
    String getDescription();
    double getCost();
}
定义组件实现类
// 具体组件:基础咖啡
class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple Coffee";
    }

    @Override
    public double getCost() {
        return 2.0;  // 基础咖啡的价格
    }
}
定义装饰器基类

我们连接Coffee组件接口

里面有我们的组件对象Coffee,我们针对他来进行修改

// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

定义装饰器基类实现类

有两个装饰器

一个是牛奶MilkDecorato

一个是糖SugarDecorator

// 具体装饰器:牛奶
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.5;  // 牛奶的价格
    }
}

// 具体装饰器:糖
class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }

    @Override
    public double getCost() {
        return decoratedCoffee.getCost() + 0.2;  // 糖的价格
    }
}

客户端代码
public class DecoratorPatternExample {
    public static void main(String[] args) {
        // 创建基础咖啡
        Coffee coffee = new SimpleCoffee();
        System.out.println(coffee.getDescription() + " - Cost: $" + coffee.getCost());

        // 添加牛奶装饰器
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getDescription() + " - Cost: $" + coffee.getCost());

        // 添加糖装饰器
        coffee = new SugarDecorator(coffee);
        System.out.println(coffee.getDescription() + " - Cost: $" + coffee.getCost());

        // 再添加一次牛奶装饰器
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getDescription() + " - Cost: $" + coffee.getCost());
    }
}

运行结果
Simple Coffee - Cost: $2.0
Simple Coffee, Milk - Cost: $2.5
Simple Coffee, Milk, Sugar - Cost: $2.7
Simple Coffee, Milk, Sugar, Milk - Cost: $3.2

策略模式

什么是策略模式

不影响客户端的情况下发生变化

简单来说,就是我们在一个接口中封装我们针对某些操作的多个方法

例如:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使他们可以相互替换

针对一个业务多个不同的方法进行封装,EasyExcel业务举例子

1.获取具体导出数据

2.获取当前业务类型 getEnum(),我们的enum类里面会有这个类具体的模板编码,模板文件名称,存储路径

3.获取导出类的实体类类型

策略模式的核心思想是:

  • 将算法封装在独立的类中,使得它们可以互相替换。
  • 上下文类持有一个策略对象,并通过策略接口调用具体的算法。
  • 客户端可以动态地选择不同的策略,而不需要修改上下文类的代码

流程介绍

我们把针对某个操作的行为封装到一个接口中,我们使用这个接口中的方法来处理业务中的某些特定逻辑

针对不同的业务有不同的策略

假设我们有一个电商系统,需要根据不同的支付策略(如信用卡支付、支付宝支付、微信支付)来处理订单支付


策略模式Demo

这里简单点,我们的策略接口中只有一个pay()方法,但是我们可以对三个不同的业务使用

ps:一般策略模式都是多个方法的

定义策略接口

我们有一个pay()支付方法

// 策略接口
interface PaymentStrategy {
    void pay(int amount);
}

定义策略实现类

我们有三个不同的实现类

分别针对信用卡支付,支付宝支付和微信支付

// 具体策略:信用卡支付
class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;
    private String expiryDate;

    public CreditCardPayment(String cardNumber, String expiryDate) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Credit Card (Card Number: " + cardNumber + ", Expiry Date: " + expiryDate + ")");
    }
}

// 具体策略:支付宝支付
class AlipayPayment implements PaymentStrategy {
    private String account;

    public AlipayPayment(String account) {
        this.account = account;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Alipay (Account: " + account + ")");
    }
}

// 具体策略:微信支付
class WechatPayment implements PaymentStrategy {
    private String account;

    public WechatPayment(String account) {
        this.account = account;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Wechat Pay (Account: " + account + ")");
    }
}

定义一个上下文类(适配策略实现类)

我们有一个PaymentStrategy接口对象

当我们传不同的对象,例如银行卡,支付宝,微信时

他能自动转变适配过去

这样子我们就能实现一个上下文类就可以在业务逻辑中适配多个业务对象

// 上下文类
class PaymentContext {
    private PaymentStrategy paymentStrategy;

    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void executePayment(int amount) {
        paymentStrategy.pay(amount);
    }
}

客户端代码

一个上下文类就可以在业务逻辑中适配多个业务对象

public class StrategyPatternExample {
    public static void main(String[] args) {
        // 创建支付上下文,使用信用卡支付
        PaymentContext context = new PaymentContext(new CreditCardPayment("1234-5678-9012-3456", "12/25"));
        context.executePayment(100);  // 支付 100 元

        // 切换支付策略,使用支付宝支付
        context = new PaymentContext(new AlipayPayment("alice@alipay.com"));
        context.executePayment(200);  // 支付 200 元

        // 切换支付策略,使用微信支付
        context = new PaymentContext(new WechatPayment("bob@wechat.com"));
        context.executePayment(300);  // 支付 300 元
    }
}

运行结果
Paid 100 via Credit Card (Card Number: 1234-5678-9012-3456, Expiry Date: 12/25)
Paid 200 via Alipay (Account: alice@alipay.com)
Paid 300 via Wechat Pay (Account: bob@wechat.com)

模板模式

什么是模板模式

模板模式是一种行为设计模式,它定义了一个算法的框架,并将一些步骤的实现延迟到子类

模板模式使得子类可以在不改变算法结构的情况下重新定义算法的某些步骤

ps:

其实和策略模式差不多,只不过策略模式是在接口中定义多个方法然后实现类实现

而模板模式是在一个抽象类中一开始定义好一些不变的方法(被final修饰)和一些要由子类改写的抽象方法

模板模式的核心思想是:

  • 定义一个算法的框架,并将一些步骤的实现延迟到子类中。
  • 模板方法通常是 final 的,以防止子类修改算法的结构。
  • 具体步骤可以是抽象的(由子类实现),也可以是具体的(通用步骤)

流程介绍

我们要一开始就定义好我们的方法的调用顺序,我们的调用顺序不能改变,我们的子类只负责改变在调用顺序里使用到的方法

假设我们有一个制作饮料的系统,饮料的制作过程可以分为几个固定的步骤(如烧水、冲泡、倒入杯中、添加调料等),但每种饮料的具体实现可能不同


模板模式Demo

定义抽象类模板

我们定义好了一个一开始的算法模板

也就是我们的方法的调用顺序

// 抽象模板类
abstract class BeverageTemplate {
    // 模板方法:定义算法的框架
    public final void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 具体步骤:烧水(通用步骤)
    private void boilWater() {
        System.out.println("Boiling water...");
    }

    // 抽象步骤:冲泡(由子类实现)
    protected abstract void brew();

    // 具体步骤:倒入杯中(通用步骤)
    private void pourInCup() {
        System.out.println("Pouring into cup...");
    }

    // 抽象步骤:添加调料(由子类实现)
    protected abstract void addCondiments();
}

子类实现类

有一个冲咖啡的实现类Coffee

和一个冲茶的实现类Tea

// 具体子类:咖啡
class Coffee extends BeverageTemplate {
    @Override
    protected void brew() {
        System.out.println("Brewing coffee grounds...");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding sugar and milk...");
    }
}

// 具体子类:茶
class Tea extends BeverageTemplate {
    @Override
    protected void brew() {
        System.out.println("Steeping the tea...");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding lemon...");
    }
}

客户端代码
public class TemplateMethodPatternExample {
    public static void main(String[] args) {
        // 制作咖啡
        System.out.println("Making Coffee:");
        BeverageTemplate coffee = new Coffee();
        coffee.prepareBeverage();

        System.out.println();

        // 制作茶
        System.out.println("Making Tea:");
        BeverageTemplate tea = new Tea();
        tea.prepareBeverage();
    }
}

运行结果
Making Coffee:
Boiling water...
Brewing coffee grounds...
Pouring into cup...
Adding sugar and milk...

Making Tea:
Boiling water...
Steeping the tea...
Pouring into cup...
Adding lemon...

责任链模式

什么是责任链模式

让我们的一个请求,沿着一个链条从前往后进行处理

责任链模式的核心思想是:

  • 将多个处理者连成一条链,并沿着这条链传递请求,直到有对象处理它为止。
  • 每个处理者只处理自己能处理的请求,否则将请求传递给下一个处理者。
  • 责任链模式可以动态地调整链中的处理者顺序或增减处理者

流程介绍

我们弄多个业务处理逻辑,然后装配到一个链式结构中

让我们的请求能够根据这个链从前往后进行处理

假设我们有一个审批系统,员工提交报销申请后,需要经过多个层级的审批(如经理、总监、CEO)。每个审批者只能处理一定金额范围内的请求,如果超出范围,则将请求传递给下一个审批


责任链模式Demo

链式节点接口

我们的责任链的节点是Approver接口

里面有一个processRequest()处理业务逻辑的方法

一个setNextApprover(Approver nextApprover)进入下一个链处理业务逻辑的方法,传递的对象就是我们的接口本身

interface Approver {
    void processRequest(PurchaseRequest request);
    void setNextApprover(Approver nextApprover);
}

链式节点实现类

我们有总监,CEO,经理审批

当我们业务逻辑处理完后,我们就要进入链中的下一个节点进行业务处理

金额小于1000由经理来处理

金额小于5000由总监处理

金额小于10000由CEO处理

// 具体处理者:经理
class Manager implements Approver {
    private Approver nextApprover;

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() <= 1000) {
            System.out.println("Manager approves the request for $" + request.getAmount());
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }

    @Override
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
}

// 具体处理者:总监
class Director implements Approver {
    private Approver nextApprover;

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.println("Director approves the request for $" + request.getAmount());
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }

    @Override
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
}

// 具体处理者:CEO
class CEO implements Approver {
    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() <= 10000) {
            System.out.println("CEO approves the request for $" + request.getAmount());
        } else {
            System.out.println("Request for $" + request.getAmount() + " requires a board meeting!");
        }
    }

    @Override
    public void setNextApprover(Approver nextApprover) {
        // CEO 是责任链的末端,没有下一个处理者
    }
}

定义请求类
// 请求类
class PurchaseRequest {
    private double amount;

    public PurchaseRequest(double amount) {
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

客户端代码

建立节点实现类

然后装配我们的责任链

然后我们的一个请求就能在我们的链中进行处理了

public class ChainOfResponsibilityPatternExample {
    public static void main(String[] args) {
        // 创建处理者
        Approver manager = new Manager();
        Approver director = new Director();
        Approver ceo = new CEO();

        // 设置责任链
        manager.setNextApprover(director);
        director.setNextApprover(ceo);

        // 创建请求
        PurchaseRequest request1 = new PurchaseRequest(800);
        PurchaseRequest request2 = new PurchaseRequest(4500);
        PurchaseRequest request3 = new PurchaseRequest(12000);

        // 处理请求
        manager.processRequest(request1);  // Manager 处理
        manager.processRequest(request2);  // Director 处理
        manager.processRequest(request3);  // CEO 处理
    }
}

单例模式

参考我以前的文章

手写单例模式-CSDN博客


代理模式

参考我以前的文章

通过代理模式理解Java注解的实现原理-CSDN博客