设计模式的具体运用:
简单工厂模式、策略者模式、责任链模式定义与使用
classLoader的具体运用
自定义的classloader 来动态加载类
程序功能设计:
在商城购物时,商城可能会在特殊的日子、或者依据会员等级,对结算的商品进行价格上的优惠,本篇将模拟价格计算时,优惠策略的动态选择和优惠策略的链式处理;
程序流程图:
图中有两种价格优惠计算的流程图:
流程2:价格优惠计算时直接采用责任链模式进行处理,设计和流程都比较简单,作者更倾向于这种流程设计;但为了练习和使用更多的设计模式,所以本篇采用了流程2的方法实现;
流程1:将价格优惠计算分成了特殊和固定价格优惠(比如:会员值。或则满额减等),这种流程模式在进入到特殊优惠条件时,可以采用责任链的方式实现,而固定价格优惠计算时,可以采用策略者模式进行实现;
UML 图:
源码:
定义价格策略接口
/**
* 商品价格的计算策略
*/
public interface PriceStrategy {
double calcPrice(double price);
}
定义一个注解,用于工厂动态加载和创建PriceStrategy(价格策略)对象
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FixedPrivilegePrice {
double max() default 0;
double min() default Integer.MAX_VALUE;
}
两个简单的价格策略算法的实现,这里利用注解的两个属性:min 和 max 属性,定义策略类的使用要求:即计算的价格必须在指定的价格区间内
@FixedPrivilegePrice(min=100,max=500)
public class SimpleStrategy1 implements PriceStrategy {
@Override
public double calcPrice(double price) {
return price * 0.8;
}
}
@FixedPrivilegePrice(min=500,max=Integer.MAX_VALUE)
public class SimpleStrategy2 implements PriceStrategy {
@Override
public double calcPrice(double price) {
return price * 0.7;
}
}
默认的价格优惠策略类,用于无可用策略时,将不对价格进行优惠
public class DefaultPriceStrategy implements PriceStrategy {
@Override
public double calcPrice(double price) {
return price;
}
}
定义特殊价格的优惠策略接口,用于特殊价格优惠策略的链式处理;next() 方法在这里是责任链模式实现的关键;
/**
* 用于价格计算时,提供特殊的价格优惠算法
*
*/
public interface PricePrivilege extends PriceStrategy {
PricePrivilege next();
void register(PricePrivilege pp);
}
抽象类,实现对象链的选择和注册;
public abstract class AbstractPricePrivilege implements PricePrivilege {
protected PricePrivilege next;
public PricePrivilege next() {
return next;
}
@Override
public void register(PricePrivilege pp) {
next = pp;
}
}
特殊价格优惠策略两个简单实现:
public class Birthday extends AbstractPricePrivilege {
@Override
public double calcPrice(double price) {
//会员生日一律9折优惠
return price * 0.9;
}
}
public class PreferentialCar extends AbstractPricePrivilege {
@Override
public double calcPrice(double price) {
// 这是一张满100百减10的优惠卡
if(price >= 100)
price -= 10;
return price;
}
}
PricePrivilege 的工厂接口
public interface IPriceStratedyFactory {
PriceStrategy createAndGet(double price);
}
PricePrivilege 的工厂实现, 这个工厂类是动态加载和动态选择策略的核心: 1, 动态加载策略: 该工厂将从给定的一个目录中,动态加载可用的所有策略,并将这些策略保存到一个链表中;(并且可以提供一个动态添加和删除策略的接口,或者在从给定目录中动态加载策略时,启动一个定时检查器,用于间断性添加和删除策略,但这种方法 需要考虑策略的实效性;———————————— 后续思想,本篇未实现;) 2,动态策略选择: 每一个策略类都使用了FixedPrivilegePrice 这个注解,本篇该注解使用了两个属性:min 和max ,表示价格的最大值和最小值,当结算价格在这个区间时,就选择这种策略;当然这只是简单的价格判断,也可以根据实际添加其他的属性用于策略的选择;
import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.util.HashSet;
public class PriceStrategyFactory implements IPriceStratedyFactory {
public PriceStrategy createAndGet(double price) {
PriceStrategy ps = findPriceStrategyInHolder(price);
if(ps == null) {
ps = PriceStrategyHolder.DEFAULT_PRICE_STRATEGY;
}
return ps;
}
private PriceStrategy findPriceStrategyInHolder(double price) {
for(PriceStrategy ps : PriceStrategyHolder.priceStrategy) {
FixedPrivilegePrice fpp = ps.getClass().getAnnotation(FixedPrivilegePrice.class);
if(matchCondition(price ,fpp))
return ps;
}
return null;
}
private boolean matchCondition(double price ,FixedPrivilegePrice fpp) {
return price >= fpp.min() && price <= fpp.max();
}
static private class PriceStrategyHolder {
static private final PriceStrategy DEFAULT_PRICE_STRATEGY = new DefaultPriceStrategy();
static private HashSet<PriceStrategy> priceStrategy = new HashSet<>();
static final private LoadPriceStrategyUtil LOAD_UTIL = new LoadPriceStrategyUtil();
static {
LOAD_UTIL.classLoder(priceStrategy);
}
}
static private class LoadPriceStrategyUtil{
static final private String STRATEGY_URL = "learn.design.model.demo1";
void classLoder(HashSet<PriceStrategy> pss) {
String[] classFiles = findClassFileFromPath();
if(classFiles == null) return;
DynamicLoaderClass dlc = new DynamicLoaderClass();
for(String classFileName : classFiles) {
try {
String className = generateClassName(classFileName);
Class<?> cls = dlc.loadClass(className);
FixedPrivilegePrice fpp = cls.getAnnotation(FixedPrivilegePrice.class);
if(fpp != null ) {
pss.add((PriceStrategy) cls.newInstance());
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e1) {
}
}
}
String generateClassName(String classFileName) {
return STRATEGY_URL + "." + classFileName.substring(0 ,classFileName.indexOf("."));
}
String[] findClassFileFromPath() {
//符号替换
String loaderUrl = STRATEGY_URL.replace(".", "/");
URL url = ClassLoader.getSystemResource(loaderUrl);
if(url == null)return null;
return new File(url.getPath()).list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
});
}
}
}
提供一个自定义的类加载器工具,用于动态的加载给定目录下的class文件,默认从项目的classpath路径下查找;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DynamicLoaderClass extends ClassLoader {
private String path;
public DynamicLoaderClass(){
this(Thread.currentThread().getContextClassLoader().getResource("").getPath());
}
public DynamicLoaderClass(String path) {
this.path = path;
}
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
Class<?> cls = loadClassByParent(className);
if(cls == null)
cls = loadClassBySelf(className);
return cls;
}
private Class<?> loadClassBySelf(String className) throws ClassNotFoundException {
return findClass(className);
}
private Class<?> loadClassByParent(String className){
Class<?> cls = null;
try{
cls = super.loadClass(className);
}catch (ClassNotFoundException e) {
//load class failed;
}
return cls;
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] b = getResourceForCurrentPath(className);
return defineClass(className, b, 0, b.length);
}
private byte[] getResourceForCurrentPath(String className) throws ClassNotFoundException{
return getResourceForPath(genrateResourcePath(className));
}
private byte[] getResourceForPath(String resourcePath) throws ClassNotFoundException {
checkResourceIsExist(resourcePath);
return readResource(resourcePath);
}
private byte[] readResource(String resourcePath) throws ClassNotFoundException {
byte[] result = null;
File file = new File(resourcePath);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
result = new byte[fis.available()];
fis.read(result);
fis.close();
} catch (FileNotFoundException e) {
// This should only happen with multithreading
} catch (IOException e) {
throw new ClassNotFoundException();
}
return result;
}
private void checkResourceIsExist(String resourcePath) throws ClassNotFoundException {
File file = new File(resourcePath);
if(!file.exists())
throw new ClassNotFoundException();
}
private String genrateResourcePath(String className){
return path + "/" + className.replace(".", "/") +".class";
}
}
价格处理者接口,接口规定:价格的最终结算价格将在这个接口中处理完成;用户不需要关心如何完成,以及是否使用了价格的策略,相当于将所有的价格优惠策略都封装和隐藏起来,实现与客户端的分离
/**
* 提供对商品价格的计算方式
*
*/
public interface PriceHandler {
double calcPrice(double price ,PricePrivilege pp);
}
定义一个书记类型商品的价格处理器,
1,该类将从用户那里获取用户可以使用的特殊优惠策略,本篇中直接传入了特殊优惠策略的对象,这里是可以改进的:传入特殊优惠策略的满足条件,比如:用户领取了一张优惠卡,就传入该优惠卡的标识即可;
2,该类依赖一个与一个价格策略的工厂接口,该类将通过该工厂接口动态的选择一个策略;
public class BookPriceHandler implements PriceHandler{
private IPriceStratedyFactory ipsf;
public BookPriceHandler() {
this(new PriceStrategyFactory());
}
public BookPriceHandler(IPriceStratedyFactory ipsf) {
this.ipsf = ipsf;
}
@Override
public double calcPrice(double price, PricePrivilege pp) {
//特权价格
price = privilegePrice(price, pp);
//固定策略
price = ipsf.createAndGet(price).calcPrice(price);
return price;
}
private double privilegePrice(double price , PricePrivilege pp) {
if(pp == null) return price;
return privilegePrice(pp.calcPrice(price), pp.next());
}
}
demo:
public class Demo {
public static void main(String[] args) {
BookPriceHandler bphandler = new BookPriceHandler();
PricePrivilege pc = new PreferentialCar();
PricePrivilege b = new Birthday();
b.register(pc);
double price = bphandler.calcPrice(500, b);
System.out.println(price);
}
}
使用注意:请将策略类放置与PriceStrategyFactory $LoadPriceStrategyUtil.STRATEGY_URL所指定的包路径下,否则将抛出classnotfound 异常;