设计模式,策略模式

策略模式概述

策略模式,即与解决问题的策略有关的模式,该模式旨在更好的实现策略的实现。策略模式分为三个部分:环境、抽象策略角色、具体策略角色。策略模式能使得更好地管理和使用一类算法。

  • 环境(context):用于确定对应的具体策略角色。并将解决策略的功能最终放在环境context中(只要向下调用功能即可)。
  • 抽象策略角色:能完成一种问题的解决的各种算法的范称,通常为接口,定义能解决问题的功能,但是不实现,具体的算法的步骤由实现类解决。
  • 具体策略角色:实现抽象策略角色,要完成具体的解决问题的功能的角色。

代码实现举例

如果一家商场能让顾客办会员卡以达到优惠的目的,而会员卡分为两种:八折卡、满减卡(没满三百减五十)。要求输入原价格和优惠方式,得到优惠后的价格。

分析,这里的抽象策略角色应该为优惠方式,而具体策略角色有三个:八折、满减、普通,而环境(通常用context表示)用于确定对应具体的优惠方式

抽象策略角色(优惠方式)

java 复制代码
/**
 * 表示优惠的算法,具体优惠方式由实现类定义
 */
public interface Concession {
    //计算优惠后的价格的功能
    Double doConcession(Double price);
}

具体策略角色(优惠方式的不同具体实现)

普通:

java 复制代码
/**
 * 没有优惠卡的计算方法
 */
public class NormalConcession implements Concession {
    @Override
    public Double doConcession(Double price) {
        return price;
    }
}

八折卡:

java 复制代码
/**
 *  有八折卡的计算方法
 */
public class RebateConcession implements Concession {

    @Override
    public Double doConcession(Double price) {
        return price * 0.8;
    }
}

满减卡:

java 复制代码
/**
 * 有满减卡的计算方法(每满三百减五十)
 */
public class ReturnConcession implements Concession {
    @Override
    public Double doConcession(Double price) {
        return price - ( (int)(price / 300) * 50 );
    }
}

普通策略模式的环境(context)

java 复制代码
/**
 * 普通策略模式的Context类,用于决定不同的算法
 */
public class ConcessionContext {
    private Concession concession;
    public ConcessionContext(Concession concession){
        this.concession = concession;
    }
    public Double doConcession(Double price){
        return concession.doConcession(price);
    }
}

通过传入不同的具体策略模式的对象,来决定其成员变量concession是怎样的策略。并且将策略的功能最终放在环境中,业务代码只要调用环境的功能的方法即可。

主要业务代码

java 复制代码
public class Main {
    public static void main(String[] args) throws Exception {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入原价格");
        double price = scan.nextDouble();
        System.out.println("请输入优惠方式");
        String method = scan.next();
        ConcessionContext context = null;
        switch (method){
            case "普通":
                context = new ConcessionContext(new NormalConcession());
                break;
            case "八折卡":
                context = new ConcessionContext(new RebateConcession());
                break;
            case "满减卡":
                context = new ConcessionContext(new ReturnConcession());
                break;
            default:
                context = null;
                break;
        }
        if (context == null){
            throw new Exception("方式错误");
        }
        Double concession = context.doConcession(price);
        System.out.println("优惠后价格为:" + concession);
    }

但是这种方式有一些缺点:

  • 具体策略的选择在业务代码中进行,当修改底层时(比如增加、删除、更新了策略)也要修改业务代码
  • 业务代码中要实例化各种具体策略对象,使得代码繁杂,而且个人感觉这样使得环境context有点多余,在这种情况下,context只是起了一个没有中间作用的类,判断完后直接使用具体实现策略的对象调用方法差别也不大

策略模式与简单工厂模式相结合

结合的要点是,让环境context起到一个工厂类的效果,来完成实例化。并且,让context去完成具体实现策略的判断后再作对应,我们只要传入要选择的策略的标识即可(比如传入目标策略的名字,让context根据此名字来实例化对应的策略对象)。

升级版的context

java 复制代码
/**
 * 策略者模式与简单工厂模式相结合
 */
public class ConcessionContextPlus {
    Concession context = null;
    public ConcessionContextPlus(String method) throws Exception {
        switch (method){
            case "普通":
                context = new NormalConcession();
                break;
            case "八折卡":
                context = new RebateConcession();
                break;
            case "满减卡":
                context = new ReturnConcession();
                break;
            default:
                context = null;
                break;
        }
        if (context == null) {
            throw new Exception("方式错误");
        }
    }
    public Double doConcession(Double price){
        return context.doConcession(price);
    }
}

升级后的业务代码

java 复制代码
/**
 * 升级版业务(非常简洁,可读性高)
 */
public class MainPlus {
    public static void main(String[] args) throws Exception {
        Scanner scan = new Scanner(System.in);

        System.out.println("请输入原价格");
        double price = scan.nextDouble();

        System.out.println("请输入优惠方式");
        String method = scan.next();

        //实际上两步就完成了策略
        //传入优惠方式
        ConcessionContextPlus concessionContextPlus = new ConcessionContextPlus(method);
        //传入原价格
        Double concession = concessionContextPlus.doConcession(price);

        System.out.println("优惠后价格为" + concession);
    }
}

升级后的业务代码明显简单了很多。

总结

  • 策略模式能更好地管理和使用一类算法,在做算法增删改时,无需改动业务代码,只要对底层进行修改。使得代码的耦合度低,封装性强。
  • 策略模式结合简单工厂模式,让context起到工厂的作用,能让业务代码更为简洁,也更易于理解和使用。比起普通的策略模式,耦合度更低,封装性更强。
相关推荐
企鹅不耐热.11 分钟前
Scala基础知识6
开发语言·后端·scala
程序员一诺20 分钟前
【Django开发】前后端分离django美多商城项目第15篇:商品搜索,1. Haystack介绍和安装配置【附代码文档】
后端·python·django·框架
高 朗33 分钟前
2025高频面试设计模型总结篇
设计模式·面试·职场和发展
冷琅辞1 小时前
Go语言的嵌入式网络
开发语言·后端·golang
response_L1 小时前
国产系统统信uos和麒麟v10在线打开word给表格赋值
java·c#·word·信创·在线编辑
苹果酱05671 小时前
Golang标准库——runtime
java·vue.js·spring boot·mysql·课程设计
User_芊芊君子1 小时前
【Java】类和对象
java·开发语言
martian6651 小时前
Spring Boot后端开发全攻略:核心概念与实战指南
java·开发语言·spring boot
跟着珅聪学java3 小时前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
我命由我123453 小时前
Spring Boot 自定义日志打印(日志级别、logback-spring.xml 文件、自定义日志打印解读)
java·开发语言·jvm·spring boot·spring·java-ee·logback