当前位置: 首页 > article >正文

【Java】Java 设计模式之工厂模式与策略模式

Java设计模式是软件工程中一系列被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,它们代表了最佳的实践,帮助开发者解决在软件设计过程中遇到的各种问题。这些模式可以根据其用途分为三大类:创建型、结构型和行为型,每种模式都有其特定的应用场景和解决的问题,例如单例模式用于确保一个类只有一个实例,工厂模式用于创建对象而不暴露创建逻辑,观察者模式用于定义对象间的一对多依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。掌握这些设计模式有助于提高代码的可读性、可维护性和可扩展性。

本文中所讲的工厂模式是一种创建型设计模式,它提供了一个接口,用于创建对象,但允许子类决定实例化的类是哪一个,从而将对象的创建逻辑与实际使用逻辑分离,增强了系统的可扩展性和灵活性。而策略模式是一种行为型设计模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户,使得算法可以独立于客户端进行扩展和切换。

1. 工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,属于创建型模式。它的主要特点是提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。这样做的好处是将对象的创建逻辑封装在一个工厂类中,从而提高代码的可维护性和可扩展性。

1.1 工厂模式的主要角色:
  1. 抽象工厂(Abstract Factory):定义一个创建对象的接口,但不负责具体的对象创建过程。
  2. 具体工厂(Concrete Factory):实现抽象工厂接口,负责实际创建具体的对象。
  3. 产品(Product):工厂所创建的对象类型。
  4. 具体产品(Concrete Product):实现产品接口的具体对象。

工厂模式的主要目的是将对象的创建过程封装在工厂类中,客户端代码只需要关心从工厂获取对象的过程,而不需要了解对象的创建细节。这样可以降低代码的耦合度。

1.2 工厂模式分类:
  1. 简单工厂模式(Simple Factory Pattern):使用一个单独的工厂类来创建不同的对象,根据传入的参数决定创建哪种类型的对象。
  2. 工厂方法模式(Factory Method Pattern):定义了一个创建对象的接口,但由子类决定实例化哪个类。
  3. 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。
1.3 工厂模式的优缺点:

优点:

  • 调用者只需要知道对象的名称即可创建对象。
  • 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
  • 屏蔽了产品的具体实现,调用者只关心产品的接口。

缺点:

  • 每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。

下面是一个简单的工厂模式示例,我们将创建一个简单的图形工厂,该工厂能够根据给定的类型创建不同的图形对象。

首先,我们定义一个图形接口和几个具体的图形类:

// 图形接口
interface Shape {
    void draw();
}
// 具体的图形类:圆形
class Circle implements Shape {
    public void draw() {
        System.out.println("Drawing Circle");
    }
}
// 具体的图形类:矩形
class Rectangle implements Shape {
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}
// 具体的图形类:正方形
class Square implements Shape {
    public void draw() {
        System.out.println("Drawing Square");
    }
}

接下来,我们定义一个图形工厂类,它将根据传入的类型参数来创建并返回相应的图形对象:

// 图形工厂类
class ShapeFactory {
    // 获取图形对象
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

最后,我们使用这个工厂类来创建图形对象并调用它们的draw方法:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();
        // 获取 Circle 对象并调用它的 draw 方法
        Shape circle = shapeFactory.getShape("CIRCLE");
        circle.draw();
        // 获取 Rectangle 对象并调用它的 draw 方法
        Shape rectangle = shapeFactory.getShape("RECTANGLE");
        rectangle.draw();
        // 获取 Square 对象并调用它的 draw 方法
        Shape square = shapeFactory.getShape("SQUARE");
        square.draw();
    }
}

在以上示例中,ShapeFactory类扮演了工厂的角色,它根据传入的字符串参数来决定创建哪种类型的图形对象。客户端代码不需要直接实例化具体的图形类,而是通过工厂类来获取所需的图形对象。这样,如果将来需要添加新的图形类,只需修改工厂类即可,无需修改客户端代码,这符合开闭原则。

2. 工厂模式使用场景

2.1 工厂模式适用于以下几种场景:
  1. 不确定要使用哪个类的情况:当客户端代码需要创建对象,但具体要创建哪个类的对象在运行时才能确定时,可以使用工厂模式。
  2. 处理复杂对象的创建逻辑:如果一个对象的创建过程很复杂,包含多个步骤或依赖其他对象,使用工厂模式可以将这些逻辑封装起来,简化客户端代码。
  3. 需要屏蔽具体实现的情况:当客户端代码不应依赖于具体的产品类时,工厂模式可以提供一个统一的接口,使得客户端与具体的产品实现解耦。
  4. 系统需要支持多种产品系列的情况:比如一个系统需要支持多种数据库,可以使用抽象工厂模式,为每种数据库提供一个具体的工厂。
2.2 具体场景包括:
  • 日志记录:可以根据配置或运行时条件,选择不同的日志记录器(例如文件日志记录器、数据库日志记录器等)。
  • 数据库访问:系统可能需要支持多种数据库,如 MySQL、PostgreSQL、Oracle 等,工厂模式可以帮助切换不同的数据库实现。
  • 文件格式处理:如果系统需要处理多种文件格式,如 CSV、XML、JSON 等,可以使用工厂模式来创建相应的处理器。
  • 硬件接口适配:在需要与多种硬件设备交互时,可以为每种设备提供一个工厂,以创建相应的接口适配器。
  • 图形界面组件创建:在一个图形用户界面(GUI)应用程序中,根据不同的操作系统创建不同的按钮、文本框等组件。

工厂模式通过隐藏对象创建的细节,提供了更加灵活和可维护的代码结构。通过使用工厂模式,这些场景下的系统可以更加灵活,易于扩展和维护。当需要添加新的产品类型时,只需增加新的产品和对应的工厂,而不需要修改现有代码,符合开闭原则。

3. 策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。

3.1 策略模式主要包含的角色:
  1. 策略接口(Strategy Interface):定义所有支持的算法的公共接口。策略类将实现这个接口,从而实现具体的算法。
  2. 具体策略类(Concrete Strategies):实现策略接口的类,封装了具体的算法。
  3. 上下文类(Context Class):持有一个策略接口的引用,用于操作策略接口。上下文类并不实现算法,而是通过策略接口调用具体策略类实现的算法。
3.2 策略模式的优点:

优点:

  • 算法可以自由切换:客户端可以根据需要选择不同的策略算法。
  • 扩展性良好:增加新的策略只需要实现策略接口,然后将其注入到上下文中即可。
  • 避免使用多重条件判断:策略模式可以避免在代码中使用大量的if-else或switch-case语句。
  • 维护各算法的独立性:每个策略类封装了自己的算法,减少了算法间的耦合。

缺点:

  1. 客户端必须了解所有策略:策略模式要求客户端必须知道所有的策略类,并理解它们之间的区别,以便能够选择合适的策略。这增加了客户端的负担,尤其是在策略很多或者策略之间差异不明显时。
  2. 策略类数量增加:随着策略的增加,系统中类的数量也会相应增加。每个策略都是一个单独的类,这可能导致类爆炸,增加了系统的复杂性。
  3. 策略的管理:客户端需要负责管理策略对象的生命周期,这会增加客户端的复杂性,尤其是在需要动态切换策略的情况下。

以下是一个简单的策略模式示例:

// 策略接口
interface Strategy {
    void execute();
}

// 具体策略A
class ConcreteStrategyA implements Strategy {
    public void execute() {
        // 具体的算法实现
    }
}

// 具体策略B
class ConcreteStrategyB implements Strategy {
    public void execute() {
        // 具体的算法实现
    }
}

// 上下文类
class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void executeStrategy() {
        strategy.execute();
    }
}

// 使用
Context context = new Context(new ConcreteStrategyA());
context.executeStrategy(); // 执行策略A
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy(); // 切换策略,执行策略B

在这个例子中,Context类可以根据需要切换不同的策略,而客户端代码只需要知道如何与Context交互,不需要关心具体策略的实现细节。

4. 策略模式使用场景

4.1 策略模式适用于以下场景:
  1. 多个算法只在行为上有所不同:当有一组算法,它们执行的任务相同,但实现细节不同,可以使用策略模式来动态选择使用哪个算法。
  2. 算法需要频繁切换:如果应用程序需要根据不同的条件或用户输入频繁更改算法,策略模式可以提供一种灵活的方式来切换算法。例如支付方式的选择、排序算法的选择等。
  3. 算法需要自由扩展:当预计将来会添加更多算法时,策略模式可以轻松地通过添加新的策略类来扩展,而不需要修改现有代码。
  4. 需要隐藏算法的具体实现:策略模式可以将算法的实现细节封装在具体的策略类中,客户端只需通过策略接口与它们交互。
  5. 避免使用多重条件语句:如果代码中存在大量的if-else或switch-case语句来选择不同的算法,使用策略模式可以替代这些条件语句,使代码更加清晰。
4.2 具体的应用场景包括:
  • 支付系统:根据不同的支付方式(如信用卡、PayPal、支付宝等)选择不同的支付策略。
  • 排序算法:实现不同的排序算法(如快速排序、冒泡排序、归并排序等),根据数据特点选择合适的排序策略。
  • 图像处理:根据不同的图像处理需求(如缩放、旋转、过滤等)选择不同的处理策略。
  • 折扣计算:在电子商务系统中,根据不同的促销活动计算不同的折扣策略。
  • 验证机制:根据不同的用户角色或操作类型选择不同的验证策略。
  • 数据导出:根据用户需求将数据导出为不同的格式(如PDF、Excel、CSV等)。

通过使用策略模式,这些场景下的系统可以更加灵活,易于维护和扩展。策略模式有助于保持代码的整洁和可读性,同时提供了在运行时动态更改算法的能力。

5. 工厂模式和策略模式区别

工厂模式和策略模式都是常用的设计模式,但它们解决的问题和应用场景不同。以下是它们之间的主要区别:

5.1 目的:
  • 工厂模式:用于创建对象,它封装了对象的创建逻辑,使得创建对象的过程与使用对象的过程分离。
  • 策略模式:用于定义一系列算法,将每个算法封装起来,并使它们可以互换,从而让算法的变化独立于使用算法的客户。
5.2 关注点:
  • 工厂模式:关注对象的创建过程。
  • 策略模式:关注算法或行为的切换和扩展。
5.3 主要组件:
  • 工厂模式
    • 抽象工厂(Abstract Factory)
    • 具体工厂(Concrete Factory)
    • 产品(Product)
    • 具体产品(Concrete Product)
  • 策略模式
    • 策略接口(Strategy Interface)
    • 具体策略(Concrete Strategies)
    • 上下文(Context)
5.4 使用场景:
  • 工厂模式
    • 当创建对象的过程复杂,需要封装时。
    • 当需要根据不同条件创建不同类型的对象时。
    • 当系统需要与多个产品系列交互,但只想通过一个统一接口与它们交互时。
  • 策略模式
    • 当多个类只区别在行为上,可以使用策略模式来动态选择不同的行为。
    • 当需要自由切换算法或行为时。
    • 当算法需要扩展,但不想修改使用算法的客户端代码时。
5.5 优缺点:
  • 工厂模式
    • 优点:提高了代码的扩展性和可维护性,降低了对象间的耦合。
    • 缺点:可能导致系统中类的数量增加,增加了系统的复杂度。
  • 策略模式
    • 优点:算法可以自由切换,扩展性好,避免了使用多重条件判断。
    • 缺点:客户端需要知道所有的策略类,并理解它们之间的区别。
5.6 示例对比:
  • 工厂模式

    interface VehicleFactory {
        Vehicle createVehicle();
    }
    class CarFactory implements VehicleFactory {
        public Vehicle createVehicle() {
            return new Car();
        }
    }
    class BikeFactory implements VehicleFactory {
        public Vehicle createVehicle() {
            return new Bike();
        }
    }
    
  • 策略模式

    interface SortingStrategy {
        void sort(List<Integer> items);
    }
    class BubbleSortStrategy implements SortingStrategy {
        public void sort(List<Integer> items) {
            // 实现冒泡排序算法
        }
    }
    class QuickSortStrategy implements SortingStrategy {
        public void sort(List<Integer> items) {
            // 实现快速排序算法
        }
    }
    

工厂模式关注对象的创建,而策略模式关注算法或行为的封装和切换。两者都是通过抽象来提高代码的灵活性和可扩展性,但它们的应用目的和场景有所不同。


http://www.kler.cn/a/282191.html

相关文章:

  • 《Python 网络爬虫》
  • 删除缓存之后,浏览器显示登录新设备
  • FFmpeg源码:avio_read_partial函数分析
  • 深度学习中的Pixel Shuffle和Pixel Unshuffle:图像超分辨率的秘密武器
  • Amazon Web Services (AWS)
  • rk3399开发环境使用Android 10初体验蓝牙功能
  • 本地Docker部署Navidrome音乐服务器与远程访问听歌详细教程
  • 力扣题/回溯/子集
  • java.time包时间类浅谈
  • C语言中的int argc, char *argv[]是什么?
  • 设计模式 10 外观模式
  • mysql集群从零开始搭建
  • Qt详解QUrl
  • CSS动画魔法:用@keyframes点亮你的网页
  • Python操作Excel的十大技巧
  • 算法的学习笔记—数据流中的中位数(牛客JZ41)
  • 数学建模~~~预测方法--决策树模型
  • pyautogui的一些自动化示例,附代码
  • 新手如何找到正确入行 Web3 路径?揭开职业启航新篇章
  • linux驱动 -- 输入子系统
  • 【STM32】SPI
  • HFM深入技术学习系列之五--FDMEE钻取EBS
  • 单链表——随机链表的复制
  • 开源模型应用落地-LlamaIndex学习之旅-LLMs-集成OpenAI(一)
  • 322.零钱兑换
  • HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据