设计模式-策略模式

概述

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

核心思想

  • 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);
    }
}
相关推荐
魔道不误砍柴功1 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2341 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨1 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle4 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^4 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋34 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花4 小时前
【JAVA基础】Java集合基础
java·开发语言·windows