现在我们有一个需求,我们通常的实现方式是这样的。
假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,并且一个顾客每消费10000就增加一个级别
以上四种级别分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式
package com.example.demo.designpattern;
/**
* 假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,
* 并且一个顾客每消费10000就增加一个级别
* 以上四种会员分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式。
*/
public class Settlement {
/**
* 总价
*/
private double totalPrice = 0;
/**
* 单次消费的金额
*/
private double amount = 0;
/**
* 购买的方法
* @param amount 商品价格
* @return double 支付的价格
*/
public double buy(double amount) throws Exception {
this.amount = amount;
this.totalPrice += this.amount;
if(this.totalPrice < 10000) {
return this.amount;
} else if(this.totalPrice >= 10000 && this.totalPrice < 20000) {
return this.amount * 0.9;
} else if(this.totalPrice >= 20000 && this.totalPrice < 30000) {
return this.amount * 0.8;
} else {
return this.amount * 0.7;
}
}
}
这种面向过程的实现方式,再以后的维护和新增会员类型的时候,得需要重新添加条件判断,来满足我们的业务需求变化,是一种不灵活的设计方式。 下面我们可以改造改造这个方法,把打折方式的算法可以封装起来,以后有折扣变化,我们只需要修改这些会变的封装类就行了。 我们也称为这种封装算法族的方式为策略模式,一个算法族就是一个策略。下面我们定义一个策略接口,不同的折扣实现这个接口。
//策略接口
package com.example.demo.designpattern;
public interface CallPrice {
public double callPrice(double price);
}
package com.example.demo.designpattern;
/**
* 普通顾客 原价
*/
public class OrdinaryPlayer implements CallPrice {
@Override
public double callPrice(double price) {
return price;
}
}
package com.example.demo.designpattern;
/**
* 会员 9折
*/
public class Member implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.9;
}
}
package com.example.demo.designpattern;
/**
* 超级会员 8折
*/
public class SuperMember implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.8;
}
}
package com.example.demo.designpattern;
/**
* 金牌会员 7折
*/
public class GoldMember implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.7;
}
}
package com.example.demo.designpattern;
/**
* 假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,
* 并且一个顾客每消费10000就增加一个级别
* 以上四种会员分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式。
*/
public class Settlement {
/**
* 总价
*/
private double totalPrice = 0;
/**
* 单次消费的金额
*/
private double amount = 0;
/**
* 策略类
*/
private CallPrice callPrice;
/**
* 购买的方法
* @param amount
* @return double
*/
public double buy(double amount) throws Exception {
this.amount = amount;
this.totalPrice += this.amount;
if(this.totalPrice < 10000) {
callPrice = new OrdinaryPlayer();
} else if(this.totalPrice >= 10000 && this.totalPrice < 20000) {
callPrice = new Member();
} else if(this.totalPrice >= 20000 && this.totalPrice < 30000) {
callPrice = new SuperMember();
} else {
callPrice = new GoldMember();
}
return callPrice.callPrice(this.amount);
}
}
以上的设计,只要以后价格有变化,我们就可以增加新的折扣类就行了。我们还可以继续改造,用工厂模式来帮我们生成对应的策略类。
package com.example.demo.designpattern;
/**
* 策略工厂类,返回具体的策略实例
*/
public class PriceFactory {
/**
* 获得对应金额的策略类
* @param price
* @return
* @throws Exception
*/
public CallPrice getCallPrice(double price) throws Exception {
if(price < 10000) {
return new OrdinaryPlayer();
} else if(price >= 10000 && price < 20000) {
return new Member();
} else if(price >= 20000 && price < 30000) {
return new SuperMember();
} else {
return new GoldMember();
}
}
/**
* 获得工厂实例
* @return
*/
public static PriceFactory getInstance() {
return new PriceFactory();
}
}
接下来改造我们的购买方法。
package com.example.demo.designpattern;
/**
* 假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,
* 并且一个顾客每消费10000就增加一个级别
* 以上四种会员分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式。
*/
public class Settlement {
/**
* 总价
*/
private double totalPrice = 0;
/**
* 单次消费的金额
*/
private double amount = 0;
/**
* 策略类
*/
private CallPrice callPrice;
/**
* 购买的方法
* @param amount
* @return double
*/
public double buy(double amount) throws Exception {
this.amount = amount;
this.totalPrice += this.amount;
callPrice = PriceFactory.getInstance().getCallPrice(this.amount);
return callPrice.callPrice(this.amount);
}
}
这样我们的购买方法就更加简洁了。以后有更多的策略,我们只需要修改策略工厂就行了,满足我们的实时变化。但是还是如果我们的折扣方式有很多种呢,那我们就需要写很多的if else 判断,代码的可读性就变得很差。然后我们就可以采用注解和反射的方式, 来改造我们的工厂类。
首先,我们需要在原来的策略类里,添加一个自定义的注解,注解有两个默认值,也就是最大值和最小值,通过最大值最小值来判断,我们具体要实例化的策略类。
package com.example.demo.designpattern;
/**
* 普通顾客 原价
*/
@PriceRegion(max = 10000)
public class OrdinaryPlayer implements CallPrice {
@Override
public double callPrice(double price) {
return price;
}
}
package com.example.demo.designpattern;
/**
* 会员 9折
*/
@PriceRegion(max = 20000, min = 10000)
public class Member implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.9;
}
}
package com.example.demo.designpattern;
/**
* 超级会员 8折
*/
@PriceRegion(min = 20000, max = 30000)
public class SuperMember implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.8;
}
}
package com.example.demo.designpattern;
/**
* 金牌会员 7折
*/
@PriceRegion(min = 30000)
public class GoldMember implements CallPrice {
@Override
public double callPrice(double price) {
return price * 0.7;
}
}
package com.example.demo.designpattern;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,声明最大值和最小值俩变量
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PriceRegion {
int min() default Integer.MIN_VALUE;
int max() default Integer.MAX_VALUE;
}
package com.example.demo.designpattern;
public class Settlement {
/**
* 总价
*/
private double totalPrice = 0;
/**
* 单次消费的金额
*/
private double amount = 0;
private CallPrice callPrice;
public double buy(double amount) throws Exception {
this.amount = amount;
this.totalPrice += this.amount;
callPrice = PriceFactory.getInstance().getCallPrice(this.totalPrice);
return callPrice.callPrice(this.amount);
}
}
package com.example.demo.designpattern;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 策略工厂类,返回具体的策略实例
*/
public class PriceFactory {
/**
* 要扫描的策略类的包
*/
public static final String SCAN_PACKAGE = PriceFactory.class.getPackage().getName();
/**
* 策略类的集合
*/
private List> callPriceList = new ArrayList<>();
public PriceFactory() {
try {
getCallPriceList();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获得对应金额的策略类
* @param price
* @return
* @throws Exception
*/
public CallPrice getCallPrice(double price) throws Exception {
for (Class extends CallPrice> clazz : callPriceList) {
PriceRegion priceRegion = clazz.getAnnotation(PriceRegion.class);
if (priceRegion.max() > price && priceRegion.min() < price) {
return clazz.newInstance();
}
}
return null;
}
/**
* 扫描策略类,添加到集合
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public List> getCallPriceList() throws ClassNotFoundException {
String path = System.getProperty("user.dir") + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator + "" + SCAN_PACKAGE.replace(".", File.separator) + File.separator;
File file = new File(path);
String[] strs = file.list();
for (String str : strs) {
String forName = SCAN_PACKAGE+"."+str.replace(".java", "");
Class extends CallPrice> clazz = (Class extends CallPrice>) Class.forName(forName);
if (clazz.isAnnotationPresent(PriceRegion.class)) {
callPriceList.add(clazz);
}
}
return null;
}
/**
* 获得工厂实例
* @return
*/
public static PriceFactory getInstance() {
return new PriceFactory();
}
}
package com.example.demo.designpattern;
public class Client {
public static void main(String[] args) throws Exception {
Settlement settlement = new Settlement();
System.out.println("顾客需支付:" + settlement.buy(5000));
System.out.println("顾客需支付:" + settlement.buy(10000));
System.out.println("顾客需支付:" + settlement.buy(10000));
System.out.println("顾客需支付:" + settlement.buy(10000));
}
}
以上的修改,就能达到一个代码的复用性和可读性。提高了以后代码的扩展性。 通过这个简单例子让我们知道了策略模式和工厂模式的应用场景。
Java的策略模式,将具体的算法族封装起来,通过工厂类实例化具体的策略实例,来实际算出我们的需求。