设计模式--策略模式(由简单工厂到策略模式到两者结合图文详解+总结提升)

news2024/11/24 12:06:51

目录

  • 概述
    • 概念
    • 组成
    • 应用场景
    • 注意事项
    • 类图
  • 衍化过程
    • 需求
    • 简单工厂实现
      • 代码
    • 策略模式
      • 代码
    • 策略模式+简单工厂
      • 代码
  • 总结升华
    • 版本迭代的优化点及意义
    • 什么样的思路进行衍化的
    • 扩展思考--如何理解策略与算法

概述

概念

    策略模式是一种行为型设计模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

组成

1、Context(上下文):用于维护策略类的引用,并在运行时根据需要选择合适的策略。
2、Strategy(策略):定义一个公共接口,用于封装不同的算法或行为。
3、ConcreteStrategy(具体策略):实现策略接口,提供具体的算法或行为。

应用场景

    当一个系统需要根据不同的情况选择不同的算法或行为时,可以使用策略模式。
    当一个类中有多个条件语句,每个条件语句对应一个算法或行为时,可以考虑使用策略模式简化代码。

注意事项

    策略模式将算法或行为封装在不同的策略类中,因此需要对策略类进行合理的设计和组织。
    策略模式需要在运行时动态选择策略,因此需要在客户端代码中设置策略的选择逻辑。

类图

在这里插入图片描述

衍化过程

需求

    商场根据不同的促销活动,以不同的方式对商品进行折扣计算和结算。具体来说,商场收银的需求可以分为以下几个阶段的变化过程:

  1. 初始需求:商场最开始只有一种收费方式,即正常收费,即不打折不返现,直接按照原价进行结算。

  2. 变化需求1:商场决定进行促销活动,对部分商品进行打折处理。这时,商场需要根据商品的折扣率来计算折扣后的价格,并进行结算。

  3. 变化需求2:商场进一步决定进行满减活动,即满足一定条件后,可以返现一定金额。这时,商场需要根据商品的价格和满减条件来计算返现后的价格,并进行结算。

  4. 变化需求3:商场希望能够根据不同的促销活动,动态地选择不同的收费方式。这时,商场引入了策略模式,将不同的收费方式封装在不同的策略类中,并在运行时根据需要选择合适的策略进行结算。

前两版比较简单,这里不再赘述,主要从需求3说起:
    分为简单工厂实现、策略模式实现、简单工厂+策略模式

简单工厂实现

代码

收费相关类

//收费抽象类
abstract class CashSuper {
    public abstract double acceptCash(double money);
}
//正常收费类
public class CashNormal extends CashSuper {

    @Override
    public double acceptCash(double money) {
        return money;
    }
}
//打折收费类
public class CashRebate extends CashSuper {
    private double moneyRebate=1;
    public CashRebate(String moneyRebate){
        this.moneyRebate= Double.parseDouble(moneyRebate);
    }
    @Override
    public double acceptCash(double money) {
        return money*moneyRebate;
    }
}
//满减收费类
public class CashReturn extends CashSuper {
    private double moneyCondtion;
    private double moneyReturn;
    public CashReturn(String moneyCondition,String moneyReturn){
        this.moneyCondtion= Double.parseDouble(moneyCondition);
        this.moneyReturn= Double.parseDouble(moneyReturn);
    }
    @Override
    public double acceptCash(double money) {
        double result=money;
        if (money>=moneyCondtion){
            result=money-Math.floor(money/moneyCondtion)*moneyReturn;
        }
        return result;
    }
}

工厂

public class CashFactory {
    public  CashSuper creatCashAccept(String type) {
        CashSuper cs = null;
        switch (type){
            case "正常收费":
                cs=new CashNormal();
            break;
            case "满300返100":
                cs=new CashReturn("300","100");
            break;
            case "打八折":
                cs=new CashRebate("0.8");
                break;
        }
        return cs;

    }

客户端

public class Client {
    public static void main(String[] args) {
        CashFactory factory=new CashFactory();
        CashSuper cs=factory.creatCashAccept("满300返100");
        double result=cs.acceptCash(300);
        System.out.println(result);
    }
}

策略模式

类图
在这里插入图片描述

NS图
在这里插入图片描述

代码

收费上下文类

public class CashContext {
    private CashSuper cs;
    public CashContext(CashSuper csuper){
        this.cs=csuper;
    }
    public double getResult(double money){
        return cs.acceptCash(money);
    }

}

客户端

public class Client {
    public static void main(String[] args) {


        //传的是不同的策略对象
        CashContext selectNormal = new CashContext(new CashNormal());
        CashContext selectRebate = new CashContext(new CashRebate("0.8"));
        CashContext selectReturn = new CashContext(new CashReturn("300","100"));

        System.out.println("选择正常:"+selectNormal.getResult(300));
        System.out.println("选择打八折:"+selectRebate.getResult(300));
        System.out.println("选择满减:"+selectReturn.getResult(300));
    }
}

策略模式+简单工厂

在这里插入图片描述

代码

收费上下文类(其中包括简单工厂)

public class CashContext {
   private CashSuper cs=null;

   //参数是个字符串,而不是收费策略对象
   public CashContext(String type){
       switch (type){
           case "正常收费":
               cs=new CashNormal();
               break;
           case "满300返100":
               cs=new CashReturn("300","100");
               break;
           case "打8折":
               cs=new CashRebate("0.8");
               break;
       }
   }
   public double getResult(double money){
       return cs.acceptCash(money);

   }

}

客户端

public class Client {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入销售模式:");
        String type = scanner.nextLine();

        //传的是字符串,而不是具体的策略类
        CashContext cash = new CashContext(type);
        System.out.println("请输入原价");
        double price = Double.parseDouble(scanner.nextLine());
        System.out.println("最后收费" + cash.getResult(price));

    }
}

总结升华

版本迭代的优化点及意义

1.前两版到简单工厂
     将算法封装,每一个优惠策略都是一个单独的类,而且它们都继承自一个抽象类,抽象类是此版本的一个巨大进步,出现了面向对象以及类的概念,后续的扩充、维护、复用打下了基础。

2.简单工厂到策略模式
    使用简单工厂违反开闭原则,考虑到了算法变化性,要这些算法随时可以互相替换,也正是策略模式概念中提到的,而封装变化点是面向对象一种重要的思维方式。

3.策略模式到简单工厂+策略模式
    策略模式中实例化对象还是交给了客户端去完成,结合了简单工厂之后,实例化对象由CashContext来完成,可以在客户端消除条件语句。

    另外,简单工厂模式客户端需要认识CashSuper和CashFactory,而简单工厂+策略模式,客户端之需要认识CashContext一个类就可以,降低了耦合性。
在这里插入图片描述

什么样的思路进行衍化的

有限到无限:
    依据业务需求,如果存在潜在的需要,就用无限的思维去进行设计(看到switch case就要思考是否需要无限的思维)
    如何去做到无限,使用想象力与创造力把有限的需求变成无限的需求,同时使用有限的方法去实现,落实到代码上是依据软件工程的7大原则和23个设计模式(独立或组合),达到可扩充、可复用、可维护的目标。

扩展思考–如何理解策略与算法

    策略模式中提到策略模式定义了算法家族,看类图的时候貌似策略和算法是一个概念,但是两者真的是一样的吗,如无必要勿增实体。那它们是什么关系呢,有没有一种可能,策略和算法并不是一一对应的关系,而独立算法和算法的组合都可以称之为策略呢,就像类图中Strategy与Context是聚合关系。这只是一个想法,策略模式也没有结束,后续还有策略模式+工厂方法+反射的实现,以及我所猜测的算法的组合也可称之为策略,敬请期待。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/839689.html

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

相关文章

Docker安装Grafana以及Grafana应用

Doker基础 安装 1、 卸载旧的版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 2、需要的安装包 sudo yum install -y yum-utils 3、设置镜像的仓库 yum-config-m…

UML-构件图

目录 1.概述 2.构件的类型 3.构件和类 4.构件图 1.概述 构件图主要用于描述各种软件之间的依赖关系,例如,可执行文件和源文件之间的依赖关系,所设计的系统中的构件的表示法及这些构件之间的关系构成了构件图 构件图从软件架构的角度来描述…

数组的学习

数组学习 文章目录 数组来由数组的使用数组的内存图变量声明和args参数说明声明分配空间值的省略写法数组的length属性数列输出求和判断购物金额结算Arrays的sort和toString方法Arrays的equals和fill和copyOf和binarySearch方法字符数组顺序和逆序输出 数组来由 录入30个学生…

Gson:解析JSON为复杂对象:TypeToken

需求 通过Gson&#xff0c;将JSON字符串&#xff0c;解析为复杂类型。 比如&#xff0c;解析成如下类型&#xff1a; Map<String, List<Bean>> 依赖&#xff08;Gson&#xff09; <dependency><groupId>com.google.code.gson</groupId><art…

渗透攻击方法:原型链污染

目录 一、什么是原型链 1、原型对象 2、prototype属性 3、原型链 1、显示原型 2、隐式原型 3、原型链 4、constructor属性 二、原型链污染重现 实例 Nodejs沙箱逃逸 1、什么是沙箱&#xff08;sandbox&#xff09; 2、vm模块 一、什么是原型链 1、原型对象 JavaS…

UE4 Cesium 学习笔记

Cesium中CesiumGeoreference的原点Orgin&#xff0c;设置到新的位置上过后&#xff0c;将FloatingPawn的Translation全改为0&#xff0c;才能到对应的目标点上去 在该位置可以修改整体建筑的材质 防止刚运行的时候&#xff0c;人物就掉下场景之下&#xff0c;controller控制的…

LeetCode113. 路径总和 II

113. 路径总和 II 文章目录 [113. 路径总和 II](https://leetcode.cn/problems/path-sum-ii/)一、题目二、题解方法一&#xff1a;递归另一种递归版本方法二&#xff1a;迭代 一、题目 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶…

Flutter 实现按位置大小比例布局的控件

文章目录 前言一、如何实现&#xff1f;1、数值转成分数2、RowFlexible布局横向3、ColumnFlexible布局纵向 二、完整代码三、使用示例1、基本用法2、四分屏3、六分屏4、八分屏5、九分屏6、414分屏 总结 前言 做视频监控项目时需要需要展示多分屏&#xff0c;比如2x2、3x3、414…

vue2-vue中key的原理

vue中key是什么&#xff1f;它有什么作用&#xff1f;原理是什么&#xff1f; 1、key是什么&#xff1f; 先考虑两个实际场景 当我们使用v-for时&#xff0c;需要给单元加上key 用new Date()生成的时间戳作为key&#xff0c;手动强制触发重新渲染。 在上面两种场景中&#xf…

一百四十三、Linux——Linux的CentOS 7系统语言由中文改成英文

一、目的 之前安装CentOS 7系统的时候把语言设置成中文&#xff0c;结果Linux文件夹命名出现中文乱码的问题&#xff0c;于是决定把Linux系统语言由中文改成英文 二、实施步骤 &#xff08;一&#xff09;到etc目录下&#xff0c;找到配置文件locale.conf # cd /etc/ # ls…

总结七大排序!

排序总览 外部排序&#xff1a;依赖硬盘&#xff08;外部存储器&#xff09;进行的排序。对于数据集合的要求特别高&#xff0c;只能在特定场合下使用&#xff08;比如一个省的高考成绩排序&#xff09;。包括桶排序&#xff0c;基数排序&#xff0c;计数排序&#xff0c;都是o…

C++ STL快速应用

STL 容器 STL容器有共同的操作接口&#xff0c;包括初始化操作、判空、查看大小、比较元素、销毁、交换&#xff0c;这些操作都是一样的接口。 对于访问遍历元素&#xff08;增删改查&#xff09;&#xff0c;都可以使用迭代器&#xff08;正向&#xff09;进行操作&#xff0c…

信号平滑或移动平均滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【雕爷学编程】Arduino动手做(184)---快餐盒盖,极低成本搭建机器人实验平台2

吃完快餐粥&#xff0c;除了粥的味道不错之外&#xff0c;我对个快餐盒的圆盖子产生了兴趣&#xff0c;能否做个极低成本的简易机器人呢&#xff1f;也许只需要二十元左右 知识点&#xff1a;轮子&#xff08;wheel&#xff09; 中国词语。是用不同材料制成的圆形滚动物体。简…

JDK17环境下安装Nacos

1.配置好jdk17环境 命令台java -version显示17版本 2.下载并安装Nacos 下载地址&#xff1a;Releases alibaba/nacos GitHub 安装完本地解压 解压完到nacos的bin目录下&#xff0c;执行.\startup.cmd -m standalone启动即可。 用过好几种方式&#xff0c;比如启动startup…

一百四十五、Kettle——查看Kettle在Windows本地和在Linux上生成的.kettle文件夹位置

&#xff08;一&#xff09;目的 查看kettle连数据库后自动生成的.kettle文件夹在Windows本地和在Linux中的位置&#xff0c; 这个文件很重要&#xff01;&#xff01;&#xff01; &#xff08;二&#xff09;.kettle文件夹在Windows本地的位置 C:\Users\Administrator\.k…

ClickHouse SQL与引擎--基本使用(一)

1.查看所有的数据库 show databases; 2.创建库 CREATE DATABASE zabbix ENGINE Ordinary; ATTACH DATABASE ck_test ENGINE Ordinary;3.创建本地表 CREATE TABLE IF NOT EXISTS test01(id UInt64,name String,time UInt64,age UInt8,flag UInt8 ) ENGINE MergeTree PARTI…

英特尔傲腾CAS报错unknown error cache acceleration software could not start cache

英特尔傲腾CAS报错unknown error cache acceleration software could not start cache 文章目录 英特尔傲腾CAS报错unknown error cache acceleration software could not start cache我是怎么遇到这个问题的我是如何解决的实验步骤打Primo Cache蓝屏补丁拔掉原来的系统盘开关机…

【力扣】21. 合并两个有序链表 <链表指针>

【力扣】21. 合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;…

Java List(列表)

List 是一个有序、可重复的集合&#xff0c;集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素&#xff0c;可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引&#xff0c;第一个添加到 List 集合中的元素的索引为 0&#xff…