策略模式-设计模式

在讲述之前,我们首先看小例子:

现实生活中我们去商场上买东西的时候,卖场经常根据不同的客户来制定不同的报价策略,比如新客户不打折扣,针对老客户打9折,针对VIP打8折……

现在我们做一个报价管理模块,简要点就是针对不同的客户,提供不同的报价。

假如是有你来做,你会怎么做?
在日常的开发中,我们大部分会写出如下的代码片段:

public class QuoteManager {
    public BigDecimal quote(BigDecimal originalPrice,String customType){
        if ("新客户".equals(customType)) {
            System.out.println("抱歉!新客户没有折扣!");
            return originalPrice;
        }else if ("老客户".equals(customType)) {
            System.out.println("恭喜你!老客户打9折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        }else if("VIP客户".equals(customType)){
            System.out.println("恭喜你!VIP客户打8折!");
            originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
            return originalPrice;
        }
        //其他人员都是原价
        return originalPrice;
    }
}

我们会在日常的开发中大量使用if ,else if,else if,else……上面的代码工作的很好,但是呢,经过大量的判断操作,也是有问题的,将不同客户的报价的算法都放在了同一个方法里面,使得该方法随着业务的拓展会变得越来越臃肿,时刻更改着此处代码,达不到复用性。

放看一下上面的改进,我们将不同客户的报价算法单独作为一个方法。

public class QuoteManagerImprove {
    public BigDecimal quote(BigDecimal originalPrice, String customType){
        if ("新客户".equals(customType)) {
            return this.quoteNewCustomer(originalPrice);
        }else if ("老客户".equals(customType)) {
            return this.quoteOldCustomer(originalPrice);
        }else if("VIP客户".equals(customType)){
            return this.quoteVIPCustomer(originalPrice);
        }
        //其他人员都是原价
        return originalPrice;
    }

    /**
     * 对VIP客户的报价算法
     * @param originalPrice 原价
     * @return 折后价
     */
    private BigDecimal quoteVIPCustomer(BigDecimal originalPrice) {
        System.out.println("恭喜!VIP客户打8折");
        originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }

    /**
     * 对老客户的报价算法
     * @param originalPrice 原价
     * @return 折后价
     */
    private BigDecimal quoteOldCustomer(BigDecimal originalPrice) {
        System.out.println("恭喜!老客户打9折");
        originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }

    /**
     * 对新客户的报价算法
     * @param originalPrice 原价
     * @return 折后价
     */
    private BigDecimal quoteNewCustomer(BigDecimal originalPrice) {
        System.out.println("抱歉!新客户没有折扣!");
        return originalPrice;
    }

}

上面的代码比刚开始的时候好一点,将每个具体的算法都单独抽出来,当某个具体的算法出现变动问题,只需要修改相应的算法就可以啦。

但是改进后的代码还是有问题的,有什么问题呢?

当我们新增一个客户类型,首先要添一个该客户类型的报价算法,然后再quote方法中再次添加一个else if ,这就违反了设计原则--开闭原则(open-closed - principle)

开闭原则:

对于扩展是开放的,这意味着模块的行为是可以拓展的,当应用的需求改变时候,可以对模块进行扩展。

对于修改是关闭的,对模块行为进行扩展时 ,不用改动模块的源代码或者二进制代码。

那有没有办法可适用于可扩展,可维护,又可以方便的响应变化呢,下面就是我们要讲的策略模式。

策略模式

定义:

策略模式定义了一系列的算法,把它们一个个封装起来,并且使他们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。

结构:

  1. 策略接口角色IStrategy:用来约束一系列具体策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体实现的算法。
  2. 具体策略实现角色ConcreteStrategy:具体的策略实现,即算法的具体实现。
  3. 策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现了交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从中获取相关数据,回调了策略上下文对象的方法。

UML类图

策略模式代码的一般实现

package strategy.examp01;
//策略接口
public interface IStrategy {
    //定义的抽象算法方法 来约束具体的算法实现方法
    public void algorithmMethod();
}

具体的策略实现:

package strategy.examp01;
// 具体的策略实现
public class ConcreteStrategy implements IStrategy {
    //具体的算法实现
    @Override
    public void algorithmMethod() {
        System.out.println("this is ConcreteStrategy method...");
    }
}
package strategy.examp01;
// 具体的策略实现2
public class ConcreteStrategy2 implements IStrategy {
     //具体的算法实现
    @Override
    public void algorithmMethod() {
        System.out.println("this is ConcreteStrategy2 method...");
    }
}
package strategy.examp01;
/**
 * 策略上下文
 */
public class StrategyContext {
    //持有一个策略实现的引用
    private IStrategy strategy;
    //使用构造器注入具体的策略类
    public StrategyContext(IStrategy strategy) {
        this.strategy = strategy;
    }
   public void contextMethod(){
        //调用策略实现的方法
        strategy.algorithmMethod();
    }
}

客户端使用:

package strategy.examp01;
//外部客户端
public class Client {
    public static void main(String[] args) {
        //1.创建具体测策略实现,多态使用
        IStrategy strategy = new ConcreteStrategy2();
        //2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
        StrategyContext ctx = new StrategyContext(strategy);
        //3.调用上下文对象的方法来完成对具体策略实现的回调
        ctx.contextMethod();
    }
}

针对一开始所讲的例子:可以使用策略模式对其进行改造,不同的类型的客户有不同的折扣,可以将不同类型的客户报价规则都封装为一个独立的算法,然后抽象出报价算法的公共接口。

公共报价的策略接口:

package strategy.examp02;
import java.math.BigDecimal;
//报价策略接口
public interface IQuoteStrategy {
    //获取折后价的价格
    BigDecimal getPrice(BigDecimal originalPrice);
}

新客户报价策略实现:

package strategy.examp02;
import java.math.BigDecimal;
//新客户的报价策略实现类
public class NewCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("抱歉!新客户没有折扣!");
        return originalPrice;
    }
}

老客户报价策略实现:

package strategy.examp02;
import java.math.BigDecimal;
//老客户的报价策略实现
public class OldCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("恭喜!老客户享有9折优惠!");
        originalPrice = originalPrice.multiply(new BigDecimal(0.9)).setScale(2,BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }
}

VIP客户报价策略实现:

package strategy.examp02;
import java.math.BigDecimal;
//VIP客户的报价策略实现
public class VIPCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("恭喜!VIP客户享有8折优惠!");
        originalPrice = originalPrice.multiply(new BigDecimal(0.8)).setScale(2,BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }
}

报价上下文:

package strategy.examp02;
import java.math.BigDecimal;
//报价上下文角色
public class QuoteContext {
    //持有一个具体的报价策略
    private IQuoteStrategy quoteStrategy;

    //注入报价策略
    public QuoteContext(IQuoteStrategy quoteStrategy){
        this.quoteStrategy = quoteStrategy;
    }

    //回调具体报价策略的方法
    public BigDecimal getPrice(BigDecimal originalPrice){
        return quoteStrategy.getPrice(originalPrice);
    }
}

外部客户端

package strategy.examp02;
import java.math.BigDecimal;
//外部客户端
public class Client {
    public static void main(String[] args) {
        //1.创建老客户的报价策略,多态使用
        IQuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();

        //2.创建报价上下文对象,并设置具体的报价策略
        QuoteContext quoteContext = new QuoteContext(oldQuoteStrategy);

        //3.调用报价上下文的方法
        BigDecimal price = quoteContext.getPrice(new BigDecimal(100));

        System.out.println("折扣价为:" +price);
    }
}

控制台输出:

恭喜!老客户享有9折优惠!
折扣价为:90.00

这个时候,商城营销部推出了新的客户类型--MVP,可以享受7折优惠,那该怎么办?

这个很容易,只需要新增个报价策略的实现,然后外部客户端调用的时候,创建这个新增的报价策略实现,并放到策略上下文就可以啦,对原本的已经实现的代码没有任何的改动。

MVP用户的报价策略实现:

package strategy.examp02;
import java.math.BigDecimal;
//MVP客户的报价策略实现
public class MVPCustomerQuoteStrategy implements IQuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("哇偶!MVP客户享受7折优惠!!!");
        originalPrice = originalPrice.multiply(new BigDecimal(0.7)).setScale(2,BigDecimal.ROUND_HALF_UP);
        return originalPrice;
    }
}

外部客户端实现:

package strategy.examp02;
import java.math.BigDecimal;
//外部客户端
public class Client {
    public static void main(String[] args) {
        //创建MVP客户的报价策略
        IQuoteStrategy mvpQuoteStrategy = new MVPCustomerQuoteStrategy();

        //创建报价上下文对象,并设置具体的报价策略
        QuoteContext quoteContext = new QuoteContext(mvpQuoteStrategy);

        //调用报价上下文的方法
        BigDecimal price = quoteContext.getPrice(new BigDecimal(100));

        System.out.println("折扣价为:" +price);
    }
}

控制台输出:

哇偶!MVP客户享受7折优惠!!!
折扣价为:70.00

深入理解策略模式

策略模式的作用:

把具体的算法实现从业务逻辑中剥离出来,成一系列的独立算法类,使得可以相互替换。

策略模式的重点:

不是如何设计是实现算法,而是如何组织和调用这些算法,从而让我们的程序结构更加的灵活、可扩展。

策略模式就是把各个平等的具体实现进行抽象化,封装成独立的算法,然后经过上下文和具体的算法类进行交互。各个算法之间都是平等的,地位都是一致的,正是由于各个算法的平等性,所以是可替代的。但是同一时刻只能使用一个策略。

三国刘备取西川时,庞统给的上、中、下的三个计策:

  上策:挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也。

  中策:杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。

  下策:退还白帝,连引荆州,慢慢进图益州,此为下计。
  这三个计策都是取西川的计策,也就是攻取西川这个问题的具体的策略算法,刘备可以采用上策,可以采用中策,当然也可以采用下策,由此可见策略模式的各种具体的策略算法都是平等的,可以相互替换。

  那谁来选择具体采用哪种计策(算法)?

在这个故事中当然是刘备选择了,也就是外部的客户端选择使用某个具体的算法,然后把该算法(计策)设置到上下文当中;

还有一种情况,客户端不选择具体的算法,把这个事情交给上下文,这相当于刘备说我不管哪种计策,只要攻下西川即可,具体怎么攻占有上下文决定。

//攻取西川的策略
2 public interface IOccupationStrategyWestOfSiChuan {
3     public void occupationWestOfSiChuan(String msg);
4 }
//攻取西川的上上计策
public class UpperStrategy implements IOccupationStrategyWestOfSiChuan {
    @Override
    public void occupationWestOfSiChuan(String msg) {
        if (msg == null || msg.length() < 5) {
            //故意设置障碍,导致上上计策失败
            System.out.println("由于计划泄露,上上计策失败!");
            int i = 100/0;
        }
        System.out.println("挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也!");
    }
}
//攻取西川的中计策
public class MiddleStrategy implements IOccupationStrategyWestOfSiChuan {
    @Override
    public void occupationWestOfSiChuan(String msg) {
        System.out.println("杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。");
    }
}
//攻取西川的下计策
public class LowerStrategy implements IOccupationStrategyWestOfSiChuan {
    @Override
    public void occupationWestOfSiChuan(String msg) {
        System.out.println("退还白帝,连引荆州,慢慢进图益州,此为下计。");
    }
}
//攻取西川参谋部,就是上下文啦,由上下文来选择具体的策略
 2 public class OccupationContext  {
 3
 4     public void occupationWestOfSichuan(String msg){
 5         //先用上上计策
 6         IOccupationStrategyWestOfSiChuan strategy = new UpperStrategy();
 7         try {
 8             strategy.occupationWestOfSiChuan(msg);
 9         } catch (Exception e) {
10             //上上计策有问题行不通之后,用中计策
11             strategy = new MiddleStrategy();
12             strategy.occupationWestOfSiChuan(msg);
13         }
14     }
15 }
//此时外部客户端相当于刘备了,不管具体采用什么计策,只要结果(成功的攻下西川)
public class Client {

    public static void main(String[] args) {
        OccupationContext context = new  OccupationContext();
        //这个给手下的人激励不够啊
        context.occupationWestOfSichuan("拿下西川");
        System.out.println("=========================");
        //这个人人有赏,让士兵有动力啊
        context.occupationWestOfSichuan("拿下西川之后,人人有赏!");
    }
}

控制台输出

由于计划泄露,上上计策失败!
杨怀、高沛是蜀中名将,手下有精锐部队,而且据守关头,我们可以装作要回荆州,引他们轻骑来见,可就此将其擒杀,而后进兵成都,此为中计。
=========================
挑选精兵,昼夜兼行直接偷袭成都,可以一举而定,此为上计计也!

策略模式的优点:

  1. 策略模式的功能通过抽象、封装来定义一系列的算法,是的算法之间可以相互替换,所以为这些算法定义一个公共的接口,用来约束算法的功能实现。如果算法有公共的功能,可以将接口变为抽象类,将公共功能放在了抽象父类里面。
  2. 策略模式的算法是可以相互替换,平等的,避免了if else 组织结构。
  3. 扩展性更好:更容易扩展策略,只需要新增一个策略实现类,然后在使用策略实现的地方,使用策略实现即可。

策略模式的缺点:

  1. 增加了对象的数量。
  2. 只适合扁平的算法结构。由于策略模式是平等的关系,实际上就是扁平的算法结构。

策略模式的本质:

分离算法,选择实现。

策略模式体现了开闭原则:策略模式将一系列的算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。

策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略都是兄弟关系,实现了同一个接口或者继承同一个抽象类。这样只需要实现策略的客户端保持面向抽象编程,就可以动态切换不同的策略实现进行替换。

以上就是策略模式的基本内容和使用,也是设计模式中另一个较为重要的模式,希望对大家有所帮助!!!

原文地址:https://www.cnblogs.com/guohai-stronger/p/11829428.html

时间: 2024-10-15 02:47:19

策略模式-设计模式的相关文章

策略模式 -- 设计模式系列文章(一)

概述 在日常开发工作中,适当的使用一些设计模式,可以让代码扩展性更强,能更好地拥抱变化,让代码更加优雅.本文主要介绍设计模式中的策略模式,并附上测试示例 Demo 供大家参考. 定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 个人理解 策略模式,针对那些动作因对象而异的情况下,把每一个动作都独立封装起来并实现同一个接口,通过组合的方式赋予对象相对应的动作,从而使得所有的动作都可以相互替换.通过策略模式,可以达到在运

策略模式设计模式(Strategy)摘录

23种子GOF设计模式一般分为三类:创建模式.结构模型.行为模式. 创建模式抽象的实例.一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化托付给还有一个对象.创建型模式有两个不断出现的主旋律.第一.它们都将关于该系统使用哪些详细的类的信息封装起来.第二,它们隐藏了这些类的实例是怎样被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建.谁创建它.它是怎样被创建的.以及何时创建这些方面

不一样的策略模式(设计模式五)

前言 什么是设计模式?说白了就是套路,是经过历代程序员总结出来的.很多时候我们虽然学会了很多套路,但是啥时候使用,使用哪个合适,我想这才是问题的关键. 知道怎么用不知道什么时候用,这时候看下代码风格也行用的上,策略模式是非常容易通过代码风格使用上的. 策略模式,为什么叫策略模式呢?其实策略模式还有一个别名叫做政策(policy)模式,在古代,对不同的国家呢,实行不同的政策,对A呢,采取税务10%,对B国采取税务20%. 这样根据不同国家政策不同呢,在计算机中就是根据不同对象采取不同的方法,就叫做

策略模式--设计模式系列

今天我们写一个鸭子类,首先分析一下鸭子有哪些特征呢? 鸭子:会叫,会游水,会飞,外观 现在有个需求:分两种鸭子,一种是外观是绿头,一种是红头,写下看: class Duck: def quack(self): print('呱呱叫') def swim(self): print('我会游泳') def display(self):pass def fly(self): print('我会飞') class RedDuck(Duck): def display(self): print('我是红头

三分钟理解“策略模式”——设计模式轻松掌握

实际问题: 由于超市隔三差五就要办促销活动,但每次促销活动的方式不一样,所以需要开发一个软件,营业员只要输入原价再选择活动类型后,就能计算出折扣以后的价钱. 普通人的做法: mian(){ String input = readLine(); double price = readLine(); switch (input) case "九五折": price = price * 0.95; break; case "满100返50": if(price>=1

对设计模式的总结之简单工厂与策略模式

前言 面向对象编程追求的本质-提高扩展性.可维护性.灵活性和复用性.合理利用面向对象6个原则,能够很好的达到要求.如何利用好就是至关重要的了,前人总结了23+个设计模式能够让初学者更容易学到其中的精髓,本文就说说我对本人对简单工厂模式.策略模式的见解. 简单工厂模式与策略模式 简单工厂模式 工作中,常常遇到需要做一个功能(鸭子),这个功能中含有可控个数的子操作功能(鸭子叫,鸭子跑,鸭子飞),而且子功能在不同的情况下处理方式又不相同(成年鸭子/小鸭子叫,成年鸭子/小鸭子跑,成年鸭子/小鸭子飞).我

PHP设计模式——策略模式

声明:本系列博客参考资料<大话设计模式>,作者程杰. 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化,即封装变化的算法. 适用场景: 1. 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为. 2. 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现. 3. 对客户隐藏具体策略(算法)的实现细节,彼此完全独立. 4.客户端必须知道所有的策略类,并自行决定使

【设计模式】 模式PK:策略模式VS状态模式

1.概述 行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下. 策略模式(左)和状态模式(右)的通用类图. 两个类图非常相似,都是通过Context类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式.但根据两者的定义,我们发现两者的区别还是很明显的:策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的:而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的.这两种模式虽然都有变换的行为,但

设计模式(一)策略模式

1.什么是策略模式? 策略模式,又叫算法簇模式,就是定义了不同的算法簇,并且之间可以互相替换,此模式算法的变化独立于使用算法的客户. 2.策略模式有什么好处? 策略模式的好处在于你可以动态改变对象的行为. 3.设计原则 设计原则是把一个类中经常改变或者将来改变的部分提取出来,作为一个接口(C++中可以用抽象类),然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现这个接口的类的行为. 策略模式属于对象行为型模式,主要针对一组算法,将每一个算法封装到具有共同接口(C++中即为抽象基