策略模式之商场促销

Posted by 李小武 on May 13, 2010

每到5.1、10.1等节日,都会看到商场里人山人海。商场之所以如此吸引人,是因为疯狂的折扣。

下面以商场打折为例,谈谈策略模式。

商品的折扣算法:

算法一:对于有的商品,没有折扣。

算法二:对于有的商品,采取固定金额的折扣。

算法三:对于有的商品,采取百分比的折扣。

解决方案:

方案一:将所有的业务逻辑放在客户端。客户端根据参数选择具体的算法,但是这样客户端会变得复杂而且难以维护。

方案二:客户端利用不同层次的继承在子类中实现不同的行为。但这样使算法跟客户端紧密耦合在一起。

方案三:策略模式。把策略算法和客户端分隔开。这样都新的打折算法也不必修改客户端。

模式的实现:

类图:

抽象折扣类:

抽象折扣类代码:

abstract public class DiscountStrategy
{
    /**
     * 商品单价。
     */
    protected double price=0.0d;

    /**
     * 商品数量。
     */
    protected int copies=0;

    /**
     * <b>构造方法。</b>  
     * <p><b>详细说明:</b></p>
     * <!-- 在此添加详细说明 -->
     * 无。
     * @param price
     * @param copies
     */
    public DiscountStrategy(double price,int copies)
    {
        this.price=price;
        this.copies=copies;
    }

    /**
     * <b>折扣计算。</b>  
     * <p><b>详细说明:</b></p>
     * <!-- 在此添加详细说明 -->
     * 无。
     * @return
     */
    public abstract double calculateDiscount();
    public double getPrice()
    {
        return price;
    }
    public void setPrice(double price)
    {
        this.price = price;
    }
    public int getCopies()
    {
        return copies;
    }
    public void setCopies(int copies)
    {
        this.copies = copies;
    }
}

无折扣策略类:

无折扣策略类代码:

public class NoDiscountStrategy extends DiscountStrategy
{
    /**
     * <b>构造方法。</b>  
     * <p><b>详细说明:</b></p>
     * <!-- 在此添加详细说明 -->
     * 无。
     * @param price
     * @param copies
     */
    public NoDiscountStrategy(double price, int copies)
    {
        super(price, copies);
    }
    /** 
     * <b>calculateDiscount。</b>  
     * @see oliver.designpattern.strategy.DiscountStrategy#calculateDiscount()
     */
    @Override
    public double calculateDiscount()
    {
        return 0;
    }
}

固定折扣策略类:

固定折扣策略类代码:

public class FlatRateStrategy extends DiscountStrategy
{
    /**
     * 固定折扣价格。
     */
    private double amount;
    /**
     * <b>构造方法。</b>  
     * <p><b>详细说明:</b></p>
     * <!-- 在此添加详细说明 -->
     * 无。
     * @param price
     * @param copies
     * @param amount
     */
    public FlatRateStrategy(double price, int copies,double amount)
    {
        super(price, copies);
        this.amount=amount;
    }
    /**
     * <b>calculateDiscount。</b>  
     * @see oliver.designpattern.strategy.DiscountStrategy#calculateDiscount()
     */
    @Override
    public double calculateDiscount()
    {
        return amount*copies;
    }
    public double getAmount()
    {
        return amount;
    }
    public void setAmount(double amount)
    {
        this.amount = amount;
    }
}

百分比折扣策略类:

百分比折扣策略代码:

public class PercentageStrategy extends DiscountStrategy
{
    /**
     * 打折百分比。
     */
    private double percent;
    /**
     * <b>构造方法。</b>  
     * <p><b>详细说明:</b></p>
     * <!-- 在此添加详细说明 -->
     * 无。
     * @param price
     * @param copies
     * @param percent
     */
    public PercentageStrategy(double price, int copies,double percent)
    {
        super(price, copies);
        this.percent=percent;
    }
    /**
     * <b>calculateDiscount。</b>  
     * @see oliver.designpattern.strategy.DiscountStrategy#calculateDiscount()
     */
    @Override
    public double calculateDiscount()
    {
        return price*copies*percent;
    }
    public double getPercent()
    {
        return percent;
    }
    public void setPercent(double percent)
    {
        this.percent = percent;
    }
}

 

测试代码:

商品类——图书:

public class Book
{
    /**
     * 打折策略。
     */
    private DiscountStrategy discountStrategy;
    public DiscountStrategy getDiscountStrategy()
    {
        return discountStrategy;
    }
    public void setDiscountStrategy(DiscountStrategy discountStrategy)
    {
        this.discountStrategy = discountStrategy;
    }
}

 

商品类——水果:

public class Fruit
{
    /**
     * 打折策略。
     */
    private DiscountStrategy discountStrategy;
    public DiscountStrategy getDiscountStrategy()
    {
        return discountStrategy;
    }
    public void setDiscountStrategy(DiscountStrategy discountStrategy)
    {
        this.discountStrategy = discountStrategy;
    }
}

商品类——玩具:

public class Toy
{
    /**
     * 打折策略。
     */
    private DiscountStrategy discountStrategy;
    public DiscountStrategy getDiscountStrategy()
    {
        return discountStrategy;
    }
    public void setDiscountStrategy(DiscountStrategy discountStrategy)
    {
        this.discountStrategy = discountStrategy;
    }
}

主类:

public class SuperMarket
{
    public static void main(String[] args)
    {

        Book englishBook = new Book();
        englishBook.setDiscountStrategy(new PercentageStrategy(20.0d,5,0.2));
        Fruit apple = new Fruit();
        apple.setDiscountStrategy(new FlatRateStrategy(1.3,30,0.1));
        Toy car = new Toy();
        car.setDiscountStrategy(new NoDiscountStrategy(50.0,1));

        System.out.println("EnglishBoos Discount:"+englishBook.getDiscountStrategy().calculateDiscount());
        System.out.println("Apple Discount:"+apple.getDiscountStrategy().calculateDiscount());
        System.out.println("Toy Car Discount:"+car.getDiscountStrategy().calculateDiscount());

    }
}  

策略模式的使用情况:

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

  2. 一个系统需要动态地在集中算法中选择一种。那么这些算法可以包装到一个个的具体算法里面,而这些具体算法都都是一个抽象算法类的子类。

  3. 一个系统的算法使用的数据不可以上客户端知道,策略模式可以避免让客户端涉及到不必要接触的复杂的和只与算法有关的数据。

  4. 如果一个对象有很多的行为,如果不使用恰当的模式,这些行为只好使用多重的条件选择语句来实现。此时,使用侧路模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象的设计概念。

策略模式的优点:

  1. 提供了管理相关算法足的办法。

  2. 避免了难以维护的多重条件选择语句的使用。

  3. 提供了可以代替继承关系的办法。

策略模式的缺点:

  1. 客户端必须提前知道所有的策略类,并且自己决定使用那一个策略类。

  2. 策略模式造成很多的策略类。

另外在java语言内部也有好多策略模式的例子:如comparator,AWT中的LayoutManager,Swing中的Border等。有兴趣的可以研究研究。

工程实例下载:http://cid-2c8a0dc7c1eb1d71.skydrive.live.com/self.aspx/soft/Design%20Pattern/Strategy.7z