当前位置: 首页 > news >正文

设计模式之策略者模式详解(Strategy Pattern)

其他设计模式

设计模式之建造者模式理解
设计模式之桥接模式理解
设计模式之适配器模式理解
设计模式之装饰者模式详解
设计模式之组合模式详解
设计模式之外观模式详解(Facade Pattern)
设计模式之享元模式详解(FlyWeight Pattern)
设计模式之代理模式详解(Proxy Pattern)
设计模式之模板方法模式详解(Template Method Pattern)
设计模式之命令模式详解(Command Pattern)
设计模式之访问者模式详解(Visitor Pattern)
设计模式之观察者模式详解(Observer Pattern)
设计模式之迭代器模式详解(Iterator Pattern)
设计模式之中介者模式详解(Mediator Pattern)
设计模式之备忘录模式详解(Memento Pattern)
设计模式之状态模式详解(State Pattern)
设计模式之职责链(责任链)模式(ResponsibilityChain Pattern)
设计模式之解释器模式详解(Interpreter Pattern)

引言

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法,卖场往往根据不同的客户制定不同的报价策略,比如针对新客户不打折扣,针对老客户打9折,针对VIP客户打8折…

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

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

认识策略者模式

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

策略模式属于对象行为模式,其主要优点如下。

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

其主要缺点如下。

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

原理类图与角色说明

策略者模式的原理类图

角色说明

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

模式的实现

//抽象策略类
public interface Strategy {
    public void strategyMethod();//策略方法
}
//具体策略类A
public class ConcreteStrategyA implements Strategy {
    @Override
    public void strategyMethod() {
        System.out.println("具体策略A的策略方法被访问!");
    }
}
//具体策略类B
public class ConcreteStrategyB implements Strategy {
    @Override
    public void strategyMethod() {
        System.out.println("具体策略B的策略方法被访问!");
    }
}
//环境类
public class Context {
    private Strategy strategy;

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void strategyMethod() {
        strategy.strategyMethod();
    }
}
//客户端测试
public class Client {
    public static void main(String[] args) {
        //创建具体策略A
        Strategy s = new ConcreteStrategyA();
        //创建策略环境
        Context context = new Context(s);
        context.strategyMethod();
        System.out.println("-----------------------");
        
        //创建具体策略B
        s = new ConcreteStrategyB();
        //使用策略B
        context.setStrategy(s);
        context.strategyMethod();
    }
}

程序的运行结果如下:

具体策略A的策略方法被访问!
-----------------------
具体策略B的策略方法被访问!

应用实例

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

现在我们要做一个报价管理的模块,简要点就是要针对不同的客户,提供不同的折扣报价。下面通过使用策略者模式来实现:

创建抽象报价策略接口
//报价策略接口
public interface QuoteStrategy {
    //获取折后价的价格
    BigDecimal getPrice(BigDecimal originalPrice);
}
创建具体报价策略
//新客户的报价策略实现
public class NewCustomerQuoteStrategy implements QuoteStrategy {
    @Override
    public BigDecimal getPrice(BigDecimal originalPrice) {
        System.out.println("抱歉!新客户没有折扣!");
        return originalPrice;
    }
}
//老客户的报价策略实现
public class OldCustomerQuoteStrategy implements QuoteStrategy {
    @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;
    }
}
创建报价上下文
//报价上下文角色
public class QuoteContext {
    //持有报价策略
    QuoteStrategy quoteStrategy;

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

    //回调具体报价策略的方法
    public BigDecimal getPrice(BigDecimal originalPrice) {
        return quoteStrategy.getPrice(originalPrice);
    }
}
客户端测试
//客户端测试
public class Client {
    public static void main(String[] args) {
        //1.创建老客户的报价策略
        QuoteStrategy oldQuoteStrategy = new OldCustomerQuoteStrategy();

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

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

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

        //改变报价策略
        QuoteStrategy newQuoteStrategy = new NewCustomerQuoteStrategy();
        quoteContext.setQuoteStrategy(newQuoteStrategy);
        BigDecimal price1 = quoteContext.getPrice(new BigDecimal(100));

        System.out.println("价格为为:" +price1);

    }
}
程序的运行结果
恭喜!老客户享有9折优惠!
折扣价为:90.00
抱歉!新客户没有折扣!
价格为为:100

这个时候,商场营销部新推出了一个客户类型–VIP用户,可以享受折扣8折优惠,那该怎么办呢?

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

VIP用户的报价策略实现:

//Vip客户报价策略实现
public class VIPCustomerQuoteStrategy implements QuoteStrategy{
    @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;
    }
}
//客户端测试
public class Client {
    public static void main(String[] args) {
        //1.创建老客户的报价策略
        QuoteStrategy vipQuoteStrategy = new VIPCustomerQuoteStrategy();

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

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

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

程序的运行结果为:

恭喜!VIP客户享有8折优惠!
折扣价为:80.00

相关文章:

  • 利用J2ME里的RMS对记录进行排序
  • 设计模式之职责链(责任链)模式(ResponsibilityChain Pattern)
  • SWT GC的drawLine方法的一个隐藏Bug
  • 多维数组与矩阵之子数组的最大累加和
  • 游戏也是软件,J2ME游戏程序员不能忘本
  • 多维数组与矩阵之子矩阵的最大累加和
  • 本周技术关注:Oracle10G、MSSQL2005、MYSQL5: CLuster、Replication、Snapshot
  • 设计模式之解释器模式详解(Interpreter Pattern)
  • 书评--信息经营法则
  • Java最大公约数与最小公倍数
  • 关于SWT drawLine bug的进一步验证
  • 矩阵乘法
  • IT出版人的Blog世界
  • 关于Java数组,你该了解这些
  • 世界上最难攀登的山其实是自己!
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • css的样式优先级
  • IOS评论框不贴底(ios12新bug)
  • JavaScript设计模式与开发实践系列之策略模式
  • React as a UI Runtime(五、列表)
  • windows下如何用phpstorm同步测试服务器
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 讲清楚之javascript作用域
  • 使用docker-compose进行多节点部署
  • 提醒我喝水chrome插件开发指南
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 用mpvue开发微信小程序
  • $.each()与$(selector).each()
  • (06)Hive——正则表达式
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (二)c52学习之旅-简单了解单片机
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (理论篇)httpmoudle和httphandler一览
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (十六)Flask之蓝图
  • (十一)手动添加用户和文件的特殊权限
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转)http-server应用
  • .NET Core Web APi类库如何内嵌运行?
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .net对接阿里云CSB服务
  • .NET微信公众号开发-2.0创建自定义菜单
  • /usr/lib/mysql/plugin权限_给数据库增加密码策略遇到的权限问题
  • [android] 切换界面的通用处理
  • [docker] Docker的数据卷、数据卷容器,容器互联
  • [Excel]如何找到非固定空白格數列的條件數據? 以月份報價表單為例
  • [flask]http请求//获取请求体数据
  • [Hibernate] - Fetching strategies
  • [ios-必看] IOS调试技巧:当程序崩溃的时候怎么办 iphone IOS
  • [JavaScript]_[初级]_[关于forof或者for...of循环语句的用法]
  • [JavaWeb]——获取请求参数的方式(全面!!!)