精华内容
下载资源
问答
  • 2019-04-19 00:16:52

    一句话概括:

    一个类的行为或算法可以在运行时更改。

    补充介绍:

    策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式。

    Strategy的意思是“策略”,指的是与敌军对垒时行军作战的方法。

    在策略模式中,我们创建各种表示策略的对象一个行为随着策略对象改变而改变的context对象。策略对象改变context对象的执行算法。

    定义一系列的算法,把策略对象一个个封装起来, 并且使它们可相互替换。

    主要解决在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

    参与角色:

    1)策略类的抽象基类(也可以是接口)拥有不同策略类的共有方法

    2)各种策略实现类

    3)Context类(持有策略基类的对象,可以动态随着策略对象的改变而改变行为)

    优点:

    1)算法可以自由切换。

    2)避免使用多重条件判断。

    3)扩展性良好。

    缺点:

    1)策略类会增多。

    2)所有策略类都需要对外暴露。

    使用案例或场景:

    1) 诸葛亮的锦囊妙计,每一个锦囊就是一个策略。

    2)旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。

    3)JAVA AWT 中的 LayoutManager。

    示例程序

    需要源码的朋友可以前往github下载:

    https://github.com/aharddreamer/chendong/tree/master/design-patterns/demo-code/design-patterns

    程序简介:

    下面这个程序的功能是让电脑玩猜拳游戏。我们考虑了两种猜拳策略,第一种策略是“如果这局获胜,那么下局也出一样的手势” (VinningStrategy),这是一种稍微有些笨的策略;另一种策略是“根据上一局的手势从概率上计算出下一局的手势” (ProbStrategy)。

    示例程序类/接口一览:

    Hand 表示猜拳游戏中的“手势”的类(工具)

    Strategy 表示猜拳游戏中的策略的类 (策略类的基类)

    WinningStrategy 表示“如果这局获胜,那么下局也出一样的手势” 这一策略的类 (具体策略对象1)

    ProbStrategy 表示“根据上一局的手势从概率上计算出下一局的手势” 这一策略的类 (具体策略对象2)

    Player 表示进行猜拳游戏的类 (Context环境类)

    StrategyPatternTest 测试程序的类

     

    代码:

    public class Hand {
        public static final int SHI_TOU = 0; //表示石头的值
        public static final int JIAN_DAO = 1; //表示石头的值
        public static final int BU = 2; //表示石头的值
        public static final Hand[] hand = {
                new Hand(SHI_TOU), new Hand(JIAN_DAO), new Hand(BU)
        };
        public static final String[] name = {
                "石头", "剪刀", "布"
        };
        private int handValue;
        private Hand (int handValue) {
            this.handValue = handValue;
        }
        public boolean isStrongerThan(Hand hand) {
            return fight(hand) == -1;
        }
        public int fight(Hand hand) {
            if (this == hand) {
                return 0;
            }else if ((this.handValue + 1) % 3 == hand.handValue) {
                return 1;
            }else {
                return -1;
            }
        }
        public String toString() {
            return name[this.handValue];
        }
        public static Hand getHand(int handValue) {
            return hand[handValue];
        }
        public boolean isWeakerThan(Hand hand) {
            return fight(hand) == -1;
        }
    }
    
    
    
    public interface Strategy {
        Hand nextHand();
        void study(boolean vin);
    }
    
    
    public class WinningStrategy implements Strategy {
        private Random random;
        private boolean won = false;
        private Hand preHand;
        public WinningStrategy(int seed) {
            random = new Random(seed);
        }
        public Hand nextHand() {
            if (!won) {
                preHand = Hand.getHand(random.nextInt(3));
            }
            return preHand;
        }
        public void study(boolean win) {
            won = win;
        }
    }
    
    public class Player {
        private String name;
        private Strategy strategy;
        private int wincount;
        private int losecount;
        private int gamecount;
        //赋予姓名和策略
        public Player(String name, Strategy strategy) {
            this.name = name;
            this.strategy = strategy;
        }
    
        //策略决定下一句要出的手势
        public Hand nextHand() {
            return strategy.nextHand();
        }
        public void win() {
            strategy.study(true);
            wincount++;
            gamecount++;
        }
        public void lose() {
            strategy.study(false);
            losecount++;
            gamecount++;
        }
        public void even() {
            gamecount++;
        }
    
        @Override
        public String toString() {
            return "Player{" +
                    "name='" + name + '\'' +
                    ", strategy=" + strategy +
                    ", wincount=" + wincount +
                    ", losecount=" + losecount +
                    ", gamecount=" + gamecount +
                    '}';
        }
    }
    
    
    public class ProbStrategy implements Strategy {
        private Random random;
        private int preHandValue = 0;
        private int currentHandValue = 0;
        private int[][] history = {
                {1, 1, 1, },
                {1, 1, 1, },
                {1, 1, 1, },
        };
        public ProbStrategy(int seed) {
            random = new Random(seed);
        }
        public Hand nextHand() {
            int bet = random.nextInt(getSum(currentHandValue));
            int handValue = 0;
            if (bet < history[currentHandValue][0]) {
                handValue = 0;
            }else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
                handValue = 1;
            }else {
                handValue = 2;
            }
            preHandValue = currentHandValue;
            currentHandValue = handValue;
            return Hand.getHand(handValue);
        }
        private int getSum(int hv) {
            int sum = 0;
            for (int i = 0; i < 3 ; i++) {
                sum += history[hv][i];
            }
            return sum;
        }
        public void  study(boolean win) {
            if (win) {
                history[preHandValue][currentHandValue] ++;
            }else {
                history[preHandValue][(currentHandValue + 1) % 3] ++;
                history[preHandValue][(currentHandValue + 2) % 3] ++;
            }
        }
    }
    
    public class StrategyPatternTest {
        public static void main(String[] args) {
            //可以选择动态确定seed
            /*if (args.length != 2) {
                System.out.println("Usage: java Main randomseed1 randomseed2");
                System.out.println("Example: java Main 314 15");
                System.exit(0);
            }
            int seed1 = Integer.parseInt(args[0]);
            int seed2 = Integer.parseInt(args[1]);*/
            int seed1 = 314;
            int seed2 = 15;
            Player player1 = new Player("WinningStrategyPlayer", new WinningStrategy(seed1));
            Player player2 = new Player("ProbStrategyPlayer", new ProbStrategy(seed2));
            for (int i = 0; i < 1000 ; i++) {
                Hand nextHand1 = player1.nextHand();
                Hand nextHand2 = player2.nextHand();
                if (nextHand1.isStrongerThan(nextHand2)) {
                    System.out.println("Winner: " + player1);
                    player1.win();
                    player2.lose();
                }else if (nextHand2.isStrongerThan(nextHand1)) {
                    System.out.println("Winner: " + nextHand2);
                    player1.lose();
                    player2.win();
                }else {
                    System.out.println("Even...");
                    player1.even();
                    player2.even();
                }
            }
            System.out.println("Total Result: ");
            System.out.println(player1.toString());
            System.out.println(player2.toString());
        }
    }
    
    

    运行结果:

    Winner: Player{name='WinningStrategyPlayer', strategy=org.cd.designpatterns.strategy.WinningStrategy@2ff4acd0, wincount=310, losecount=330, gamecount=993}

    Winner: 石头

    Winner: Player{name='WinningStrategyPlayer', strategy=org.cd.designpatterns.strategy.WinningStrategy@2ff4acd0, wincount=311, losecount=331, gamecount=995}

    Even...

    Winner: 石头

    Even...

    Even...

    -------(省略若干行)---------

    Total Result:

    Player{name='WinningStrategyPlayer', strategy=org.cd.designpatterns.strategy.WinningStrategy@2ff4acd0, wincount=312, losecount=332, gamecount=1000}

    Player{name='ProbStrategyPlayer', strategy=org.cd.designpatterns.strategy.ProbStrategy@54bedef2, wincount=332, losecount=312, gamecount=1000}

    更多相关内容
  • 策略模式和 Template 模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。策略模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合...
  • 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

    展开全文
  • 策略模式解决多重if判断问题)

    千次阅读 2021-01-14 20:44:21
    策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。 1.环境(Context)角色:持有一个Strategy的引用。 2.抽象策略(Strategy)角色:这是一个...

    什么是策略模式?

    策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。

    1.环境(Context)角色:持有一个Strategy的引用。
    2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
    3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
    在这里插入图片描述

    实现步骤:

    1. 定义策略接口(提前使用枚举/工厂模式初始化具体策略、可以将策略放债Map中:key为类名小写,value为具体的class地址)
    2. 实现不同的策略类
    3. 利用多态或其他方式调用策略

    为什么叫做策略模式?

    每个if判断都可以理解为就是一个策略。

    策略模式优缺点

    优点

    1. 算法可以自由切换(高层屏蔽算法,角色自由切换)
    2. 避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)
    3. 扩展性好(可自由添加取消算法 而不影响整个功能)

    缺点
    1.策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)
    2.所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)

    策略模式的演示

    首先我们写一个共同方法,假设我们做聚合支付平台,不同的code对应不同支付页面

    public interface PayStrategy {
        /**
         * 共同方法
         * @return
         */
        String toPayHtml();
    }
    
    

    然后去定义我们的枚举去存放class信息

    /**
     * @Description: 策略的枚举类,存放了所有策略的实现
     * @ClassName design_pattern
     * @Author: 王瑞文
     * @Date: 2021/1/14 10:42
     */
    public enum PayEnumStrategy {
        //阿里支付
        ALI_PAY("策略模式.impl.AliPayStrategy"),
        //微信支付
        WECHAT_PAY("策略模式.impl.WeiXInPayStrategy"),
        //小米支付
        XIAOMI_PAY("策略模式.impl.XiaoMiPayStrategy");
    
        PayEnumStrategy(String className) {
            this.setClassName(className);
        }
    
        public String getClassName() {
            return className;
        }
    
        public void setClassName(String className) {
            this.className = className;
        }
    
        private String className;
    }
    

    建造工厂去根据不同code去找到对应实现

    /**
     * @Description:
     * @ClassName design_pattern
     * @Author: 王瑞文
     * @Date: 2021/1/14 11:07
     */
    public class StrategyFactory {
        //使用策略工厂获取具体策略实现
        public static PayStrategy getPayStrategy(String strategyType) {
            //获取具体的策略地址
            String className = PayEnumStrategy.valueOf(strategyType).getClassName();
            System.out.println("【策略地址】" + className);
            try {
                return (PayStrategy) Class.forName(className).newInstance();
            } catch (Exception e) {
                return null;
            }
        }
    }
    

    去创建上下文对象执行不同策略,测试结果

    /**
     * @Description:
     * @ClassName design_pattern
     * @Author: 王瑞文
     * @Date: 2021/1/14 10:39
     */
    public class PayContextStrategy {
        /**
         * @param payCode
         * @return
         */
        public static String toPayHtml(String payCode) {
            if (null == payCode) {
                return "payCode不能为空";
            }
            //使用策略工厂获取策略实现
            PayStrategy payStrategy = StrategyFactory.getPayStrategy(payCode);
            if (payStrategy == null) {
                return "没有具体策略!";
    
            }
    
            return payStrategy.toPayHtml();
        }
    
        public static void main(String[] args) {
            System.out.println(toPayHtml("ALI_PAY"));
            System.out.println(toPayHtml("WECHAT_PAY"));
            System.out.println(toPayHtml("XIAOMI_PAY"));
    
        }
    }
    

    当然以上是基于java项目写的,很多地方也不是很完善,工厂细节没有涉及到,如果我们在maven项目中写当然还可以更简单。
    maven- 我们可以把小写类名存放在数据库中,通过bean去读取我们容器中的Impl。在这就不写啦,仅仅提供思路方法

    Spring框架中使用的策略模式

    ClassPathXmlApplicationContext Spring底层Resource接口采用策略模式
    Spring 为 Resource 接口提供了如下实现类:
    UrlResource:访问网络资源的实现类。
    ClassPathResource:访问类加载路径里资源的实现类。
    FileSystemResource:访问文件系统里资源的实现类。
    ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
    InputStreamResource:访问输入流资源的实现类。
    ByteArrayResource:访问字节数组资源的实现类。

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

    千次阅读 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)需要注意的是 :每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大。

    展开全文
  • 代码四、优缺点五、延伸样例1.结合简单工厂样例2.结合反射样例3.结合Spring样例结尾 前言 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开...
  • 在设计游戏角色伤害时,因角色差异而使用不同的计算公式,如何解决,同一计算方式分布在不同的角色中、新增角色增加维护等问题,可以使用策略模式解决。 二、策略模式的定义 GoF对策略模式的解释:定义一组算法,并...
  • java策略模式

    千次阅读 多人点赞 2019-04-09 18:19:48
    在说策略模式之前,先看一个小例子: 假如:有一场演讲比赛,有十个评委对参赛选手的成绩进行打分,但最终要通过评委的平均分来决定选手的名次。现在有两种求平均分的策略: 第一种:将十名裁判的分加起来求平均值...
  • if else终结者——策略模式

    千次阅读 多人点赞 2021-05-28 10:48:15
    阅读完本篇文章你将了解到什么是策略模式策略模式的优缺点,以及策略模式在源码中的应用。 策略模式引入 在软件开发中,我们常常会遇到这样的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们...
  • 策略模式

    千次阅读 2019-09-25 20:23:35
    策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略...
  • Java设计模式及应用场景之《策略模式

    千次阅读 热门讨论 2020-04-09 21:45:33
    一、策略模式定义 Define a family of algorithms,encapsulate each one,and make them interchangeable. 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。 二、策略模式的结构和说明 Strategy ...
  • 文章目录1.前言2.概念3.模式的结构和实现3.1 模式的结构3.2模式的实现 1.前言 在现实生活中常常遇到多种可选择的情况,例如,出行旅游可以乘坐飞机、...如果采用策略模式就能很好解决该问题。 2.概念 该模式定义了一
  • SpringBoot如何使用策略模式干掉if else

    千次阅读 2020-08-19 10:17:01
    解决思路 1、首先构建一个 GeneralChannelRule 基础规则抽象类,定义一个抽象方法process(),不同的渠道都需要实现该抽象方法。 public abstract class GeneralChannelRule { public abstract void process(); } 2...
  • 策略模式+反射-解决多重if-else if问题

    千次阅读 热门讨论 2018-10-19 12:03:11
    当没有用到设计模式时,我们一般会采用下面的方式处理业务 int type = 1; if(type == 1){ System.out.println("普通客户,商品原价出售"); }else if(type == 2){ System.out.println(...
  • 设计模式之策略模式(实例+Demo)

    万次阅读 多人点赞 2018-03-09 21:24:58
    马上要放清明小长假了,各位都想好去哪里玩了没?清明前的周日,整理下发型,梳理好心情,发个文章,再...5. 策略模式的优缺点。6.其他 1. 为什么要写这篇文章? 简单来说四个字:记录分享。用来记录供自己快速...
  • 策略模式的优势

    千次阅读 2017-01-06 14:18:06
    废话上说 咱先上图: 那什么时候需要用到策略模式: 如果系统中某个类的某一行为存在多种实现方式,而且“这些实现方式可以互换时使用策略模式”。 个人理解: 由于策略模式是一个对象行为型模式
  • 状态模式原理分析前言什么是状态模式状态模式示例状态模式角色状态模式与责任链模式状态模式与策略模式状态模式应用场景状态模式优缺点总结 前言 状态对于我们开发者而言是相当熟悉的,平常开发中总是离不开对象的...
  • 策略模式(策略设计模式)详解

    千次阅读 2019-05-23 17:30:35
    策略模式(策略设计模式)详解 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。 ...
  • 3、简述策略模式及其使用方法

    千次阅读 2020-10-03 11:48:37
    文章目录简述策略模式及其使用方法概念结构优缺点及使用范围优点缺点适用环境使用方法练习题目源代码 概念 定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的...
  • 基于Spring自动注入的策略模式

    千次阅读 2018-10-25 19:45:20
    但我的活动分成多种活动模式如:秒杀,折扣等,这样就不能统一对具体活动进行crud 二 传统方式: 1,我就要通过创建不同活动类型的service如:ISeckillActivityService,IDiscountActivityService等 2,在要...
  • 什么是策略模式? 策略这个词应该怎么理解呢,打个比方说,我们出门的时候选择不同的出行方式,比如步行、骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。 再比如我们去逛商场...
  • 策略模式在实际业务场景中的使用及优化 策略模式(Strategy Pattern):定义不同的策略算法,以达到新增算法、移除算法、修改算法的便利性和调用无感知,并且不同的算法区分开之后也更加方便阅读策略算法。(个人...
  • 23种设计模式的优缺点

    千次阅读 2021-01-27 21:06:01
    1、单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。 2、原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似...
  • Java设计模式-策略模式

    千次阅读 2019-03-28 09:00:11
    策略模式
  • 【设计模式系列】行为型之策略模式

    千次阅读 多人点赞 2021-03-05 22:25:46
    首先介绍一下众多博客文章中提到的策略模式的概念,针对于文章结构构成,请允许小编在此处再细说一下。 策略模式:百度百科中引述为:指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。 1.策略模式是...
  • Java设计模式之策略模式

    万次阅读 多人点赞 2015-04-13 07:20:41
    本文属于23种设计模式系列。 介绍的是策略模式
  • 使用策略模式消除过量ifelse

    千次阅读 2020-06-16 10:03:12
     策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。 策略模式的结构  策略...
  • 设计模式 | 策略模式及典型应用

    千次阅读 2018-10-18 20:54:46
    介绍策略模式 示例 商场购物打折策略的实现 策略模式总结 源码分析策略模式的典型应用 Java Comparator 中的策略模式 Spring Resource 中的策略模式 Spring Bean 实例化中的策略模式 策略模式 在软件开发中,...
  • 设计模式——策略模式

    千次阅读 2018-04-07 11:21:37
    策略模式是一种算法的封装,把算法本身和使用分割开来。一个抽象类,每个具体算法都是这个抽象类的具体实现,然后在context中定义一个算法类,并且赋值为传进来的算法对象,不同的算法传入不同的算法对象。各个算法...
  • C语言实现设计模式—策略模式

    千次阅读 2019-11-12 20:39:40
    文章目录介绍抽象实现代码模块 介绍 在某些情况下,一个客户不想或者不能直接引用一个...硬件代理模式使用结构体封装硬件设备,硬件有可能是内存、传感器设备、IO设备等,但无论什么样的设备,硬件代理的作用就是隐...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 138,461
精华内容 55,384
关键字:

如何解决策略模式的缺点