Java设计模式之策略模式+工厂模式(反射和注解)

现在我们有一个需求,我们通常的实现方式是这样的。

  1. 假设有3种会员,分别为会员,超级会员以及金牌会员和普通顾客,针对不同类别的会员,有不同的打折方式,并且一个顾客每消费10000就增加一个级别

  2. 以上四种级别分别采用原价(普通顾客),九折(会员),八折(超级会员)和七折(金牌会员)的折扣方式

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 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 clazz = (Class) 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的策略模式,将具体的算法族封装起来,通过工厂类实例化具体的策略实例,来实际算出我们的需求。

你可能感兴趣的:(Java设计模式之策略模式+工厂模式(反射和注解))