设计模式-策略模式

概述

  • 定义了一系列的算法,并将每一个算法封装起来,使得它们可以相互替换。这种模式让算法独立于使用它的客户而变化,也就是说,客户端可以根据需要在运行时动态地改变对象的行为。
  • 例如,在电商场景中,各种优惠策略(新用户折扣、满减、打折等)作为具体策略类,均实现同一策略接口;上下文为订单结算模块,根据用户类型灵活调用不同策略进行价格计算。这一模式有效增强了系统灵活性和扩展性,遵循开闭原则,易于扩展而不易修改原有代码。

核心思想

  • Context 上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
  • Strategy 策略角色:抽象策略角色,是对策略、算法的抽象,定义每个策略或算法必须具有的方法和属性
  • ConcreteStrategy 具体策略角色:用于实现抽象策略中的操作,即实现具体的算法

场景使用

  • 计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略
  • Java AWT中的LayoutManager,即布局管理器
  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式
  • 不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法

优缺点

优点
  • 满足开闭原则,当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例
  • 避免使用多重条件判断,如果不用策略模式可能会使用多重条件语句不利于维护,和工厂模式的搭配使用可以很好地消除代码if-else的多层嵌套(工厂模式主要是根据参数,获取不同的策略)
缺点
  • 策略类数量会增多,每个策略都是一个类,复用的可能性很小
  • 对外暴露了类所有的行为和算法,行为过多导致策略类膨胀

示例

JDK源码的应用

Student 类
java 复制代码
public class Student {

    private String name;

    private Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
实现降序
java 复制代码
import net.laow.strategy.Student;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorTest {

    @Test
    public void comparatorSortTest(){
        List<Student> list = new ArrayList<>();
        list.add(new Student("王二", 20));
        list.add(new Student("张三", 15));
        list.add(new Student("李四", 18));

        // 对学生的集合按年龄进行排序
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // 降序
                 return s2.getAge()-s1.getAge();
            }
        });

        System.out.println(list);
    }
}
实现升序
java 复制代码
import net.laow.strategy.Student;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorTest {

    @Test
    public void comparatorSortTest(){
        List<Student> list = new ArrayList<>();
        list.add(new Student("王二", 20));
        list.add(new Student("张三", 15));
        list.add(new Student("李四", 18));

        // 对学生的集合按年龄进行排序
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // 升序
                return s1.getAge()-s2.getAge();
            }
        });

        System.out.println(list);
    }
}

场景应用

需求
  • 小美是电商项目的营销活动组,负责多个营销活动,有折扣、优惠券抵扣、满减等,项目上线后,产品经理找茬,经常新增营销活动,导致代码改动多,加班严重搞的小美很恼火。
  • 她发现这些都是活动策略,商品的价格是根据不同的活动策略进行计算的,因此用策略设计模式进行了优化,后续新增策略后只要简单配置就行了,不用大动干戈
ProductOrder 类
java 复制代码
public class ProductOrder {

    private double oldPrice;

    private int userId;

    private int productId;


    public ProductOrder(double oldPrice, int userId, int productId) {
        this.oldPrice = oldPrice;
        this.userId = userId;
        this.productId = productId;
    }

    public double getOldPrice() {
        return oldPrice;
    }

    public void setOldPrice(double oldPrice) {
        this.oldPrice = oldPrice;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public int getProductId() {
        return productId;
    }

    public void setProductId(int productId) {
        this.productId = productId;
    }
}
Strategy 策略抽象
java 复制代码
public abstract class Strategy {
    /**
     *  根据简单订单对象,计算商品折扣后的价格
     * @param productOrder
     * @return
     */
    public abstract double computePrice(ProductOrder productOrder);
}
PromotionContext 策略上下文
java 复制代码
public class PromotionActivity {
    private Strategy strategy;

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

    /**
     * 根据策略计算最终的价格
     *
     * @param productOrder
     * @return
     */
    public double executeStrategy(ProductOrder productOrder) {
        return strategy.computePrice(productOrder);
    }
}
NormalActivity 无活动策略
java 复制代码
public class NormalActivity extends Strategy{
    @Override
    public double computePrice(ProductOrder productOrder) {
        return productOrder.getOldPrice();
    }
}
DiscountActivity 折扣策略
java 复制代码
public class DiscountActivity extends Strategy {
    /**
     * 具体的折扣
     */
    private double rate;

    public DiscountActivity(double rate) {
        this.rate = rate;
    }

    @Override
    public double computePrice(ProductOrder productOrder) {
        //业务逻辑计算
        return productOrder.getOldPrice() * rate;
    }
}
VoucherActivity 优惠卷策略
java 复制代码
public class VoucherActivity extends Strategy {

    /**
     * 传入优惠券
     */
    private double voucher;

    public VoucherActivity(double voucher) {
        this.voucher = voucher;
    }

    @Override
    public double computePrice(ProductOrder productOrder) {
        if (productOrder.getOldPrice() > voucher) {
            return productOrder.getOldPrice() - voucher;
        } else {
            return 0;
        }
    }
}
测试
java 复制代码
public class Main {
    public static void main(String[] args) {
        ProductOrder productOrder = new ProductOrder(800,1,32);
        PromotionContext context;
        double finalPrice;
        //不同策略算出不同的活动价格
        //没活动
        context = new PromotionContext(new NormalActivity());
        finalPrice = context.executeStrategy(productOrder);
        System.out.println("NormalActivity = "+finalPrice);
        //折扣策略
        context = new PromotionContext(new DiscountActivity(0.8));
        finalPrice = context.executeStrategy(productOrder);
        System.out.println("DiscountActivity = "+finalPrice);
        //优惠券抵扣
        context = new PromotionContext(new VoucherActivity(100));
        finalPrice = context.executeStrategy(productOrder);
        System.out.println("VoucherActivity = "+finalPrice);
    }
}
相关推荐
奋进的芋圆5 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin6 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20056 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉6 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国6 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882486 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈7 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_997 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹7 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理
专注_每天进步一点点7 小时前
【java开发】写接口文档的札记
java·开发语言