设计模式学习(七)——《大话设计模式》

devtools/2024/10/23 2:47:43/

文章目录

  • 设计模式学习(七)——《大话设计模式
    • 工作原理
      • 工作流程示例
    • 工作策略模式的应用场景
      • 策略模式的优点
      • 策略模式的缺点
      • 示例代码(Python)
    • 策略模式UML类图
    • 具体应用和使用场景
        • 支付方式选择
        • 数据压缩工具
        • 表单验证
        • 路由算法
        • 日志记录
        • 折扣计算
    • 策略模式和简单工厂模式的不同点

设计模式学习(七)——《大话设计模式

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

这种模式涉及到三个角色:

  • 环境类(Context):持有一个策略类的引用,最终给客户端调用。
  • 抽象策略类(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需实现的接口。
  • 具体策略类(ConcreteStrategy):包装了相关的算法或行为。

工作原理

  • 定义策略接口:首先,定义一个策略接口(Strategy),这个接口声明了算法或行为的方法。

  • 实现具体策略:接着,创建实现策略接口的具体策略类(ConcreteStrategy)。每一个具体策略类封装了一种算法或行为。

  • 创建环境类:然后,创建一个环境类(Context),它包含一个策略对象的引用。环境类提供了一个设置策略对象的方法(通常是构造器或者一个setter方法),以及将任务委托给策略对象的执行方法。

  • 客户端使用环境类:最后,客户端创建一个环境类的实例,并选择一个具体策略类传递给环境类。客户端通过环境类调用算法,而具体的算法则由具体策略类实现。

工作流程示例

假设有一个电商系统,需要根据不同的节日提供不同的打折策略。

定义策略接口:首先定义一个打折策略接口 DiscountStrategy,它有一个方法 applyDiscount。

interface DiscountStrategy {double applyDiscount(double price);
}

实现具体策略:然后实现具体策略类,例如 NoDiscountStrategy(无折扣)、ChristmasDiscountStrategy(圣诞节折扣)等。

class NoDiscountStrategy implements DiscountStrategy {public double applyDiscount(double price) {return price; // 不打折}
}class ChristmasDiscountStrategy implements DiscountStrategy {public double applyDiscount(double price) {return price * 0.9; // 打9折}
}

创建环境类:创建一个环境类 PriceCalculator,它包含一个 DiscountStrategy 的引用。

class PriceCalculator {private DiscountStrategy discountStrategy;public PriceCalculator(DiscountStrategy discountStrategy) {this.discountStrategy = discountStrategy;}public double calculatePrice(double price) {return discountStrategy.applyDiscount(price);}}

客户端使用:客户端根据当前节日选择合适的打折策略,并使用 PriceCalculator 计算打折后的价格。

public class Main {public static void main(String[] args) {DiscountStrategy christmasDiscount = new ChristmasDiscountStrategy();PriceCalculator calculator = new PriceCalculator(christmasDiscount);double originalPrice = 100.0;double discountedPrice = calculator.calculatePrice(originalPrice);System.out.println("打折后的价格是:" + discountedPrice);}}

工作策略模式的应用场景

当你想使用对象中的各种不同算法变体,同时希望能在运行时改变对象的行为时。
当你有很多类似的类,但它们在某些行为上有所不同时。
为了避免使用多重条件选择语句。不是使用条件选择来选择所需的行为,而是把这些行为封装在一个个独立的策略类中。

策略模式的优点

  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式可以提供相同行为的不同实现。
  • 使用策略模式可以避免使用多重条件转移语句。
  • 策略模式提供了一种替换继承关系的方法。

策略模式的缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式会在程序中增加许多策略类或者策略对象。

示例代码(Python)

from abc import ABC, abstractmethodclass Strategy(ABC):@abstractmethoddef do_algorithm(self, data):passclass ConcreteStrategyA(Strategy):def do_algorithm(self, data):return sorted(data)class ConcreteStrategyB(Strategy):def do_algorithm(self, data):return reversed(sorted(data))class Context():def __init__(self, strategy: Strategy):self._strategy = strategy@propertydef strategy(self):return self._strategy@strategy.setterdef strategy(self, strategy: Strategy):self._strategy = strategydef do_some_business_logic(self):result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"])print(",".join(result))####客户端代码context = Context(ConcreteStrategyA())
context.do_some_business_logic()context.strategy = ConcreteStrategyB()
context.do_some_business_logic()

策略模式UML类图

          +-------------------+|    Strategy       |+-------------------+| +execute(): void  |+-------------------+^| 实现+------------+-------------+|                          |
+-----------+             +-----------+
| ConcreteStrategyA |             | ConcreteStrategyB |
+-----------+             +-----------+
| +execute()       |             | +execute()       |
+-----------+             +-----------++-------------------+|     Context       |+-------------------+| -strategy: Strategy || +setStrategy(Strategy) || +executeStrategy()  |+-------------------+

trategy(策略接口):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口。Context使用这个接口调用具体策略定义的算法。
ConcreteStrategy(具体策略):Strategy接口的具体实现,每个实现代表了一个算法。
Context(环境类):持有一个策略类的引用,最终给客户端调用。它不决定哪一个算法是正确的,完全由客户端决定。

具体应用和使用场景

支付方式选择

在电商平台或在线支付系统中,用户可以选择多种支付方式(如支付宝、微信支付、银行卡等)。策略模式可以将每种支付方式实现为一个具体的策略类,支付系统的上下文(Context)根据用户的选择动态切换支付策略。

from abc import ABC, abstractmethod# 策略接口
class PaymentStrategy(ABC):@abstractmethoddef pay(self, amount):pass# 具体策略类:支付宝支付
class AlipayPayment(PaymentStrategy):def pay(self, amount):print(f"支付宝支付{amount}元")# 具体策略类:微信支付
class WechatPayment(PaymentStrategy):def pay(self, amount):print(f"微信支付{amount}元")# 环境类
class PaymentContext:def __init__(self, strategy: PaymentStrategy):self._strategy = strategydef execute_payment(self, amount):self._strategy.pay(amount)# 客户端代码
if __name__ == "__main__":context = PaymentContext(AlipayPayment())context.execute_payment(100)context = PaymentContext(WechatPayment())context.execute_payment(50)
数据压缩工具

一个支持多种压缩算法(如ZIP、RAR、7z等)的工具可以使用策略模式来设计。每种压缩算法都作为一个具体策略实现,用户可以根据需要选择不同的压缩策略。

from abc import ABC, abstractmethod
import zipfile
import rarfile# 策略接口
class CompressionStrategy(ABC):@abstractmethoddef compress(self, files, archive):pass# 具体策略类:ZIP压缩
class ZipCompressionStrategy(CompressionStrategy):def compress(self, files, archive):with zipfile.ZipFile(archive, 'w') as zipf:for file in files:zipf.write(file)print(f"{archive} has been created with ZIP compression.")# 具体策略类:RAR压缩
class RarCompressionStrategy(CompressionStrategy):def compress(self, files, archive):with rarfile.RarFile(archive, 'w') as rarf:for file in files:rarf.write(file)print(f"{archive} has been created with RAR compression.")# 环境类
class CompressionContext:def __init__(self, strategy: CompressionStrategy):self._strategy = strategydef set_strategy(self, strategy: CompressionStrategy):self._strategy = strategydef create_archive(self, files, archive):self._strategy.compress(files, archive)# 客户端代码
if __name__ == "__main__":files = ['example.txt', 'example2.txt']context = CompressionContext(ZipCompressionStrategy())context.create_archive(files, 'archive.zip')context.set_strategy(RarCompressionStrategy())context.create_archive(files, 'archive.rar')
表单验证

在Web开发中,表单验证是一个常见需求。不同的字段可能需要不同的验证策略(如邮箱验证、手机号验证、密码强度验证等)。通过策略模式,可以为每种验证需求实现一个具体策略类,从而使验证逻辑更加灵活和可扩展。

from abc import ABC, abstractmethod
import re# 验证策略接口
class ValidationStrategy(ABC):@abstractmethoddef validate(self, value):pass# 具体策略类:邮箱验证
class EmailValidationStrategy(ValidationStrategy):def validate(self, value):if re.match(r"[^@]+@[^@]+\.[^@]+", value):print("邮箱格式正确。")return Trueelse:print("邮箱格式错误。")return False# 具体策略类:手机号验证
class PhoneValidationStrategy(ValidationStrategy):def validate(self, value):if re.match(r"^1[3-9]\d{9}$", value):print("手机号格式正确。")return Trueelse:print("手机号格式错误。")return False# 环境类
class ValidationContext:def __init__(self, strategy: ValidationStrategy):self._strategy = strategydef set_strategy(self, strategy: ValidationStrategy):self._strategy = strategydef validate_field(self, value):return self._strategy.validate(value)# 客户端代码
if __name__ == "__main__":email_validator = ValidationContext(EmailValidationStrategy())phone_validator = ValidationContext(PhoneValidationStrategy())email_validator.validate_field("example@example.com")phone_validator.validate_field("13800000000")
路由算法

在网络设备或软件中,根据不同的网络条件选择最优的路由算法是很常见的需求。例如,可以基于最短路径、最少跳数或最低成本等标准来选择路由。每一种路由算法都可以实现为一个具体的策略类。

from abc import ABC, abstractmethod# 路由策略接口
class RoutingStrategy(ABC):@abstractmethoddef find_route(self, source, destination):pass# 具体策略类:最短路径优先
class ShortestPathStrategy(RoutingStrategy):def find_route(self, source, destination):# 假设的算法实现,实际应用中需要替换为具体的最短路径算法print(f"从{source}{destination}的最短路径已选择。")# 具体策略类:最少跳数优先
class LeastHopsStrategy(RoutingStrategy):def find_route(self, source, destination):# 假设的算法实现,实际应用中需要替换为具体的最少跳数算法print(f"从{source}{destination}的最少跳数路径已选择。")# 环境类
class Router:def __init__(self, strategy: RoutingStrategy):self._strategy = strategydef set_strategy(self, strategy: RoutingStrategy):self._strategy = strategydef route(self, source, destination):self._strategy.find_route(source, destination)# 客户端代码
if __name__ == "__main__":router = Router(ShortestPathStrategy())router.route("A", "B")router.set_strategy(LeastHopsStrategy())router.route("A", "C")
日志记录

在软件系统中,可能需要将日志记录到不同的地方,比如控制台、文件或远程服务器等。通过策略模式,可以为每种日志记录方式实现一个具体策略类,使得日志记录方式可以灵活切换。

from abc import ABC, abstractmethod
import datetime# 日志记录策略接口
class LogStrategy(ABC):@abstractmethoddef log(self, message):pass# 具体策略类:控制台日志记录
class ConsoleLogStrategy(LogStrategy):def log(self, message):print(f"{datetime.datetime.now()}: {message}")# 具体策略类:文件日志记录
class FileLogStrategy(LogStrategy):def __init__(self, file_name):self.file_name = file_namedef log(self, message):with open(self.file_name, 'a') as file:file.write(f"{datetime.datetime.now()}: {message}\n")# 环境类
class Logger:def __init__(self, strategy: LogStrategy):self._strategy = strategydef set_strategy(self, strategy: LogStrategy):self._strategy = strategydef log(self, message):self._strategy.log(message)# 客户端代码
if __name__ == "__main__":logger = Logger(ConsoleLogStrategy())logger.log("这是一条控制台日志信息。")logger.set_strategy(FileLogStrategy("logs.txt"))logger.log("这是一条文件日志信息。")
折扣计算

在销售系统中,根据不同的促销活动(如满减、打折、返现等)计算折扣。每种促销活动都可以实现为一个具体策略类,销售系统根据当前的促销策略来计算最终价格。

from abc import ABC, abstractmethod# 折扣策略接口
class DiscountStrategy(ABC):@abstractmethoddef apply_discount(self, amount):pass# 具体策略类:固定金额折扣
class FixedDiscountStrategy(DiscountStrategy):def __init__(self, discount):self.discount = discountdef apply_discount(self, amount):return max(amount - self.discount, 0)# 具体策略类:百分比折扣
class PercentageDiscountStrategy(DiscountStrategy):def __init__(self, percentage):self.percentage = percentagedef apply_discount(self, amount):return amount * (1 - self.percentage / 100.0)# 环境类
class DiscountContext:def __init__(self, strategy: DiscountStrategy):self._strategy = strategydef set_strategy(self, strategy: DiscountStrategy):self._strategy = strategydef calculate_price(self, amount):return self._strategy.apply_discount(amount)if __name__ == "__main__":# 创建一个DiscountContext对象,初始策略为固定金额折扣10discount_context = DiscountContext(FixedDiscountStrategy(10))# 计算并打印固定金额折扣后的价格print(f"固定金额折扣后价格:{discount_context.calculate_price(100)}")  # 输出应该是90# 更改策略为百分比折扣20%discount_context.set_strategy(PercentageDiscountStrategy(20))# 计算并打印百分比折扣后的价格print(f"百分比折扣后价格:{discount_context.calculate_price(100)}")  # 输出应该是80

策略模式和简单工厂模式的不同点

策略模式(Strategy Pattern)和简单工厂模式(Simple Factory Pattern)是两种常用的设计模式,它们在解决问题的方式和应用场景上有所不同。

以下是它们之间的主要区别:

  • 策略模式
    • 目的:策略模式的目的是定义一系列算法,将它们封装起来,并使它们可以互相替换。策略模式允许算法在不影响客户端的情况下独立于使用它们的客户端变化。
    • 应用场景:当有多种类似的操作或算法,且它们之间仅在实现细节上有所不同时,可以使用策略模式。它允许在运行时选择最适合的算法或操作。
    • 实现方式:通常通过定义一个公共接口来实现,各个策略类实现这个接口,客户端通过持有一个对这个接口的引用来使用不同的策略。
  • 简单工厂模式
    • 目的:简单工厂模式的目的是创建对象。它提供一个创建对象的接口,将对象的创建过程封装起来,客户端不需要知道具体的类名,只需要知道相应的参数。
    • 应用场景:当创建对象的逻辑比较复杂,但是又希望客户端代码与对象创建过程解耦时,可以使用简单工厂模式。它常用于创建相似对象的场景。
    • 实现方式:通过一个工厂类来实现,工厂类有一个静态方法,根据不同的参数返回不同类的实例。客户端只需要调用这个方法并传递相应的参数,无需直接实例化对象。
  • 主要区别
    • 目标不同:策略模式主要用于封装算法族和动态选择算法,而简单工厂模式主要用于创建对象。
    • 应用场景不同:策略模式适用于算法或操作多样性的场景,简单工厂模式适用于创建对象时需要隐藏创建逻辑的场景。
    • 实现方式和侧重点不同:策略模式侧重于定义一系列可互换的算法,并让客户端可以动态选择使用哪一种算法。简单工厂模式侧重于封装对象创建过程,减少客户端与具体类之间的依赖。

:通常通过定义一个公共接口来实现,各个策略类实现这个接口,客户端通过持有一个对这个接口的引用来使用不同的策略。

  • 简单工厂模式
    • 目的:简单工厂模式的目的是创建对象。它提供一个创建对象的接口,将对象的创建过程封装起来,客户端不需要知道具体的类名,只需要知道相应的参数。
    • 应用场景:当创建对象的逻辑比较复杂,但是又希望客户端代码与对象创建过程解耦时,可以使用简单工厂模式。它常用于创建相似对象的场景。
    • 实现方式:通过一个工厂类来实现,工厂类有一个静态方法,根据不同的参数返回不同类的实例。客户端只需要调用这个方法并传递相应的参数,无需直接实例化对象。
  • 主要区别
    • 目标不同:策略模式主要用于封装算法族和动态选择算法,而简单工厂模式主要用于创建对象。
    • 应用场景不同:策略模式适用于算法或操作多样性的场景,简单工厂模式适用于创建对象时需要隐藏创建逻辑的场景。
    • 实现方式和侧重点不同:策略模式侧重于定义一系列可互换的算法,并让客户端可以动态选择使用哪一种算法。简单工厂模式侧重于封装对象创建过程,减少客户端与具体类之间的依赖。

简而言之,策略模式关注于算法和行为的多样性及其可替换性,而简单工厂模式关注于创建具体对象,隐藏创建细节,减少客户端与具体类之间的依赖。


http://www.ppmy.cn/devtools/5894.html

相关文章

IaC:实现持续交付和 DevOps 自动化的关键

基础架构即代码(IaC)和 CI/CD 流水线最初似乎并不匹配。因为它们代表了两种不同的流程。IaC 主要关注基础设施的配置和开发,而 CI/CD 则围绕软件开发、测试和部署。 然而,将 IaC 集成到 CI/CD 流水线中具有多种优势。首先&#xf…

对组合模式的理解

目录 一、场景1、题目描述 【[案例来源](https://kamacoder.com/problempage.php?pid1090)】2、输入描述3、输出描述4、输入示例5、输出示例 二、实现(假的组合模式)1、代码2、为什么上面的写法是假的组合模式? 三、实现(真的组合…

十分钟快速制作一个俄罗斯方块桌面游戏

准备 安装 Python: 下载 Python 安装程序: 访问 Python 官方网站,在下载页面选择适合您操作系统的 Python 版本。通常推荐下载最新版本。 运行安装程序: 下载完成后,运行下载的安装程序。在安装过程中,请确保勾选“Add Python X.X to PATH”选项(X.X 代表您下载的 Pyth…

模块与包及json模块学习

【一】模块与包介绍 【1】什么是模块 在Python中,一个py文件其实就是一个模块 文件名 knight.py中 py就是模块名 【2】模块的优点 有了模块以后可以增加程序的可读性,提高开发效率 【3】模块的来源 (1)在Python解释器内部内…

MySQL数据库企业级开发技术(下篇)

使用语言 MySQL 使用工具 Navicat Premium 16 代码能力快速提升小方法,看完代码自己敲一遍,十分有用 拖动表名到查询文件中就可以直接把名字拉进来中括号,就代表可写可不写 目录 1. 视图 1.1 需要视图的原因 1.2 视图介绍 1.2.1 …

51.基于SpringBoot + Vue实现的前后端分离-校园志愿者管理系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统,采用SpringBoot Vue框架,MYSQL数据库设计开发,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得基于SpringBoot Vue技术的校园志愿者管理系统设计与实现管理工…

Java -jar参数详解:掌握Java可执行JAR文件的运行技巧

作为一种常用的开发语言,Java经常使用可执行的JAR(Java Archive)文件来打包和分发应用程序。使用java -jar命令运行JAR文件是一种方便快捷的方式。本文将详细介绍java -jar命令的各种参数,帮助您充分利用这个功能。 一、简介 java…

MySQL数据库——17.正则表达式

MySQL正则表达式是一种强大的模式匹配工具,用于在文本数据中搜索和匹配特定的模式。MySQL使用正则表达式来执行模式匹配的操作,可以在SELECT语句的WHERE子句中使用,也可以在其他SQL语句中使用。 基本语法: 在MySQL中,正则表达式通常与REGEXP关键字一起使用。基本语法如下…