工厂模式策略者模式责任链模式综合应用

Posted loveyoumi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了工厂模式策略者模式责任链模式综合应用相关的知识,希望对你有一定的参考价值。

设计模式的具体运用:

  简单工厂模式、策略者模式、责任链模式定义与使用

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 异常;





以上是关于工厂模式策略者模式责任链模式综合应用的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之策略模式与责任链模式详解和应用

Kotlin - 改良责任链模式

Kotlin - 改良责任链模式

Kotlin - 改良责任链模式

前端设计模式之责任链模式

软件设计模式