⭐设计模式—策略模式

一、背景:

在开发中经常遇到这种情况,实现某个功能有多种算法策略,我们可以根据不同环境或者条件选择不同的算法策略来完成该功能,比如查找、排序等,一种常用方式是硬编码在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过 if-else 或者 case 等条件判断语句来进行选择。但是如果需要增加新的算法策略,就需要修改封装算法类的源代码;更换查找算法,也需要修改客户端的调用代码。并且在这个类中封装了大量算法,也会使得该类代码较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。

如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?解决方法就是使用策略模式。

二、什么是策略模式:

将类中经常改变或者可能改变的部分提取为作为一个抽象策略接口类,然后在类中包含这个对象的实例,这样类实例在运行时就可以随意调用实现了这个接口的类的行为。

比如定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化,这就是策略模式。

策略模式包含如下角色:

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

策略模式的优点在于可以动态改变对象的行为;但缺点是会产生很多策略类,同时客户端必须知道所有的策略类,并自行决定使用哪一个策略类;

策略模式适用用于以下几种场景:

  • (1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需要动态地在几种算法中选择一种
  • (2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

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

1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式

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

三、代码实现:

以电商项目为例,假设电商项目目前有普通用户,和vip用户,后续运营打算再开通一个Svip用户,不同的用户购买商品会有不同的折扣,比如普通用户原价购买,vip用户享有95折,svip用户享有9折。

1).传统实现方式

代码

public class tradition {
    
     //传统实现模式  普通用户原价购买,vip用户享有95折,svip用户享有9折。
    public Double pay(String type,Double money){

        if (type.equals("vip用户")){
            return money*0.95;
        }
        if (type.equals("svip用户")){
            return money*0.9;
        }
        return money;  //普通会员计费
    }

}

传统的方式就是通过各种if来实现,这种方式是最简便也是最直白的,但是缺点就是后期维护性非常差,如果后续还要增加新的会员或者计费方式等就需要再这里加上if(),这样是不符合开闭原则的。

2).策略模式实现

抽象类策略

//抽象策略类
public interface PayStrategy {
    public double pay(Double money);
}

具体实现类

public class PrimaryMember implements PayStrategy{
    @Override
    public double pay(Double money) {
        return money;  //普通会员没有优惠  原价购买
    }
}

public class VipMember implements PayStrategy{
    @Override
    public double pay(Double money) {
        return money*0.95;
    }
}

public class SvipMember implements PayStrategy{
    @Override
    public double pay(Double money) {
        return money*0.9; //svip用户  9折购买
    }
}

上下文类

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

public class Context {

    private PayStrategy payStrategy; //策略接口

    public Context(PayStrategy payStrategy){
        this.payStrategy=payStrategy;
    }

    public void setPayStrategy(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
    }

    //计算价格
    public Double operate(double money){
        //通过接口变量调用对应的策略
        return  this.payStrategy.pay(money);
    }
}

测试类

public class TestUser {

    public static void main(String[] args) {

        //具体的行为策略
        PayStrategy payStrategy=new PrimaryMember();
        PayStrategy payStrategyVip=new VipMember();
        PayStrategy payStrategySvip=new SvipMember();

        //用户可以选择不同的策略
        Context context=new Context(payStrategy);
        Context contextVip=new Context(payStrategyVip);
        Context contextSvip=new Context(payStrategySvip);

        context.operate(300); //普通会员  300元
        contextVip.operate(300); //vip 会员 285元
        contextSvip.operate(300);//svip会员 270元
    }

}
四.策略模式优缺点

1) 优点

  • 策略模式提供了对"开闭原则"的完美支持,用户可以在不 修改原有系统的基础上选择算法或行为,也可以灵活地增加 新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。

2) 缺点

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

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

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

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

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

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

相关推荐
Lojarro11 分钟前
【Spring MVC】第二站-Spring MVC请求
java·spring·mvc
小高不明21 分钟前
仿 RabbitMQ 的消息队列1(实战项目)
java·spring boot·分布式·spring·rabbitmq·mvc
鹿屿二向箔34 分钟前
搭建一个基于Spring Boot的驾校管理系统
java·spring boot·后端
hong_zc36 分钟前
Spring MVC(一)
java·spring·mvc
洛阳纸贵39 分钟前
基于SpringCloud的广告系统设计与实现(一)
java·开发语言
黄雪超44 分钟前
数据结构与算法面试专题——引入及归并排序
数据结构·算法·面试
m0_748244961 小时前
使用Nginx正向代理让内网主机通过外网主机访问互联网
java·前端·nginx
曲奇是块小饼干_1 小时前
leetcode刷题记录(四十八)——128. 最长连续序列
java·算法·leetcode·职场和发展
黄名富2 小时前
Kafka 日志存储 — 文件目录及日志格式
java·分布式·微服务·zookeeper·kafka
ekskef_sef2 小时前
Spring Boot——日志介绍和配置
java·数据库·spring boot