【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.mzph.cn/web/51460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全的历史

如今&#xff0c;网络安全几乎成为各大公司和利益相关者关注的焦点。但在早期&#xff0c;网络安全的概念非常模糊。 直到多年以后&#xff0c;由于网络攻击和危险实体威胁的频繁发生&#xff0c;网络安全的发展才受到重视。这些措施的发展成为了网络安全的演变。 网络安全起…

el-pagination 下拉条目数最后一个样式改成全部

2024.08.27今天我学习了如何把el-pagination的下拉条目数修改&#xff0c;效果如下&#xff1a; 我们需要把最后一条选择换成全部展示&#xff0c;其实传给后端的还是总的数量&#xff0c;只是换了一个content&#xff0c; 通过f12查看元素可以拿到.el-select-dropdown__item …

创建django项目时遇到的改项目名字问题

问题描述 今天在学习时遇到了一些问题&#xff0c;现记录一下。 今天我利用 ‘django-admin startproject 项目名字’命令创建了一个项目&#xff0c;并觉得当时项目名字没有命好&#xff0c;所以就随性的运行了 ’rename 旧项目名字 新项目名字‘这一命令。但是随后就出现…

使用idea快速创建springbootWeb项目(springboot+springWeb+mybatis-Plus)

idea快速创建springbootWeb项目 详细步骤如下 1&#xff09;创建项目 2&#xff09;选择springboot版本 3&#xff09;添加web依赖 4&#xff09;添加Thymeleaf 5&#xff09;添加lombok依赖 然后点击create进入下一步 双击pom.xml文件 6&#xff09;添加mybatis-plus依赖 …

java.util.Optional.or() .orElse(), .orElseGet()的区别

java.util.Optional 是 Java 8 引入的一个容器类&#xff0c;用于表示一个可能包含或不包含非空值的对象。它提供了多种方法来处理可能为空的对象&#xff0c;从而避免使用null值。 在 Optional 中&#xff0c;.or(), .orElse(), 和 .orElseGet() 都是用于处理可能为空的值的方…

7-10 简单求阶乘问题

本题要求编写程序&#xff0c;计算N的阶乘。 输入格式: 输入在一行中给出一个不超过12的正整数N。 输出格式: 在一行中输出阶乘的值。 输入样例: 4输出样例: 24 #include <stdio.h>int Fac(int x){if(x1) return 1;// 递归出口return x*Fac(x-1);// 递归 } int m…

C_03_函数学习

函数 优点&#xff1a; 降低代码耦合度降低代码冗余度提高代码复用率提高代码可读性 思想&#xff1a; 封装【包装】 声明&#xff1a; 语法&#xff1a; extern 函数名(形参列表)&#xff1b;// 注意&#xff1a;此时 形参列表中变量名可以忽略不写&#xff1b;定义&#xff1…

47.【C语言】指针(重难点)(J)

目录 26.自制排序函数(★★) *分析 *代码 往期推荐 26.自制排序函数 *分析 之前在42.【C语言】冒泡排序写过一个排序函数&#xff0c;可以将此自制一个类似qsort的函数 画圈的地方是需要修改的 #include <stddef.h> void bubble_sort(void* base, size_t num,size_t w…

面试被面试官问:3D目标检测预处理优化策略有哪些?

01 前言&#xff1a; 3D目标检测是计算机视觉领域中一个重要的任务&#xff0c;广泛应用于自动驾驶、机器人导航、无人机等领域。由于3D数据的复杂性和多样性&#xff0c;数据预处理在3D目标检测中扮演着关键角色。良好的预处理策略不仅可以提升模型的检测精度&#xff0c;还可…

并发知识笔记

一、使用线程持有变量获取线程执行结果 /*** 线程持有变量*/private static final ThreadLocal<Map<String, Object>> MAP_THREAD_LOCAL ThreadLocal.withInitial(HashMap::new);// ----------------------------------------------------------------正文int dat…

Day1:88. 合并两个有序数组、27.移除元素、26.删除有序数组中的重复项

此次挑战的是leetcode面试150题&#xff0c;每天刷三题&#xff0c;争取一小时内结束。 88. 合并两个有序数组 思路直接想到&#xff0c;双指针&#xff0c;从后往前放&#xff08;避免数组移动&#xff09;&#xff0c;但是没有考虑到nums1结束了&#xff0c; nums2还没结束…

Flat Ads:全球金融应用的营销投放洞察

随着移动互联网的普及,金融应用在全球范围内迅速崛起。无论是移动银行、支付服务,还是理财工具,金融类应用已经成为现代生活中不可或缺的一部分。根据最新的行业报告,全球金融应用的下载量和用户活跃度在过去几年里持续增长,尤其是在新兴市场,用户对数字金融服务的需求不断攀升…

力扣top100-链表类题易错点总结-c++实现(更新中)

首先给一个我之前写的双指针在链表类题中的妙用的link&#xff1a;双指针在链表中的妙用 tip1 来自“合并两个有序链表” 题目链接戳这里 这道题注意的就是如果是要返回一个新链表的头结点&#xff0c;一定要新建一个头结点&#xff1a; ListNode* prehead new ListNode…

java框架第二课(Reflection反射机制)

一.关于反射 (1)使用场景介绍 平常我们写代码时&#xff0c;都是已知类名&#xff0c;类的属性&#xff0c;构造方法&#xff0c;其他方法等信息&#xff0c;然后根据类名new对象&#xff0c;这个过程称为正向操作(例如&#xff1a;有一个管理员类&#xff0c;有账号和密码属…

【SQL】三角形判断

目录 题目 分析 代码 题目 表: Triangle ------------------- | Column Name | Type | ------------------- | x | int | | y | int | | z | int | ------------------- 在 SQL 中&#xff0c;(x, y, z)是该表的主键列。 该表的每一行包…

Sigmoid 函数及其导数推导

Sigmoid 函数及其导数推导 1. 了解 Sigmoid 函数 Sigmoid 函数是神经网络中常用的激活函数&#xff0c;因其平滑的S形曲线和将输入压缩至 (0, 1) 的特性&#xff0c;在神经网络的激活函数中扮演着重要角色。其定义如下&#xff1a; σ ( x ) 1 1 e − x \sigma(x) \frac{1…

FunASR自动语音识别的创新平台

1. 什么是自动语音识别&#xff08;ASR&#xff09; 自动语音识别&#xff08;ASR, Automatic Speech Recognition&#xff09;是一种将语音信号转换为文本的技术。随着语音助手、智能家居、翻译系统等应用的兴起&#xff0c;ASR技术的重要性日益凸显。传统的ASR系统依赖于复杂…

操作系统线程属性

线程属性 int pthread_create (pthread_t* restrict thread,const pthread_attr_t* restrict attr,void* (*start_routine) (void*),void* restrict arg); ​ //创建线程函数的第二个参数即为线程属性&#xff0c;传空指针表示使用缺省属性。 typedef struct {// 分离状态int …

【应用开发】解决正点原子I.MX6ull应用编程zlib移植问题

问题描述 在正点原子应用开发移植zlib库的时候&#xff0c;文档中有这样一段描述&#xff0c;先删除开发板中的zlib库&#xff0c;然后再拷贝zlib库 这就会导致在使用scp命令拷贝编译好的zlib库的时候报错没有zlib.so.1&#xff0c;如下图所示&#xff1a; 解决方法 千万不…

《算法竞赛进阶指南》0x27A*

如果给定一个“目标状态”&#xff0c;需要求出从初态到目标状态的最小代价&#xff0c;那么优先队列BFS的“优先策略”显然不完善。一个状态的当前代价最小&#xff0c;只能说明从起始状态到当前状态得到代价很小&#xff0c;而在未来的搜索中&#xff0c;从该状态到目标状态可…