设计模式以及其最佳的实践

工厂模式

工厂模式通常包括以下几种变体:

  1. 简单工厂模式(Simple Factory Pattern): 简单工厂模式通过一个工厂类来负责对象的创建,客户端通过调用工厂类的静态方法或者实例方法来获取对象。简单工厂模式将对象的创建过程封装在一个工厂类中,对客户端隐藏了对象的创建细节。

  2. 工厂方法模式(Factory Method Pattern): 工厂方法模式通过定义一个抽象的工厂接口和多个具体的工厂类来创建对象。每个具体的工厂类负责创建特定类型的对象,客户端可以根据需要选择不同的工厂类来创建对象。工厂方法模式允许客户端在不修改现有代码的情况下添加新的产品类。

  3. 抽象工厂模式(Abstract Factory Pattern): 抽象工厂模式通过定义一个抽象工厂接口和多个具体的工厂类来创建一系列相关或依赖的对象。每个具体的工厂类负责创建一组相关的产品对象,客户端可以通过选择不同的具体工厂类来获取不同系列的产品对象。抽象工厂模式强调了产品族的概念,适用于需要创建一组相关对象的场景。

1. 简单工厂模式

简单工厂模式通过一个工厂类来负责对象的创建。把创建车的操作细节都放到了工厂里,而客户直接使用工厂的创建方法,传入想要的车型号就行了,而不必去知道创建的细节,其实就是对创建对象的过程进行解耦,屏蔽细节。

下面是一个示例,工厂类创建不同类型的产品(如Car):

// 产品接口
interface Car {
    void drive();
}

// 具体产品类
class BMW implements Car {
    public void drive() {
        System.out.println("驾驶BMW");
    }
}

class SUV implements Car {
    public void drive() {
        System.out.println("驾驶SUV");
    }
}

// 简单工厂类
class CarFactory {
    public static Car createCar(String type) {
        switch (type) {
            case "BMW":
                return new BMW();
            case "SUV":
                return new SUV();
            default:
                throw new IllegalArgumentException("未知的车型");
        }
    }
}

// 客户端代码
public class SimpleFactoryDemo {
    public static void main(String[] args) {
        Car bmw = CarFactory.createCar("BMW");
        bmw.drive();
        
        Car suv = CarFactory.createCar("SUV");
        suv.drive();
    }
}

2. 工厂方法模式

工厂方法模式通过定义一个抽象的工厂接口和多个具体的工厂类来创建对象。每个具体的工厂类负责创建特定类型的对象。

每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。

// 产品接口
interface Car {
    void drive();
}

// 具体产品类
class BMW implements Car {
    public void drive() {
        System.out.println("驾驶BMW ");
    }
}

class SUV implements Car {
    public void drive() {
        System.out.println("驾驶SUV");
    }
}

// 抽象工厂接口,工厂方法模式将工厂抽象化,并定义一个创建对象的接口。
interface CarFactory {
    Car createCar();
}

// 具体工厂类
class BMWFactory implements CarFactory {
    public Car createCar() {
        return new BMW();
    }
}

class SUVFactory implements CarFactory {
    public Car createCar() {
        return new SUV();
    }
}

// 客户端代码
public class FactoryMethodDemo {
    public static void main(String[] args) {
        BMWFactory bmwFactory = new BMWFactory();
        Car bmw = bmwFactory.createCar();
        bmw.drive();
        
        CarFactory suvFactory = new SUVFactory();
        Car suv = suvFactory.createCar();
        suv.drive();
    }
}

3. 抽象工厂模式

通过抽象工厂模式,我们可以实现以下的效果:比如户外的时候生产SUV和山地自行车,城市中的时候生产BMW和公路自行车。

每个具体的工厂类负责创建一组相关的产品对象,客户端可以通过选择不同的具体工厂类来获取不同系列的产品对象。抽象工厂模式强调了产品族的概念,适用于需要创建一组相关对象的场景。

// 抽象产品A
interface Car {
    void drive();
}

// 具体产品A1
class BMW implements Car {
    public void drive() {
        System.out.println("驾驶BMW ");
    }
}

// 具体产品A2
class SUV implements Car {
    public void drive() {
        System.out.println("Driving an SUV");
    }
}

// 抽象产品B自行车
interface Bike {
    void ride();
}

// 具体产品B1
class RoadBike implements Bike {
    public void ride() {
        System.out.println("骑公路车");
    }
}

// 具体产品B2
class MountainBike implements Bike {
    public void ride() {
        System.out.println("骑山地车");
    }
}

// 抽象工厂接口
interface VehicleFactory {
    Car createCar();
    Bike createBike();
}

// 具体工厂类1 城市交通工具工厂
class UrbanVehicleFactory implements VehicleFactory {
    public Car createCar() {
        return new BMW();
    }
    public Bike createBike() {
        return new RoadBike();
    }
}

// 具体工厂类2 野外交通工具工厂
class OutdoorVehicleFactory implements VehicleFactory {
    public Car createCar() {
        return new SUV();
    }
    public Bike createBike() {
        return new MountainBike();
    }
}

// 客户端代码
public class AbstractFactoryDemo {
    public static void main(String[] args) {
        VehicleFactory urbanFactory = new UrbanVehicleFactory();
        Car bmw = urbanFactory.createCar();
        Bike roadBike = urbanFactory.createBike();
        bmw.drive();
        roadBike.ride();
        
        VehicleFactory outdoorFactory = new OutdoorVehicleFactory();
        Car suv = outdoorFactory.createCar();
        Bike mountainBike = outdoorFactory.createBike();
        suv.drive();
        mountainBike.ride();
    }
}

策略模式

策略模式是一种行为设计模式,用于定义一组算法并将其封装起来,使它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端。

在策略模式中,通常有三个角色:

  • 上下文(Context): 上下文是客户端代码所调用的对象,持有一个策略类的引用,最终给客户端调用。

  • 策略(Strategy): 策略是一个接口或者抽象类,定义了一个算法族的公共接口。具体的策略类实现了这个接口,每个策略类封装了一种具体的算法。

  • 具体策略(Concrete Strategy): 具体策略是策略的具体实现类,实现了策略接口定义的算法。具体策略类之间可以相互替换,客户端可以在运行时选择不同的具体策略来实现不同的算法。

在这里插入图片描述
算法的独立性: 策略模式将算法的实现从上下文中分离出来,使得算法可以独立于客户端而变化。这样一来,客户端可以根据需要选择不同的策略,而不需要修改原有的代码。

易于扩展: 新的策略可以很容易地被添加到系统中,无需修改现有的代码,只需实现相应的策略接口即可。

减少条件判断: 策略模式可以避免大量的条件判断语句,使得代码更加清晰和易于理解。

封装性良好: 策略模式将算法封装在独立的策略类中,使得每个策略类都可以独立地进行单元测试和维护。

最佳实践:

下面是一个实例,根据音乐文件的后缀名不同,选择不同的策略来下载音乐文件中的封面图片。

封面下载接口如下:

	/**
     * 封面下载
     */
    @ResponseBody
    @GetMapping("/cover/{filename}")
    public void cover(@PathVariable String filename, HttpServletResponse response){
        // 获取文件扩展名
        String fileExtension = filename.substring(filename.lastIndexOf(".") + 1);
        // 获取封面下载策略
        CoverStrategy coverStrategy = CoverStrategyFactory.getCoverStrategy(fileExtension);
        // 调用策略
        coverStrategy.download(UPLOAD_DIR + filename,response);
    }

CoverStrategy 策略接口:

/**
 * 策略接口,定义抽象方法dowload。
 * 实现该接口的类需要实现下载文件的方法。
 */
public interface CoverStrategy {

    void download(String filepath, HttpServletResponse response) ;
}

CoverStrategyFactory 封面策略工厂类:

/**
 * 封面策略工厂类,用于根据文件扩展名返回相应的策略实现。
 */
public class CoverStrategyFactory {
	// 存储文件扩展名与对应的策略实现的映射关系
    private static Map<String, CoverStrategy> coverStrategyMap = new ConcurrentHashMap<>();
	//根据后缀名返回对应的策略类
    public static CoverStrategy getCoverStrategy(String fileExtension) {
    	//如果没有对应的策略 返回默认策略
        if (!coverStrategyMap.containsKey(fileExtension)) {
            return new BaseCoverStrategy();
        }
        //从map中选择对应策略
        return coverStrategyMap.get(fileExtension);
    }

	/**
     * 注册新的策略实现到工厂中。
     * 通过文件扩展名作为键,将对应的策略实现存储到map中。
     *
     * @param fileExtension 文件扩展名,如 "jpg", "png", "txt" 等。
     * @param coverStrategy 对应文件扩展名的策略实现。
     */
    public static void register(String fileExtension,CoverStrategy coverStrategy) {
        coverStrategyMap.put(fileExtension, coverStrategy);
    }
}

BaseCoverStrategy 基本封面策略类:实现了 CoverStrategy 接口并实现了下载方法,该类还实现了 InitializingBean 接口,在 Spring 初始化完成后把自身注册为 mp3flac 文件扩展名对应的策略。


/**
 * 基本的音频封面图片获取策略类
 */
@Service
public class BaseCoverStrategy implements CoverStrategy, InitializingBean {

    @Override
    public void download(String filepath, HttpServletResponse response)  {
        //封面的下载逻辑
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        CoverStrategyFactory.register("mp3", this);
        CoverStrategyFactory.register("flac", this);
    }
}

OggCoverStrategy ogg封面策略类:另一个实现了 CoverStrategy 接口的具体策略类,同样的在 Spring 初始化完成后把自身注册为 ogg 文件扩展名对应的策略。


/**
 * ogg格式的音频封面图片获取策略类
 */
@Service
public class OggCoverStrategy implements CoverStrategy, InitializingBean {

    @Override
    public void download(String filepath, HttpServletResponse response) {
         //封面的下载逻辑
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        CoverStrategyFactory.register("ogg", this);
    }
}

责任链设计模式

责任链模式在实际应用中常见的场景包括审批流程、日志记录、权限验证等。例如,一个用户提交了一个审批请求,可能需要经过多个审批人依次审批,每个审批人根据自身的权限和责任决定是否处理请求,直到找到能够处理请求的审批人为止。再比如我们有一个在线购物系统,用户下单后需要依次进行以下处理:库存检查、价格计算、优惠券验证、订单生成等。每个处理步骤都由不同的处理器来完成,而且处理器之间存在一定的逻辑关系。每个处理器负责处理一项具体的任务,并且决定是否将请求传递给下一个处理器。

在责任链模式中,通常有以下几个角色:

抽象处理者(Handler): 定义了一个处理请求的接口,通常包含一个处理方法来处理请求,以及一个设置后继处理者的方法,用于构建责任链。

具体处理者(Concrete Handler): 实现了抽象处理者定义的处理方法,并根据自身能力决定是否处理请求。如果自身无法处理请求,则将请求传递给下一个处理者。

客户端(Client): 创建请求对象并将其发送给责任链的第一个处理者。它不关心处理细节和请求的传递过程,只需要知道如何创建请求对象和将请求发送给责任链即可。

案例:日志打印器

假设我们有一个日志记录器系统,它可以根据日志级别(如 INFO、DEBUG、ERROR)进行不同的处理。我们将使用责任链模式来实现这个系统。

1.定义日志级别
首先,定义不同的日志级别:

public class LogLevel {
    public static final int INFO = 1;
    public static final int DEBUG = 2;
    public static final int ERROR = 3;
}

2. 创建抽象日志记录器类

创建一个抽象类 Logger,它包含一个指向下一个处理器的引用以及一个处理日志请求的抽象方法:

public abstract class Logger {
    // 日志级别
    protected int level;

    // 责任链中的下一个处理器
    protected Logger nextLogger;

    // 设置责任链中的下一个处理器
    public void setNextLogger(Logger nextLogger) {
        this.nextLogger = nextLogger;
    }

    // 处理日志消息
    public void logMessage(int level, String message) {
        // 如果当前处理器的级别小于或等于消息的级别,则处理该消息
        if (this.level <= level) {
            write(message);
        }
        // 否则,传递给责任链中的下一个处理器
        else if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }

    // 抽象方法,用于具体处理日志消息,由子类实现
    protected abstract void write(String message);
}


3. 创建具体的日志记录器类
实现具体的日志记录器类,它们分别处理不同级别的日志消息:

public class InfoLogger extends Logger {
    public InfoLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("INFO: " + message);
    }
}

public class DebugLogger extends Logger {
    public DebugLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("DEBUG: " + message);
    }
}

public class ErrorLogger extends Logger {
    public ErrorLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("ERROR: " + message);
    }
}

4. 创建责任链

public class LoggerChain {
    public static Logger getChainOfLoggers() {
        Logger errorLogger = new ErrorLogger(LogLevel.ERROR);
        Logger debugLogger = new DebugLogger(LogLevel.DEBUG);
        Logger infoLogger = new InfoLogger(LogLevel.INFO);

        infoLogger.setNextLogger(debugLogger);
        debugLogger.setNextLogger(errorLogger);

        return infoLogger;
    }
}

5.调用责任链

public class LoggerChain {
    // 静态方法,创建并返回日志处理器的责任链
    public static Logger getChainOfLoggers() {
        // 创建不同级别的日志处理器
        Logger errorLogger = new ErrorLogger(LogLevel.ERROR);  // 错误级别的日志处理器
        Logger debugLogger = new DebugLogger(LogLevel.DEBUG);  // 调试级别的日志处理器
        Logger infoLogger = new InfoLogger(LogLevel.INFO);     // 信息级别的日志处理器

        // 设置责任链:infoLogger -> debugLogger -> errorLogger
        infoLogger.setNextLogger(debugLogger);
        debugLogger.setNextLogger(errorLogger);

        // 返回责任链的第一个处理器
        return infoLogger;
    }
}

输出

INFO: This is an information.
DEBUG: This is a debug level information.
ERROR: This is an error information.

模板设计方法

模板设计模式(Template Method Pattern)是一种行为型设计模式,它定义了一个操作的算法骨架(模板),并允许子类在不改变算法结构的前提下对特定步骤进行实现或修改。这种模式在父类中定义了一个或多个抽象方法,子类通过重写这些方法来定制操作。

abstract class AbstractClass {
    // 模板方法
    public void templateMethod() {
        step1();
        step2();
        step3();
    }

    // 具体步骤
    private void step1() {
        System.out.println("具体步骤1");
    }

    // 抽象步骤,留给子类实现
    abstract void step2();

    // 具体步骤
    private void step3() {
        System.out.println("具体步骤3");
    }
}

class ConcreteClass extends AbstractClass {
    // 实现抽象步骤
    @Override
    void step2() {
        System.out.println("具体步骤2");
    }
}
public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        AbstractClass instance = new ConcreteClass();
        instance.templateMethod();  // 执行模板方法
    }
}

适配器模式

适配器模式有两种:对象适配器模式类适配器模式

  • 对象适配器模式: 基本思路与类适配器模式相同, 只是将 Adapter适配器类修改, 不是继承 Adaptee被适配者, 而是拥有 Adaptee类的实例, 以解决兼容性的问题。
  • 类适配器模式: Adapter适配器类, 继承 Adaptee被适配器类, 实现 Target目标类的接口, 完成适配。

以下举一个类适配器的例子:在生活中手机充电器, 需要将家用220V的交流电 转换为 5V的直流电后, 才能对手机充电

  • 手机充电器 相当于 Adapter适配器
  • 220V的交流电 相当于 Adaptee 被适配者
  • 5V的直流电 相当于 Target目标

Adapter适配器类, 继承 Adaptee被适配器类, 实现 Target目标类的接口, 完成适配。

Target目标接口: 输出5V电压

public interface Voltage5V {
    public int output5V();
}

Adaptee被适配器类: 220V电压。

public class Voltage220V {
    public int output220V() {
        System.out.println("正常220V电压");
        return 220;
    }
}

Adapter适配器类: 手机充电器 (将220V电压转换成 5V电压)

// Adapter适配器类: 将220V的交流电转换为5V的直流电
class VoltageAdapter extends Voltage220V implements Voltage5V {
    // 实现Voltage5V接口的方法,适配220V电压到5V
    @Override
    public int output5V() {
        // 获取220V的电压
        int voltage220V = output220V();
        // 转换为5V的电压
        int voltage5V = voltage220V / 44; // 220V 转换为 5V 的转换逻辑
        System.out.println("将220V电压转换为5V电压");
        return voltage5V;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建适配器对象
        Voltage5V adapter = new VoltageAdapter();
        // 使用适配器获取5V电压
        int voltage = adapter.output5V();
        System.out.println("手机充电器获得了" + voltage + "V的电压,为手机充电");
    }
}
  1. 适配器模式可以使得原本不兼容的接口能够相互协作,从而实现代码的复用。
  2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性
  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值