策略模式 订阅
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。 展开全文
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。
信息
外文名
Pattern:Strategy
环境角色
持有一个策略类的引用
抽象策略角色
由一个接口或者抽象类实现
中文名
策略模式
具体策略角色
包装了相关的算法和行为
策略模式简介
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。策略模式:
收起全文
精华内容
下载资源
问答
  • 策略模式
    千次阅读
    2020-12-15 15:22:19

    策略模式

    ​ 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

    ​ 在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。

    ​ 如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决这些问题。

    1.策略模式的定义与特点

    策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

    简述:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。

    策略模式的主要优点如下。

    1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
    2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
    3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
    4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
    5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

    其主要缺点如下。

    1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
    2. 策略模式造成很多的策略类。

    2.鸭子问题

    模拟鸭子项目,具体要求如下:

    1)有各种鸭子(比如 绿头鸭、红头鸭,鸭子有各种行为,比如叫、游泳)

    2)显示鸭子的信息

    传统方案解决鸭子问题的分析和代码实现

    1)传统的设计方案

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LReUNUvw-1608016555763)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20201214135952353.png)]

    2)代码实现

    .java文件说明:
    Duck.java:基类鸭子
    GreenHeadDuck.java:绿头鸭
    RedHeadDuck.java:红头鸭
    SimulateDuck.java:模拟鸭子(main)

    Duck.java

    //抽象类:鸭子
    public  abstract class Duck {
        public Duck(){
            //子类的构造函数中可以定义行为
        }
        //在本抽象类中已经实现了
        public void quack(){
            System.out.println("~~嘎嘎叫~~");
        }
        //由子类实现
        public abstract void display();
        //在本抽象类中自己已经实现了
        public void swim(){
            System.out.println("~~我会游泳~~");
        }
    
    }
    

    RedHeadDuck.java:

    public class RedHeadDuck extends Duck{
        @Override
        public void display() {
            System.out.println("我是独一无二的,我的头是红色的");
        }}
    

    GreenHeadDuck.java:

    public class GreenHeadDuck extends Duck {
        @Override
        public void display() {
            System.out.println("我和你们不一样,我的头是绿色的");
        }
    }
    

    SimulateDuck.java:

         GreenHeadDuck greenHeadDuck=new GreenHeadDuck();
            RedHeadDuck redHeadDuck=new RedHeadDuck();
    
            greenHeadDuck.display();
            greenHeadDuck.quack();
            greenHeadDuck.swim();
     
    
    
            redHeadDuck.display();
            redHeadDuck.quack();
            redHeadDuck.swim();
    

    我们已经实现了基本的项目需求了。模拟鸭子算是成功啦~!

    但是项目添加了新的需求,添加会飞的鸭子(并不是所有的鸭子都会飞)

    传统的方式实现的问题分析和解决方案

    • 如果在基类写Fly()方法,其他鸭子,都继承了Duck类,所以fly让所有子类都会飞了,违背逻辑。

    • 这个问题,是继承带来的问题,对类的局部改动,尤其超类的局部改动,会影响其他部分,会有溢出效应

    • 为了改进问题,我们可以通过覆盖fly方法来解决,

    • 问题又来了,如果我们有一个玩具鸭子,这样需要玩具鸭子去覆盖Duck的所有实现的方法 ,覆盖工作量特别大

    • 解决方法:策略模式(strategy pattern)思路:继承是实现共性,减少代码的重复。接口是实现特性。

    3.策略模式解决鸭子问题

    策略模式解决鸭子问题的分析

    1)设计方案

    需要新的设计方式,应对项目的扩展性,降低复杂度:
    a. 分析项目变化与不变部分,提取变化部分,抽象成接口+实现;(抽象是共性,接口是特性)
    b. 鸭子那些功能是会根据新需求变化的?叫声、飞行
    接口:

    public interface FlyBehavior{
    void fly();
    }
    public interface QuackBehavior{
    void quack();
    }
    

    c. 好处:新增行为简单,行为类更好的复用,组合方便。既有继承带来的复用好处,没有挖坑

    2)代码实现

    .java文件说明:
    Duck.java:基类鸭子
    GreenHeadDuck.java:绿头鸭
    RedHeadDuck.java:红头鸭
    SimulateDuck.java:模拟鸭子(main)
    FlyBehavior.java:(接口)特有的飞行行为
    QuackBehavior.java:(接口)特有的叫喊行为
    BadFlyBehavior.java:飞行行为的实现类
    BadQuackBehavio.java:叫喊行为的实现类

    Duck.java:

    //抽象类:鸭子
    public  abstract class Duck {
        FlyBehavior flyBehavior;
        QuackBehavior quackBehavior;
    
        public Duck(){
            //子类的构造函数中可以定义行为
        }
        //在本抽象类中已经实现了
        public void quack(){
    
            //System.out.println("~~嘎嘎叫~~");
            quackBehavior.quack();
        }
        //由子类实现
        public abstract void display();
        //在本抽象类中自己已经实现了
        public void swim(){
    
            System.out.println("~~我会游泳~~");
        }
        //实例化对象时可以动态的改变对象的行为(比继承灵活性强)
        public void SetFlyBehavior(FlyBehavior fb) {
    
            flyBehavior = fb;
        }
    
        //实例化对象时可以动态的改变对象的行为
        public void SetQuackBehavior(QuackBehavior qb) {
            quackBehavior = qb;
        }
        public void fly(){
            //System.out.println("飞");
            flyBehavior.fly();
        }
    }
    
    

    GreenHeadDuck.java:

    public class GreenHeadDuck extends Duck {
        public GreenHeadDuck(){
            //行为轴展示具体的行为
            flyBehavior = new BadFlyBehavior();
        }
        @Override
        public void display() {
            System.out.println("我和你们不一样,我的头是绿色的");
        }
        //覆盖超类
    //    public void fly(){
    //        System.out.println("我不会飞");
    //    }
    }
    
    

    RedHeadDuck.java:

    public class RedHeadDuck extends Duck {
        public RedHeadDuck(){
            quackBehavior = new BadQuackBehavior();
        }
        @Override
        public void display() {
            System.out.println("我是独一无二的,我的头是红色的");
        }}
    

    SimulateDuck.java:

    /**
     * 主类:模拟鸭子
     */
    public class SimulateDuck {
        public static void main(String[] args) {
    //父类为Duck,屏蔽了超类的差别性
           Duck greenHeadDuck = new GreenHeadDuck();
            Duck redHeadDuck=new RedHeadDuck();
    //        GreenHeadDuck greenHeadDuck = new GreenHeadDuck();
    //        RedHeadDuck redHeadDuck = new RedHeadDuck();
            greenHeadDuck.display();
            greenHeadDuck.fly();
            greenHeadDuck.SetQuackBehavior(new QuackBehavior() {
                @Override
                public void quack() {
                    System.out.println("我会叫");
                }
            });
            greenHeadDuck.swim();
    
            redHeadDuck.display();
            redHeadDuck.quack();
            redHeadDuck.swim();
            redHeadDuck.SetFlyBehavior(new FlyBehavior() {
                @Override
                public void fly() {
                    System.out.println("我会飞");
                }
            });
        }
    }
    

    FlyBehavior.java:

    public interface FlyBehavior {
        void fly();
        }
    

    QuackBehavior.java:

    public interface QuackBehavior {
        void quack();
    }
    

    BadFlyBehavior.java:

    public class BadFlyBehavior implements FlyBehavior{
        @Override
        public void fly() {
            System.out.println("我不会飞");
        }
    }
    

    BadQuackBehavio.java:

    public class BadQuackBehavior  implements QuackBehavior{
        @Override
        public void quack() {
            System.out.println("我不会叫");
        }
    }
    
    

    总结:运用设计模式中的策略模式,把变化的部分提取出来变为接口+实现。

    Duck类中的SetQuackBehavoir()方法,灵活的让实例化对象灵活的改变对象的行为。比如,绿头鸭,使用了SetQuackBehavoir()方法,定制了自己的quck()方法。因为不是每只鸭都能叫的。叫的是当前鸭的特性。

    4.设计原则

    策略模式体现了几个设计原则

    • 封装变化:把变化的代码从不变的代码中分离出来(找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起。)

    • 针对接口编程而不是具体类(定义了策略接口)

    • 多用组合/聚合,少用继承(客户通过组合方式使用策略)

    5.价格计算问题

    以一个价格计算策略为背景

    没有用策略模式

    我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现)

    public class NoStrategy {
        /**
         * 传入客服等级类型获取相应的价格
         * @param type   会员类型(等级)
         * @param price  响应的价格
         * @return
         */
        public double getPrice(String type, double price) {
    
            if ("普通客户小批量".equals(type)) {
                System.out.println("[未采用设计模式] 不打折,原价");
                return price;
            } else if ("普通客户大批量".equals(type)) {
                System.out.println("[未采用设计模式] 打九折");
                return price * 0.9;
            } else if ("老客户小批量".equals(type)) {
                System.out.println("[未采用设计模式] 打八折");
                return price * 0.8;
            } else if ("老客户大批量".equals(type)) {
                System.out.println("[未采用设计模式] 打七折");
                return price * 0.7;
    
    
                //拓展一种策略
       //     }else if("老客户特大批量".equals(type)){
       //        System.out.println("[未采用设计模式] 打六折");
       //         return price*0.6;
            }
    
    
            //乱传的也是当普通客户小批量(就是不打折)
            return price;
        }
    
    }
    
    • NoStrategy:没有策略的做法
    • 实现起来比较容易,符合一般开发人员的思路
    • 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护。
    • 如果有新增类型,就需要频繁的修改此处的代码!
    • 不符合开闭原则!—对这个类的修改要关闭,就是这个类要是写好了就不要去改他了,对类的功能的拓展要开放,显然只有面向接口编程才满足,
    • 所以应用策略模式Strategy这个接口

    6.策略模式解决价格计算问题

    1.写一个策略接口Strategy

    • Strategy:策略接口
    • 这个是对类NoStrategy改成面向接口的方式实现策略,不像NoStrategy一样,直接写死策略的实现,而是使用这个接口先定义策略
    public interface Strategy {
        /**
         * 通过策略获取价格
         * @param standardPrice
         * @return
         */
        double getPrice(double standardPrice);
    }
    

    2.面向接口,组合编程,少用继承(继承虽然可以复用代码,但是会造成耦合度增加,解决方式往往采用接口做类的属性),如下,这样所有实现Strategy 的各种策略实现类都"组合"到这个类里面了

    • Context:策略模式上下文—策略接收器,专门接收策略实现的算法
    • 负责和具体的策略类交互
    • 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
    • 如果使用spring的依赖注入功能,还可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法.
    public class Context {
        /**
         * 当前采用的算法对象
         * 面向接口,组合编程,少用继承
         * 简言之复杂类型(类,接口等)做属性
         */
        private Strategy strategy;
    
        public Context(Strategy strategy) {
    
            this.strategy = strategy;
        }
    
        public double getReultPrice(double price){
    
            return this.strategy.getPrice(price);
        }
    }
    

    3:既然是策略模式接口Strategy都明确了要做的事情是根据会员情况,返回价格,但是没有真正的实现,那么总有类来实现赛

    策略实现类1 VIP0Strategy

    /**
     * VIP0Strategy:普通会员策略
     */
    public class VIP0Strategy implements Strategy {
        /**
         * 输入一个价格,经过VIP0Strategy策略计算价格
         * @param standardPrice
         * @return
         */
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]普通会员 原价:"+standardPrice);
            return standardPrice;
        }
    }
    

    策略实现类2 VIP1Strategy

    /**
     * VIP1Strategy: 一级会员策略
     */
    public class VIP1Strategy implements Strategy {
        /**
         * 输入一个价格,经过VIP1Strategy策略计算价格
         * @param standardPrice
         * @return
         */
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]一级会员 打九折:"+standardPrice * 0.9);
            return standardPrice * 0.9;
        }
    
    }
    

    策略实现类3 VIP2Strategy

    /**
     * VIP2Strategy:二级会员策略
     */
    public class VIP2Strategy implements Strategy {
        /**
         * 输入一个价格,经过VIP2Strategy策略计算价格
         * @param standardPrice
         * @return
         */
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]二级会员八折:"+standardPrice*0.8);
            return standardPrice*0.8;
        }
    }
    

    策略实现类4 VIP3Strategy(新增加的需求)

    public class VIP3Strategy implements Strategy {
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]老客户特大批量:"+standardPrice*0.6);
            return standardPrice*0.6;
        }
    }
    

    4.客户端:

    • Client:策略模式客户端—Client 的main方法 可以想象成我们在使用别人写好的框架,我们有新的需求,对框架开发者来说就是需要对已有的
    • 代码进行维护升级,比如此时我们修改NoStrategy类,那么修改完后新版本的框架NoStrategy类很有能是对已经在使用的客户机制上不兼容的,如果用户升级为新版框架,遇到使用NoStrategy类的会报错,各种不兼容就不符合开发者维护的版本的规范,所以修改已有的类是极其不科学的
    
    import com.cx.price.NoStrategy;
    import com.cx.price.VIP1Strategy;
    
    
    public class Client {
        public static void main(String[] args) {
    
            System.out.println("未使用模式-----------------------------------------");
            NoStrategy noStrategy = new NoStrategy();
            double price = noStrategy.getPrice("普通客户大批量", 1000);
            System.out.println(price);
            System.out.println("\n测试策略------------------------------------------");
            Context context0 = new Context(new VIP1Strategy());
            double resultPrice = context0.getReultPrice(1000);
            System.out.println(resultPrice);
    //怎么体现策略模式呢?比如现在需求是增加一种会员机制,  '老客户特大批量' ,那么显然打折力度更大,我们设置为6折,
    // 分别在未使用策略模式和使用了策略模式的基础上拓展,看那个更加易于拓展,方便维护
            //为了实现这么一个折扣计算功能,代码需要写4个if-else,如果需求再增多一个规则,代码还需重构if-else,这样在可维护性、可读性大大降低,而且修改容易出bug。
            //如果运用策略模式,每个规则对应一个策略,根据符合的条件对应选择哪一种策略,这样整体代码逻辑清晰,而且不管新增或修改规则时,只需要新增或调整对应的规则策略,这样大大降低bug的风险,可维护性更高。
    
    //        //新增策略后未使用模式(会修该策略核心类)
    //        NoStrategy noStrategy1 = new NoStrategy();
    //        double price1 = noStrategy1.getPrice("老客户特大批量", 1000);
    //        System.out.println(price1);
    //
    //
    //        //新增策略后使用模式(不会修改策略接口,只是添加一个实现)
    //        Context context2 = new Context(new VIP3Strategy());
    //        double price2 = context2.getReultPrice(1000);
    //        System.out.println(price2);
    
        }
    }
    

    结论: 修改服务器端已经写好了的类是极其不好的维护形式,因为这个类NoStrategy可能在别的类中作为依赖或者叫做别的类引用了该类,在不明确的情况下,可能牵一发动全身,是不好的维护方式,使用了策略模式,我们只是添加了一个策略接口的实现,低侵入式,不会对已有代码造成影响,低耦合

    7.策略模式原理类图

    在这里插入图片描述

    说明:从上面这个图可以看出来客户context有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定

    策略模式的主要角色如下:

    1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
    2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
    3. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

    8.策略模式的总结

    1)策略模式的关键是 :分析项目中变化部分与不变部分
    2)策略模式的核心思想是 :多用组合/聚合,少用继承;用行为类组合,而不是行为的继承。更有弹性。
    3)体现了“开闭原则”(对修改关闭,对扩展开放)。客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if … else);
    4)提供了可以替换继承关心的办法 :策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
    5)需要注意的是 :每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大。

    更多相关内容
  • 策略模式

    千次阅读 2021-05-21 15:05:45
    策略模式其实很简单,就是针对不同的场景,做出不同的处理策略 (2)策略模式的优缺点: 优点: (1)可以砍掉代码中过多的if else if 和 switch case语句,使代码更加整洁优雅,一定程度上提高了性能。 (2) 新增...
    (1)是什么?

    策略模式其实很简单,就是针对不同的场景,做出不同的处理策略

    (2)策略模式的优缺点:

    优点:
    (1)可以砍掉代码中过多的if else if 和 switch case语句,使代码更加整洁优雅,一定程度上提高了性能。
    (2) 新增类型或者场景仅仅需要新增一个处理策略,不需要改动旧代码,遵循了开闭原则,更好维护。
    缺点:
    (1)如果场景过多,会产成过多的策略类,一定程度上增加了理解成本

    (3)怎么用?(案例演示)

    假设有一个需求,是根据传入的类型Type的不同,构造出不同的对象并且调用相对应的逻辑,这是一个很常见的日常需求,如果type较少,我们可能会直接写if … else if … else if …;type类型多,我们进一步考虑switch case,但是这些都可以用策略模式来优化
    如下面的例子,我要根据穿进去的type,决定是构建一个游泳池pool,还是一个花园garden,还是一个迷宫maze,代码可以写成:
    controller:

    @GetMapping("/strategy2/{type}")
        public String strategy2(@PathVariable String type){
            // 模拟容器,事先创建好对应的对象
            Map<String,MapConstruct> constructMap = new HashMap<>();
            constructMap.put("pool",new Pool());
            constructMap.put("garden",new Garden());
            constructMap.put("maze",new Maze());
            // 从容器中拿出实例好的对象并且调用它的方法
            MapConstruct map = constructMap.get(type);
            return map.constructMap(type);
        }
    

    策略接口:

    public interface MapConstruct {
        /**
         * 根据类型进行构建
         * @param type 类型
         * @return String 提示信息
         */
        String constructMap(String type);
    }
    

    接下来就写三个实现类实现这个接口即可。
    在这里插入图片描述

    请求一下

    在这里插入图片描述
    在这里插入图片描述

    成功

    其实我们创建Map或者List容器来装这一步,有点多余,我们用的SpringBoot的话,完全可以依靠IOC来帮我们,例如在实现类中加上@Service,注入ApplicationContext,通过它的getBean方法,这样容器refresh启动的时候就会自动装载那几个策略类进去,我们要用的话,从Spring容器拿出来就行,例如:

    @Autowired
        private ApplicationContext applicationContext;
    
    @GetMapping("/strategy/{type}")
        public String strategy(@PathVariable String type){
            MapConstruct map;
            try {
                map = (MapConstruct) applicationContext.getBean(type);
            }catch (NoSuchBeanDefinitionException e){
                return "请输入正确的类型";
            }
            return map.constructMap(type);
        }
    

    这个try catch最好还是加一下,不然当前端传过来不存在的type,就会出现找不到Bean的异常
    再测试一下:
    在这里插入图片描述

    成功
    展开全文
  • Java设计模式——策略模式

    万次阅读 多人点赞 2021-10-29 12:50:37
    策略模式 1.策略模式简介 策略模式策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解...

    策略模式

    1.策略模式简介

    策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化

    2.模式结构

    策略模式包含如下角色:

    • Strategy: 抽象策略类:策略是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法(如下图的algorithm())

    • Context: 环境类 /上下文类:

      • 上下文是依赖于接口的类(是面向策略设计的类,如下图Context类),即上下文包含用策略(接口)声明的变量(如下图的strategy成员变量)。
      • 上下文提供一个方法(如下图Context类中的的lookAlgorithm()方法),持有一个策略类的引用,最终给客户端调用。该方法委托策略变量调用具体策略所实现的策略接口中的方法(实现接口的类重写策略(接口)中的方法,来完成具体功能)
    • ConcreteStrategy: 具体策略类:具体策略是实现策略接口的类(如下图的ConcreteStrategyA类和ConcreteStrategyB类)。具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体方法。(说白了就是重写策略类的方法!)

    在这里插入图片描述

    3.案例

    在这里插入图片描述

    1).传统实现方式

    代码

        public Double calculationPrice(String type, Double originalPrice, int n) {
    
            //中级会员计费
            if (type.equals("intermediateMember")) {
                return originalPrice * n - originalPrice * 0.1;
            }
            //高级会员计费
            if (type.equals("advancePrimaryMember")) {
                return originalPrice * n - originalPrice * 0.2;
            }
            //普通会员计费
            return originalPrice;
        }
    

    传统的实现方式,通过传统if代码判断。这样就会导致后期的维护性非常差。当后期需要新增计费方式,还需要在这里再加上if(),也不符合设计模式的开闭原则。

    2).策略模式实现

    抽象类策略

    package StrategyExercise;
    
    public interface MemberStrategy {
        // 一个计算价格的抽象方法
        //price商品的价格 n商品的个数
        public double calcPrice(double price, int n);
    }
    
    

    具体实现类

    // 普通会员——不打折
    public class PrimaryMemberStrategy implements MemberStrategy { // 实现策略
        //重写策略方法具体实现功能
        @Override
        public double calcPrice(double price, int n) {
            return price * n;
        }
    }
    
    package StrategyExercise;
    
    // 中级会员 打百分之10的折扣
    public class IntermediateMemberStrategy implements MemberStrategy{
        @Override
        public double calcPrice(double price, int n) {
            double money = (price * n) - price * n * 0.1;
            return money;
        }
    }
    
    
    package StrategyExercise;
    
    // 高级会员类 20%折扣
    public class AdvanceMemberStrategy implements MemberStrategy{
        @Override
        public double calcPrice(double price, int n) {
            double money = price * n - price * n * 0.2;
            return money;
        }
    }
    
    

    上下文类

    也叫做上下文类或环境类,起承上启下封装作用。

    package StrategyExercise;
    
    /**
     * 负责和具体的策略类交互
     * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
     */
    
    // 上下文类/环境类
    public class MemberContext {
        // 用户折扣策略接口
        private MemberStrategy memberStrategy;
    
        // 注入构造方法
        public MemberContext(MemberStrategy memberStrategy) {
            this.memberStrategy = memberStrategy;
        }
    
        // 计算价格
        public double qoutePrice(double goodsPrice, int n){
            // 通过接口变量调用对应的具体策略
            return memberStrategy.calcPrice(goodsPrice, n);
        }
    
    }
    
    

    测试类

    package StrategyExercise;
    
    // 测试类
    public class Application {
        public static void main(String[] args) {
    
            // 具体行为策略
            MemberStrategy primaryMemberStrategy = new PrimaryMemberStrategy(); // 接口回调(向上转型)
            MemberStrategy intermediateMemberStrategy = new IntermediateMemberStrategy();
            MemberStrategy advanceMemberStrategy = new AdvanceMemberStrategy();
    
            // 用户选择不同策略
            MemberContext primaryContext = new MemberContext(primaryMemberStrategy);
            MemberContext intermediateContext = new MemberContext(intermediateMemberStrategy);
            MemberContext advanceContext = new MemberContext(advanceMemberStrategy);
    
            //计算一本300块钱的书
            System.out.println("普通会员的价格:"+ primaryContext.qoutePrice(300,1));// 普通会员:300
            System.out.println("中级会员的价格:"+ intermediateContext.qoutePrice(300,1));// 中级会员 270
            System.out.println("高级会员的价格:"+ advanceContext.qoutePrice(300,1));// 高级会员240
        }
    }
    
    

    运行结果

    普通会员的价格:300.0
    中级会员的价格:270.0
    高级会员的价格:240.0

    上述案例UML类图

    在这里插入图片描述

    4.策略模式优缺点

    1)优点

    • 策略模式提供了对“开闭原则”的完美支持,用户可以在不 修改原有系统的基础上选择算法或行为,也可以灵活地增加 新的算法或行为。

    • 策略模式提供了管理相关的算法族的办法。

    • 策略模式提供了可以替换继承关系的办法。

    • 使用策略模式可以避免使用多重条件转移语句。

    2)缺点

    • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
    • 策略模式将造成产生很多策略类,可以通过使用享元模式在一 定程度上减少对象的数量。

    5.策略模式适用场景

    在以下情况下可以使用策略模式:

    • 如果在一个系统里面有许多类,它们之间的区别仅在于它们 的行为,那么使用策略模式可以动态地让一个对象在许多行 为中选择一种行为。
    • 一个系统需要动态地在几种算法中选择一种。
    • 如果一个对象有很多的行为,如果不用恰当的模式,这些行 为就只好使用多重的条件选择语句来实现。
    • 不希望客户端知道复杂的、与算法相关的数据结构,在具体 策略类中封装算法和相关的数据结构,提高算法的保密性与 安全性。

    在我们生活中比较常见的应用模式有:

    1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式
    2、电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式

    在这里插入图片描述

    6.总结

    • 在策略模式中定义了一系列算法,将每一个算法封装起来,并让它们 可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为 政策模式。策略模式是一种对象行为型模式。

    • 策略模式包含三个角色:环境类在解决某个问题时可以采用多种策略, 在环境类中维护一个对抽象策略类的引用实例;抽象策略类为所支持 的算法声明了抽象方法,是所有策略类的父类;具体策略类实现了在 抽象策略类中定义的算法。

    • 策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派 给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的 策略类里面,作为一个抽象策略类的子类。

    • 策略模式主要优点在于对“开闭原则”的完美支持,在不修改原有系 统的基础上可以更换算法或者增加新的算法,它很好地管理算法族, 提高了代码的复用性,是一种替换继承,避免多重条件转移语句的 实现方式;其缺点在于客户端必须知道所有的策略类,并理解其区 别,同时在一定程度上增加了系统中类的个数,可能会存在很多策 略类

    • 策略模式适用情况包括:在一个系统里面有许多类,它们之间的区 别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多 行为中选择一种行为;一个系统需要动态地在几种算法中选择一种; 避免使用难以维护的多重条件选择语句;希望在具体策略类中封装 算法和与相关的数据结构。

    注:如果文章有任何错误或不足,请各位大佬尽情指出,评论留言留下您宝贵的建议!如果这篇文章对你有些许帮助,希望可爱亲切的您点个赞推荐一手,非常感谢啦

    image

    展开全文
  • 策略模式可以使算法可独立于使用它的客户而变化 策略模式变化的是算法 二、策略模式结构图   三、策略模式中主要角色 抽象策略(Strategy)角色:定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现。...
  • 策略模式1

    2022-08-08 18:42:16
    策略(Strategy)设计模式目录 策略(Strategy)设计模式 1 一、 模式讲解 2 认识策略模式 2 (1)、 策略模式的功能 2 (2)、 策略模
  • 大话设计模式——策略模式

    千次阅读 2022-04-05 22:55:24
    1)商场收银系统 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JFrame;...

    1)商场收银系统

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.DefaultListModel;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    /**
      * 商场收银系统
     */
    @SuppressWarnings("serial")
    public class Mall implements ActionListener {
    
        private static JLabel unitPriceLabel;
        private static JTextField unitPriceValue;
        private static JLabel numLabel;
        private static JTextField numValue;
    
        private double totalPrice = 0;
        private static JList<String> jList;
        private static DefaultListModel<String> listModel;
        private static JLabel totalNum;
    
        public static void main(String[] args) {
            JFrame frame = new JFrame("商城收银系统");
            frame.setSize(450, 400);
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel();
            frame.add(panel);
            placeComponents(panel);
            frame.setVisible(true);
        }
    
        private static void placeComponents(JPanel panel) {
            panel.setLayout(null);
            Mall mainFrame = new Mall();
            // 第一行
            unitPriceLabel = new JLabel("单价:");
            unitPriceLabel.setBounds(10, 20, 80, 25);
            panel.add(unitPriceLabel);
    
            unitPriceValue = new JTextField(20);
            unitPriceValue.setBounds(100, 20, 165, 25);
            panel.add(unitPriceValue);
    
            JButton confirmButton = new JButton("确定");
            confirmButton.setBounds(280, 20, 80, 25);
            confirmButton.addActionListener(mainFrame);
            confirmButton.setActionCommand("confirm");
            panel.add(confirmButton);
    
            // 第二行
            numLabel = new JLabel("数量:");
            numLabel.setBounds(10, 50, 80, 25);
            panel.add(numLabel);
    
            numValue = new JTextField(20);
            numValue.setBounds(100, 50, 165, 25);
            panel.add(numValue);
    
            JButton resetButton = new JButton("重置");
            resetButton.setBounds(280, 50, 80, 25);
            resetButton.addActionListener(mainFrame);
            resetButton.setActionCommand("reset");
            panel.add(resetButton);
    
            // 第三行
            listModel = new DefaultListModel<String>();
            jList = new JList<String>(listModel);
            jList.setBounds(10, 90, 400, 180);
            panel.add(jList);
    
            // 第四行
            JLabel totalLabel = new JLabel("总计:");
            totalLabel.setBounds(10, 300, 80, 25);
            panel.add(totalLabel);
            totalNum = new JLabel("0.00");
            totalNum.setBounds(100, 300, 80, 25);
            panel.add(totalNum);
        }
    
        @Override
        public void actionPerformed(ActionEvent event) {
            if (event.getActionCommand().equals("confirm")) {
                double unitPrice = Double.parseDouble(unitPriceValue.getText());
                double num = Double.parseDouble(numValue.getText());
                double total = unitPrice * num;
                listModel.addElement("单价:"+unitPriceValue.getText()+" 数量:"+numValue.getText() +" 合计:"+total);
                totalPrice +=total;
                totalNum.setText(totalPrice+"");
            } else if(event.getActionCommand().equals("reset")) {
                unitPriceValue.setText("");
                numValue.setText("");
                listModel.clear();
                totalPrice=0;
                totalNum.setText(totalPrice+"");
            }
        }
    
    }
    
    

    执行效果

    在这里插入图片描述

    2)增加打折功能

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.DefaultListModel;
    import javax.swing.JButton;
    import javax.swing.JComboBox;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    /**
     * 需求:增加打折功能
     */
    @SuppressWarnings("serial")
    public class Mall implements ActionListener {
    
    	private static JLabel unitPriceLabel;
    	private static JTextField unitPriceValue;
    	private static JLabel numLabel;
    	private static JTextField numValue;
    
    	private double totalPrice = 0;
    	private static JList<String> jList;
    	private static DefaultListModel<String> listModel;
    	private static JLabel totalNum;
    	private static JLabel calcType;
    	private static JComboBox<String> jComboBox;
    
    	public static void main(String[] args) {
    		JFrame frame = new JFrame("商城收银系统");
    		frame.setSize(450, 400);
    		frame.setLocationRelativeTo(null);
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		JPanel panel = new JPanel();
    		frame.add(panel);
    		placeComponents(panel);
    		frame.setVisible(true);
    	}
    
    	private static void placeComponents(JPanel panel) {
    		panel.setLayout(null);
    		Mall mainFrame = new Mall();
    		// 第一行
    		unitPriceLabel = new JLabel("单价:");
    		unitPriceLabel.setBounds(10, 20, 80, 25);
    		panel.add(unitPriceLabel);
    
    		unitPriceValue = new JTextField(20);
    		unitPriceValue.setBounds(100, 20, 165, 25);
    		panel.add(unitPriceValue);
    
    		JButton confirmButton = new JButton("确定");
    		confirmButton.setBounds(280, 20, 80, 25);
    		confirmButton.addActionListener(mainFrame);
    		confirmButton.setActionCommand("confirm");
    		panel.add(confirmButton);
    
    		// 第二行
    		numLabel = new JLabel("数量:");
    		numLabel.setBounds(10, 50, 80, 25);
    		panel.add(numLabel);
    
    		numValue = new JTextField(20);
    		numValue.setBounds(100, 50, 165, 25);
    		panel.add(numValue);
    
    		JButton resetButton = new JButton("重置");
    		resetButton.setBounds(280, 50, 80, 25);
    		resetButton.addActionListener(mainFrame);
    		resetButton.setActionCommand("reset");
    		panel.add(resetButton);
    
    		// 第三行
    		calcType = new JLabel("计算方式:");
    		calcType.setBounds(10, 90, 80, 25);
    		panel.add(calcType);
    		jComboBox = new JComboBox<String>();
    		jComboBox.insertItemAt("正常收费", 0);
    		jComboBox.insertItemAt("打八折", 1);
    		jComboBox.insertItemAt("打七折", 2);
    		jComboBox.insertItemAt("打五折", 3);
    		jComboBox.setSelectedIndex(0);
    		jComboBox.setBounds(100, 90, 80, 25);
    		panel.add(jComboBox);
    
    		// 第四行
    		listModel = new DefaultListModel<String>();
    		jList = new JList<String>(listModel);
    		jList.setBounds(10, 120, 400, 180);
    		panel.add(jList);
    
    		// 第五行
    		JLabel totalLabel = new JLabel("总计:");
    		totalLabel.setBounds(10, 300, 80, 25);
    		panel.add(totalLabel);
    		totalNum = new JLabel("0.00");
    		totalNum.setBounds(100, 300, 80, 25);
    		panel.add(totalNum);
    	}
    
    	@Override
    	public void actionPerformed(ActionEvent event) {
    		if (event.getActionCommand().equals("confirm")) {
    			double unitPrice = Double.parseDouble(unitPriceValue.getText());
    			double num = Double.parseDouble(numValue.getText());
    			int selectedIndex = jComboBox.getSelectedIndex();
    			double total = 0;
    			switch (selectedIndex) {
    			case 0:
    				total = unitPrice * num;
    				break;
    			case 1:
    				total = unitPrice * num * 0.8;
    				break;
    			case 2:
    				total = unitPrice * num * 0.7;
    				break;
    			case 3:
    				total = unitPrice * num * 0.5;
    				break;
    			}
    			totalPrice += total;
    			listModel.addElement("单价:" + unitPriceValue.getText() + " 数量:" + numValue.getText() + " "
    					+ jComboBox.getSelectedItem() + " 合计:" + total);
    			totalNum.setText(totalPrice + "");
    		} else if (event.getActionCommand().equals("reset")) {
    			unitPriceValue.setText("");
    			numValue.setText("");
    			listModel.clear();
    			totalPrice = 0;
    			totalNum.setText(totalPrice + "");
    		}
    	}
    
    }
    
    

    执行效果

    在这里插入图片描述

    3)简单工厂实现

    问题:如果增加满300返100的促销算法,该如何处理?

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.DefaultListModel;
    import javax.swing.JButton;
    import javax.swing.JComboBox;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    
    @SuppressWarnings("serial")
    public class Mall implements ActionListener {
    
        private static JLabel unitPriceLabel;
        private static JTextField unitPriceValue;
        private static JLabel numLabel;
        private static JTextField numValue;
    
        private double totalPrice = 0;
        private static JList<String> jList;
        private static DefaultListModel<String> listModel;
        private static JLabel totalNum;
        private static JLabel calcType;
        private static JComboBox<String> jComboBox;
    
        public static void main(String[] args) {
            JFrame frame = new JFrame("商城收银系统");
            frame.setSize(450, 400);
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel();
            frame.add(panel);
            placeComponents(panel);
            frame.setVisible(true);
        }
    
        private static void placeComponents(JPanel panel) {
            panel.setLayout(null);
            Mall mainFrame = new Mall();
            // 第一行
            unitPriceLabel = new JLabel("单价:");
            unitPriceLabel.setBounds(10, 20, 80, 25);
            panel.add(unitPriceLabel);
    
            unitPriceValue = new JTextField(20);
            unitPriceValue.setBounds(100, 20, 165, 25);
            panel.add(unitPriceValue);
    
            JButton confirmButton = new JButton("确定");
            confirmButton.setBounds(280, 20, 80, 25);
            confirmButton.addActionListener(mainFrame);
            confirmButton.setActionCommand("confirm");
            panel.add(confirmButton);
    
            // 第二行
            numLabel = new JLabel("数量:");
            numLabel.setBounds(10, 50, 80, 25);
            panel.add(numLabel);
    
            numValue = new JTextField(20);
            numValue.setBounds(100, 50, 165, 25);
            panel.add(numValue);
    
            JButton resetButton = new JButton("重置");
            resetButton.setBounds(280, 50, 80, 25);
            resetButton.addActionListener(mainFrame);
            resetButton.setActionCommand("reset");
            panel.add(resetButton);
    
            // 第三行
            calcType = new JLabel("计算方式:");
            calcType.setBounds(10, 90, 80, 25);
            panel.add(calcType);
            jComboBox = new JComboBox<String>();
            jComboBox.insertItemAt("正常收费", 0);
            jComboBox.insertItemAt("打8折", 1);
            jComboBox.insertItemAt("满300返100", 2);
            jComboBox.setSelectedIndex(0);
            jComboBox.setBounds(100, 90, 80, 25);
            panel.add(jComboBox);
    
            // 第四行
            listModel = new DefaultListModel<String>();
            jList = new JList<String>(listModel);
            jList.setBounds(10, 120, 400, 180);
            panel.add(jList);
    
            // 第五行
            JLabel totalLabel = new JLabel("总计:");
            totalLabel.setBounds(10, 300, 80, 25);
            panel.add(totalLabel);
            totalNum = new JLabel("0.00");
            totalNum.setBounds(100, 300, 80, 25);
            panel.add(totalNum);
        }
    
        @Override
        public void actionPerformed(ActionEvent event) {
            if (event.getActionCommand().equals("confirm")) {
                double unitPrice = Double.parseDouble(unitPriceValue.getText());
                double num = Double.parseDouble(numValue.getText());
                CashSuper cashSuper = CashFactory.createashAccept(jComboBox.getSelectedItem().toString());
                double total = cashSuper.acceptCash(unitPrice * num);
                totalPrice += total;
                listModel.addElement("单价:" + unitPriceValue.getText() + " 数量:" + numValue.getText() + " "
                        + jComboBox.getSelectedItem() + " 合计:" + total);
                totalNum.setText(totalPrice + "");
            } else if (event.getActionCommand().equals("reset")) {
                unitPriceValue.setText("");
                numValue.setText("");
                listModel.clear();
                totalPrice = 0;
                totalNum.setText(totalPrice + "");
            }
        }
    
    }
    
    /**
     * 现金收费抽象类
     */
    public abstract class CashSuper {
    	
    	/**
     	 *  现金收取超类的抽象方法,收取现金,参数为原价,返回为当前价
    	 */
    	public abstract double acceptCash(double money);
    }
    
    /**
     * 正常收费子类
     */
    public class CashNormal extends CashSuper{
    	
    	/**
     	 * 正常收费,原价返回
    	 */
    	public double acceptCash(double money) {
    		return money;
    	}
    }
    
    /**
     * 打折收费子类
     */
    public class CashRebate extends CashSuper {
    
    	private double moneyRebate = 1d;
    
    	public CashRebate(String moneyRebate) {
    		this.moneyRebate = Double.parseDouble(moneyRebate);
    	}
    
    	/**
    	 * 打折收费,初始化时,必须要输入折扣率,如八折,就是0.8
    	 */
    	public double acceptCash(double money) {
    		return money * moneyRebate;
    	}
    }
    
    /**
     * 返利收费子类
     */
    public class CashReturn extends CashSuper {
    
    	private double moneyCondition = 0.0d;
    	private double moneyReturn = 0.0d;
    
    	/**
    	 * 初始化时必须要输入返利条件和返回值,比如满300返100
    	 * 
    	 * @param moneyCondition 300
    	 * @param moneyReturn    100
    	 */
    	public CashReturn(String moneyCondition, String moneyReturn) {
    		this.moneyCondition = Double.parseDouble(moneyCondition);
    		this.moneyReturn = Double.parseDouble(moneyReturn);
    	}
    
    	public double acceptCash(double money) {
    		double result = money;
    		// 若大于返利条件,则需要减去返利值
    		if (money >= moneyCondition) {
    			result = money - Math.floor(money / moneyCondition) * moneyReturn; // 向下取整
    		}
    		return result;
    	}
    }
    
    
    
    /**
     * 收费对象生成工厂
     */
    public class CashFactory {
    	public static CashSuper createashAccept(String type) {
    		CashSuper cs = null;
    		switch (type) {
    		case "正常收费":
    			cs = new CashNormal();
    			break;
    		case "满300返100":
    			cs = new CashReturn("300", "100");
    			break;
    		case "打8折":
    			cs = new CashRebate("0.8");
    			break;
    		}
    		return cs;
    	}
    }
    

    执行效果

    在这里插入图片描述

    问题:如果增加满100积分10点,该如何做?
    1)增加一个积分算法,构造方法有两个参数:条件和返点
    2)在收费对象生成工厂里增加满100积分10点的分支条件,再到界面稍加改动
    *
    简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费模式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,所以这不是最好的办法,面对算法的时常变动,应该使用策略模式。
    商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法,这没有错,但算法本身只是一种策略,最重要的是这些算法随时都可能互相替换,这就是变化点,而封装变化点是面向对象很重要的思维方式。

    4)策略模式

    /**
     * 抽象算法类
     */
    public abstract class Strategy {
    	// 算法方法
    	public abstract void AlgorithmInterface();
    }
    /**
     * 具体算法A
     */
    public class ConcreteStrategyA extends Strategy{
    
    	@Override
    	public void AlgorithmInterface() {
    		System.out.println("算法A实现");
    	}
    }
    /**
     * 具体算法B
     */
    public class ConcreteStrategyB extends Strategy{
    
    	@Override
    	public void AlgorithmInterface() {
    		System.out.println("算法B实现");
    	}
    }
    /**
     * 具体算法C
     */
    public class ConcreteStrategyC extends Strategy{
    
    	@Override
    	public void AlgorithmInterface() {
    		System.out.println("算法C实现");
    	}
    }
    
    /**
     * 上下文
     */
    public class Context {
    	Strategy strategy;
    	// 初始化时,传入具体的策略对象
    	public Context(Strategy strategy) {
    		this.strategy = strategy;
    	}
    	// 上下文接口
    	public void ContextInterface() {
    		// 根据具体的策略对象,调用其算法的方法
    		strategy.AlgorithmInterface();
    	}
    }
    

    客户端

    /**
     * 客户端对象
     */
    public class Client {
    	public static void main(String[] args) {
    		// 由于实例化不同的策略,所以最终在调用context.ContextInterface()时,所获得的结果就不尽相同
    		Context context = new Context(new ConcreteStrategyA());
    		context.ContextInterface();
    
    		context = new Context(new ConcreteStrategyB());
    		context.ContextInterface();
    		
    		context = new Context(new ConcreteStrategyC());
    		context.ContextInterface();
    	}
    }
    

    5)策略模式实现

    只需要增加一个CashContext 类,再改写一下界面即可。

    public class CashContext {
        private CashSuper cs;
        // 通过构造方法,传入具体的收费策略
        public CashContext(CashSuper csuper) {
            this.cs = csuper;
        }
        public double getResult(double money) {
            // 根据收费策略的不同,获得计算结果
            return cs.acceptCash(money);
        }
    }
    

    界面逻辑修改

    if (event.getActionCommand().equals("confirm")) {
    			double unitPrice = Double.parseDouble(unitPriceValue.getText());
    			double num = Double.parseDouble(numValue.getText());
    			CashContext cashContext = null;
    			// 根据下拉选择框,将相应的策略对象作为参数传入CashContext的对象中
    	        switch (jComboBox.getSelectedItem().toString()) {
    	        case "正常收费":
    	            cashContext = new CashContext(new CashNormal());
    	            break;
    	        case "满300返100":
    	            cashContext = new CashContext(new CashReturn("300", "100"));
    	            break;
    	        case "打8折":
    	            cashContext = new CashContext(new CashRebate("0.8"));
    	            break;
    	        }
    	        // 通过对Context的getResult方法的调用,可以得到收取费用的结果,让具体算法与客户进行了隔离。
    			double total = cashContext.getResult(unitPrice*num);
    			totalPrice += total;
    			listModel.addElement("单价:" + unitPriceValue.getText() + " 数量:" + numValue.getText() + " "
    					+ jComboBox.getSelectedItem() + " 合计:" + total);
    			totalNum.setText(totalPrice + "");
    		} 
    

    执行效果

    在这里插入图片描述

    6)策略模式+简单工厂

    将界面中判断的过程转义到CashContext中。

    public class CashContext {
        private CashSuper cs;
        // 注意参数不是具体的收费策略对象,而是一个字符串,表示收费类型
        public CashContext(String type) {
            switch (type) {
            case "正常收费":
                // 将实例化具体策略的过程由客户端转移到Context类中,简单工厂的应用
                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);
        }
    }
    

    界面代码

    if (event.getActionCommand().equals("confirm")) {
    			double unitPrice = Double.parseDouble(unitPriceValue.getText());
    			double num = Double.parseDouble(numValue.getText());
    			CashContext cashContext = new CashContext(jComboBox.getSelectedItem().toString());
    			double total = cashContext.getResult(unitPrice*num);
    			totalPrice += total;
    			listModel.addElement("单价:" + unitPriceValue.getText() + " 数量:" + numValue.getText() + " "
    					+ jComboBox.getSelectedItem() + " 合计:" + total);
    			totalNum.setText(totalPrice + "");
    		} 
    

    简单工厂与策略模式的区别

    // 简单工厂模式
    CashSuper cashSuper = CashFactory.createashAccept(jComboBox.getSelectedItem().toString());
    double total = cashSuper.acceptCash(unitPrice*num);
    
    // 策略模式与简单工厂结合的用法
    CashContext cashContext = new CashContext(jComboBox.getSelectedItem().toString());
    double total = cashContext.getResult(unitPrice*num);
    

    简单工厂模式,客户端需要认识CashSuper和CashFactory两个类。
    而策略模式与简单工厂结合的用法,客户端只需要认识一个类CashContext就可以了。耦合更加降低。

    策略模式解析

    策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

    策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。

    策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。

    当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。即策略模式封装了变化。

    策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

    在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化地减轻了客户端的职责。

    注:本文内容源自程杰的《大话设计模式》

    展开全文
  • 设计模式之策略模式

    千次阅读 2022-01-27 13:31:11
    java程序的设计原则 6大原则: 单一职责:一个类和方法只做一件事。 开闭:对修改关闭,对扩展开发。...策略设计模式一般使用的场景是,多种可互相替代的同类行为,在具体运行过程中根据不同情况,选择其中
  • 策略模式和责任链模式

    千次阅读 2021-09-19 16:23:25
    一、策略模式 策略模式( Strategy Pattern )又叫也叫政策模式( Policy Pattern) ,它是将定义的算法家族、分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户。属于行为型模式。 原文: ...
  • js设计模式之策略模式

    千次阅读 2022-01-10 09:55:07
    认识策略模式 策略模式的定义:定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换。 策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成。第一部分是策略类,封装了许多具体...
  • 这次介绍一下策略模式(Strategy Pattern),相比之下是一种比较简单的模式。它也叫政策模式(Policy Pattern)。 策略模式使用的就是面向对象的继承和多态机制,其他的没有什么玄机。策略模式适合使用在: 1. 多个...
  • if else终结者——策略模式

    千次阅读 多人点赞 2021-05-28 10:48:15
    你是不是还在写着大量的if else语句,if else 不仅难以维护不易扩展,而且使代码臃肿不堪,想不想让你的业务代码更加的健壮,更易扩展,那你一定要学一学今天的主角策略模式。学会了策略模式的使用让会你的代码更加...
  • C#设计模式-策略模式练习,案例简单易懂,带注释
  • Java 设计模式之策略模式

    千次阅读 2021-11-20 20:09:42
    一、了解策略模式 1.1 什么是策略模式 策略模式 (Strategy Pattern) 是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。此模式让算法的变化独立于使用算法的客户。 1.2 策略模式组成...
  • 策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 策略模式java-demo 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 ...
  • 23种模式——策略模式

    千次阅读 2022-04-06 19:37:50
    策略(Strategy)模式本质:分离算法,选择实现。
  • java策略模式

    千次阅读 多人点赞 2019-04-09 18:19:48
    在说策略模式之前,先看一个小例子: 假如:有一场演讲比赛,有十个评委对参赛选手的成绩进行打分,但最终要通过评委的平均分来决定选手的名次。现在有两种求平均分的策略: 第一种:将十名裁判的分加起来求平均值...
  • 文章目录前言一、定义二...在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入
  • python设计模式之策略模式

    千次阅读 2020-07-04 18:52:56
    python设计模式之策略模式 大多数问题都可以使用多种方法来解决。以排序问题为例,对于以一定次序把元素放入一个列表,排序算法有很多。通常来说,没有公认最适合所有场景的算法一些不同的评判标准能帮助我们为不同...
  • 设计模式 模板模式和策略模式

    千次阅读 2022-03-19 20:17:12
    模板模式和策略模式,有相识的地方,都是通过对钩子方法的调用,来实现一个业务的完整逻辑。 所以这里我将两种模式放在一起介绍,比较容易加深对这两种模式的理解。 模板模式 在模板模式(Template Pattern)中,一...
  • 策略模式介绍及其具体使用场景

    千次阅读 2021-11-12 17:44:10
    文章目录前言一、策略模式介绍二、具体使用场景1、举例2、传统方式优化3、Map + 函数式编程 优化总结 前言 今天用策略模式优化了下之前的业务代码,重新温习了下设计模式的相关理念,在此记录一下 一、策略模式...
  • springboot服务使用策略模式

    千次阅读 2022-05-06 18:31:28
    策略模式能够帮助我们很好的解决这个问题。下面以一个简单的例子说明如何在sprinboot项目中使用策略模式。 2、消息发送策略模式搭建 2.1、定义一个消息发送接口 public interface MsgSend { void sendMessage...
  • 前言 策略模式 —— 模板模式的改进 解决 改进 状态模式 switch 常规写法 改进 实际应用 类泛滥 状态转换关系复杂的问题 后记
  • 策略模式(Strategy)一、使用策略模式的原因二、策略模式的定义三、策略模式的使用说明四、策略模式的实现范例五、中介者模式的优点和注意事项六、策略模式的总结 一、使用策略模式的原因 在设计游戏角色伤害时,因...
  • 原来使用 Spring 实现策略模式可以这么简单!

    千次阅读 多人点赞 2021-02-01 11:07:36
    策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 if-else。比如我们生活中的场景:买东西结账可以使用微信支付、支付宝...
  • 采用工厂模式 + 策略模式 定义策略接口: package com.xhwl.smarthome.service; /** * @author wangxinyu * * @date 2021/11/19 * 设备注册策略接口 */ public interface DeviceRegisterStrategy { Object ...
  • Spring实现策略模式

    千次阅读 2021-07-17 10:07:50
    Spring实现策略模式 当程序中出现太多的if/else/switch来处理不同类型的业务时,会变得极难维护,通过策略模式可以更容易的实现业务开发。 什么是策略模式 比如说对象的某个行为,在不同场景中有不同的实现方式,...
  • 策略模式:精妙替代你的if-else

    千次阅读 2022-01-05 17:59:39
    何为策略模式策略模式,作为一种常用的设计模式,其通过策略类、方法函数的方式封装不同场景的函数。从而对上层调用,可以屏蔽掉因为入参、场景的差异而导致的区别。对于内层逻辑,可以根据不同的场景执行不同的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 828,274
精华内容 331,309
关键字:

策略模式