一、背景
- 作为一个开发人员,需要掌握面向对象开发各种基础知识,同时也需要了解各种设计原则与设计模式;熟练掌握设计模式,可以让我们开发出复用高,扩展性强、易于维护的系统。
- 掌握设计模式的理论知识不难,难就难在怎么在实际项目中具体应用,或者说哪个应用场景适合应用哪个设计模式。
- 本文将通过一个实际的营销策略例子来说明策略模式的具体适用场景
二、策略模式
- 策略模式(Strategy Pattern)是一种比较简单的设计模式,其核心思想是定义算法族,分别封装起来,让它们之间可以互相替换。背后的设计原则是:①封装变化;②多用组合,少用继承;③针对接口编程,不针对实现编程。
- 我们将举营销策略中订单折扣的例子来说明:
- 公司会在每个季度根据市场情况推出各种营销策略
- 客户下单时享受公司正在执行的营销策略,形成订单折扣
2.1 未用设计模式
- 为了做比较,我们看下未用设计模式的例子是怎么样的:
- 创建一个客户类
public class Customer {
private String customerCode;
private String customerName;
private String customerClass;
public Customer(String customerCode, String customerName, String customerClass) {
this.customerCode = customerCode;
this.customerName = customerName;
this.customerClass = customerClass;
}
public String getCustomerCode() {
return customerCode;
}
public void setCustomerCode(String customerCode) {
this.customerCode = customerCode;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerClass() {
return customerClass;
}
public void setCustomerClass(String customerClass) {
this.customerClass = customerClass;
}
}
- 创建一个订单类
public class Order {
private Customer customer;
private String product;
private BigDecimal num;
private BigDecimal price;
private BigDecimal discount;
private BigDecimal amount;
private BigDecimal actualAmount;
public Order(Customer customer, String product, BigDecimal num, BigDecimal price) {
this.customer = customer;
this.product = product;
this.num = num;
this.price = price;
this.amount = num.multiply(price);
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public BigDecimal getNum() {
return num;
}
public void setNum(BigDecimal num) {
this.num = num;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getDiscount() {
return discount;
}
public void setDiscount(BigDecimal discount) {
this.discount = discount;
this.actualAmount = num.multiply(price).multiply(discount);
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public BigDecimal getActualAmount() {
return actualAmount;
}
public void setActualAmount(BigDecimal actualAmount) {
this.actualAmount = actualAmount;
}
@Override
public String toString() {
return "Order{" +
"customer=" + customer.getCustomerName() +
", product='" + product + '\'' +
", num=" + num +
", price=" + price +
", discount=" + discount +
", amount=" + amount +
", actualAmount=" + actualAmount +
'}';
}
}
- 设计一个营销策略,形成订单折扣
public class Sales {
public static void main(String[] args) {
Customer customer1 = new Customer("001","上海客户","A");
Customer customer2 = new Customer("002","北京客户","B");
Customer customer3 = new Customer("003","广州客户","C");
Customer customer4 = new Customer("004","深圳客户","D");
Order order1 = new Order(customer1,"产品", BigDecimal.valueOf(1000),BigDecimal.valueOf(10.5));
Order order2 = new Order(customer2,"产品", BigDecimal.valueOf(2000),BigDecimal.valueOf(10.5));
Order order3 = new Order(customer3,"产品", BigDecimal.valueOf(3000),BigDecimal.valueOf(10.5));
Order order4 = new Order(customer4,"产品", BigDecimal.valueOf(4000),BigDecimal.valueOf(10.5));
order1.setDiscount(calcDiscount1(customer1.getCustomerClass()));
order2.setDiscount(calcDiscount1(customer2.getCustomerClass()));
order3.setDiscount(calcDiscount1(customer3.getCustomerClass()));
order4.setDiscount(calcDiscount1(customer4.getCustomerClass()));
System.out.println(order1.toString());
System.out.println(order2.toString());
System.out.println(order3.toString());
System.out.println(order4.toString());
}
private static BigDecimal calcDiscount1(String customerClass){
switch (customerClass){
case "A":
return BigDecimal.valueOf(0.8);
case "B":
return BigDecimal.valueOf(0.9);
case "C":
return BigDecimal.valueOf(0.95);
default:
return BigDecimal.valueOf(1);
}
}
}
- 根据客户级别计算出了订单的折扣
- 以上的代码看起来简单明了,但公司的营销策略是动态变化的,到了下季度,营销策略变成了按订单下单量的多少来进行打折,那我们需要对程序做以下修补:
public class Sales {
public static void main(String[] args) {
Customer customer1 = new Customer("001","上海客户","A");
Customer customer2 = new Customer("002","北京客户","B");
Customer customer3 = new Customer("003","广州客户","C");
Customer customer4 = new Customer("004","深圳客户","D");
Order order1 = new Order(customer1,"产品", BigDecimal.valueOf(1000),BigDecimal.valueOf(10.5));
Order order2 = new Order(customer2,"产品", BigDecimal.valueOf(2000),BigDecimal.valueOf(10.5));
Order order3 = new Order(customer3,"产品", BigDecimal.valueOf(3000),BigDecimal.valueOf(10.5));
Order order4 = new Order(customer4,"产品", BigDecimal.valueOf(4000),BigDecimal.valueOf(10.5));
order1.setDiscount(calcDiscount2(order1.getNum()));
order2.setDiscount(calcDiscount2(order2.getNum()));
order3.setDiscount(calcDiscount2(order3.getNum()));
order4.setDiscount(calcDiscount2(order4.getNum()));
System.out.println(order1.toString());
System.out.println(order2.toString());
System.out.println(order3.toString());
System.out.println(order4.toString());
}
private static BigDecimal calcDiscount1(String customerClass){
switch (customerClass){
case "A":
return BigDecimal.valueOf(0.8);
case "B":
return BigDecimal.valueOf(0.9);
case "C":
return BigDecimal.valueOf(0.95);
default:
return BigDecimal.valueOf(1);
}
}
private static BigDecimal calcDiscount2(BigDecimal num){
if (num.compareTo(BigDecimal.valueOf(4000))>=0) {
return BigDecimal.valueOf(0.8);
} else if (num.compareTo(BigDecimal.valueOf(3000))>=0) {
return BigDecimal.valueOf(0.9);
} else if (num.compareTo(BigDecimal.valueOf(2000))>=0) {
return BigDecimal.valueOf(0.95);
}
return BigDecimal.valueOf(1);
}
}
- 从以上两段代码可以看到,公司的营销策略不是简单的根据同一种模式进行调整折扣系数,而是模式发生了实质性的变化:比如1季度需要按客户级别进行折扣,2季度又变换成了按订单量多少进行折扣,3季度又变成了按产品类别不同进行折扣…
2.2 采用设计模式
- 这些营销策略的变换实际上就是折扣算法的变换,再想象一下采用策略模式应该怎么做:我们可以将这些算法独立出来并进行分别封装,形成算法族,在订单下达时根据当前启用的策略来调用不同的算法。
- 定义一个营销策略的接口
public interface SalesStrategy {
BigDecimal calcDiscount(Order order);
}
- 通过继承接口分别实现不同的计算折扣的算法
public class SalesStrategyByCustomerClass implements SalesStrategy {
@Override
public BigDecimal calcDiscount(Order order) {
switch (order.getCustomer().getCustomerClass()) {
case "A":
return BigDecimal.valueOf(0.8);
case "B":
return BigDecimal.valueOf(0.9);
case "C":
return BigDecimal.valueOf(0.95);
default:
return BigDecimal.valueOf(1);
}
}
@Override
public String toString() {
return "SalesStrategyByCustomer";
}
}
public class SalesStrategyByOrderNum implements SalesStrategy {
@Override
public BigDecimal calcDiscount(Order order) {
if (order.getNum().compareTo(BigDecimal.valueOf(4000))>=0) {
return BigDecimal.valueOf(0.8);
} else if (order.getNum().compareTo(BigDecimal.valueOf(3000))>=0) {
return BigDecimal.valueOf(0.9);
} else if (order.getNum().compareTo(BigDecimal.valueOf(2000))>=0) {
return BigDecimal.valueOf(0.95);
}
return BigDecimal.valueOf(1);
}
@Override
public String toString() {
return "SalesStrategyByOrderNum";
}
}
- 改写订单类,加入营销策略变量,及折扣计算
public class Order {
...
private SalesStrategy salesStrategy;
...
public SalesStrategy getSalesStrategy() {
return salesStrategy;
}
public void setSalesStrategy(SalesStrategy salesStrategy) {
this.salesStrategy = salesStrategy;
setDiscount(this.salesStrategy.calcDiscount(this));
}
@Override
public String toString() {
return "Order{" +
"customer=" + customer.getCustomerName() +
", product='" + product + '\'' +
", num=" + num +
", price=" + price +
", salesStrategy=" + salesStrategy.toString() +
", discount=" + discount +
", amount=" + amount +
", actualAmount=" + actualAmount +
'}';
}
}
- 最后改写主程序,满足可维护性,可扩展性原则
public class Sales {
public static void main(String[] args) {
Customer customer1 = new Customer("001","上海客户","A");
Customer customer2 = new Customer("002","北京客户","B");
Customer customer3 = new Customer("003","广州客户","C");
Customer customer4 = new Customer("004","深圳客户","D");
Order order1 = new Order(customer1,"产品", BigDecimal.valueOf(1000),BigDecimal.valueOf(10.5));
Order order2 = new Order(customer2,"产品", BigDecimal.valueOf(2000),BigDecimal.valueOf(10.5));
Order order3 = new Order(customer3,"产品", BigDecimal.valueOf(3000),BigDecimal.valueOf(10.5));
Order order4 = new Order(customer4,"产品", BigDecimal.valueOf(4000),BigDecimal.valueOf(10.5));
SalesStrategy salesStrategy =getSalesStrategy();
order1.setSalesStrategy(salesStrategy);
order2.setSalesStrategy(salesStrategy);
order3.setSalesStrategy(salesStrategy);
order4.setSalesStrategy(salesStrategy);
System.out.println(order1.toString());
System.out.println(order2.toString());
System.out.println(order3.toString());
System.out.println(order4.toString());
}
private static SalesStrategy getSalesStrategy(){
return new SalesStrategyByCustomerClass();
}
}
- 分别模拟两种营销策略的执行
三、总结
- 首先策略模式必须要有操作策略的上下文环境,在本例子中就是订单;
- 接着要抽象策略类(Strategy): 在本例中就是计算订单折扣这个接口;
- 最后要有具体的策略实现,在本例中就是各种订单折扣的算法实现。