书店推出打折消息:
[1] 对于“新书”,没有折扣;
[2] 对于“计算机”类图书,固定折扣为 10 元;
[3] 对于“经管”类图书,折扣的书价的 10%
[4] 购买 5 本以上的图书,固定折扣为 20 元;
[5] 在所有的折扣计算后,总的折扣价不得超过 50 元。

 

1. 使用 Strategy 模式

对于打折消息 [1],[2],[3] 针对三种类型的图书,可以使用 Strategy 模式。

NoDiscountStrategy 代表购买“新书”的打折策略 - 没有折扣;
FlatRateStrategy 代表购买“计算机”类图书的打折策略 - 固定折扣价;
PercentageStrategy 代表购买“经管”类图书的打折策略 - 百分比折扣价。
<<abstract>>DiscountStrategy.java

package com.zj.books.strategy;

 

public abstract class DiscountStrategy {

    protected double _price = 0.0;

 

    public DiscountStrategy( double price) {

       _price = price;

    }

 

    abstract public double calculateDiscount();

……

}
_price 代表图书的价格;抽象方法 calculateDiscount() 表示具体的打折计算逻辑,延迟到子类中实现。下面的三个子类的部分逻辑。

 

NoDiscountStrategy.java

package com.zj.books.strategy;

 

public class NoDiscountStrategy extends DiscountStrategy {

 

    public NoDiscountStrategy( double price) {

       super (price);

    }

 

    public double calculateDiscount() {

       return 0.0;

    }

}

 

FlatRateStrategy.java

package com.zj.books.strategy;

 

public class FlatRateStrategy extends DiscountStrategy {

    private double _discount = 0.0;

 

    public FlatRateStrategy( double price, double discount) {

       super (price);

       _discount = discount;

    }

   

    public double calculateDiscount() {

       return _discount ;

    }

……

}

 

PercentageStrategy.java

package com.zj.books.strategy;

 

public class PercentageStrategy extends DiscountStrategy {

    private double _percent = 1.0;

 

    public PercentageStrategy( double price, double percent) {

       super (price);

       if (percent > 1.0)

           percent = 1.0;

       _percent = percent;

    }

 

    public double calculateDiscount() {

       return _price * _percent ;

    }

……

}

使用一个抽象基类 Book 持有一个策略引用,这个策略是抽象基类的引用。这个类中提供一个重要的方法 getDiscount() 通过分配的具体策略的 _strategy .calculateDiscount() 方法来得到折扣。而如何实现 Book 具体子类与 Strategy 具体子类的配对,将使用工厂方法模式。

<<abstract>> Book.java

package com.zj.books;

 

import com.zj.books.strategy.DiscountStrategy;

 

public abstract class Book {

    protected String _name ;

    protected int _typeCode ;

    protected double _price ;

    protected DiscountStrategy _strategy ;

 

    public Book(String name, int bookType, double price) {

       _name = name;

       _typeCode = bookType;

       _price = price;

    }

 

    public double getDiscount() {

       return _strategy .calculateDiscount();

    }

   

……

}

2. 使用 Factory Method 模式

对于策略的分配,使用 Factory Method 模式。这样对于书的种类和打折策略都是可以扩展的。

三个具体的 Publish 类分别针对三种类型的书和三种打折策略,给出具体的对象。
<<interface>> Publisher.java

package com.zj.purchase;

 

import com.zj.books.Book;

 

public interface Publisher {

    Book bookFactory(String name, double price);

}

三个具体的子类实现 bookFactory 方法,分别生成配对的具体 Book 类和具体 Strategy 类。
ComputerBookPublish.java

package com.zj.purchase;

 

import com.zj.books.Book;

import com.zj.books.ComputerBook;

import com.zj.books.strategy.FlatRateStrategy;

 

public class ComputerBookPublish implements Publisher{

    private double _discount =0.0;

   

    public ComputerBookPublish( double discount){

       _discount =discount;

    }

 

    public Book bookFactory(String name, double price) {

       Book book= new ComputerBook(name,price);

       book.setStrategy( new FlatRateStrategy(price, _discount ));

       return book;

    }

……

}

 

ManagementBookPublish.java

package com.zj.purchase;

 

import com.zj.books.Book;

import com.zj.books.ManagementBook;

import com.zj.books.strategy.PercentageStrategy;

 

public class ManagementBookPublish implements Publisher{

    private double _percent =1.0;

   

    public ManagementBookPublish( double percent){

       _percent =percent;

    }

 

    public Book bookFactory(String name, double price) {

       Book book= new ManagementBook(name,price);

       book.setStrategy( new PercentageStrategy(price, _percent ));

       return book;

    }

……

}

 

NewReleaseBookPublisher.java

package com.zj.purchase;

 

import com.zj.books.Book;

import com.zj.books.NewReleaseBook;

import com.zj.books.strategy.NoDiscountStrategy;

 

public class NewReleaseBookPublisher implements Publisher{

 

    public Book bookFactory(String name, double price) {

       Book book= new NewReleaseBook(name,price);

       book.setStrategy( new NoDiscountStrategy(price));

       return book;

    }

}

3. 使用 Decorate 模式

对于 [4] [5] 可使用 Decorate 模式实现。

Order 类是一个接口,定义了所有客户端可以使用的行为。其中 buy() 方法表示购买书,其中的参数依次表示册数,书名,单价,和图书类型; originalPay() 方法表示原始货款; actualPay() 表示实际货款; discount() 表示折扣; addPolicy() 方法将被应用于 Decorate 模式。
<<interface>>Order.java

package com.zj.order;

 

public interface Order {

    void buy( int copies,String name, int price, int type);

   

    double originalPay();

 

    double actualPay();

   

    void setActualPay( double pay);

 

    double discount();

   

    void setDiscount( double discount);

   

    int getCopies();

   

    void printPayList();

   

    void addPolicy();

}

PayOrder 类是一个基于打折消息 [1],[2],[3] 的应用,在 buy() 方法中,根据具体的图书类型,产生一个具体的 publisher 类,继而可以获得相应的图书实例及折扣策略实例。
PayOrder.java

package com.zj.order;

 

import java.util.HashMap;

import java.util.Map;

 

import com.zj.books.Book;

import com.zj.books.BookType;

import com.zj.purchase.ComputerBookPublish;

import com.zj.purchase.ManagementBookPublish;

import com.zj.purchase.NewReleaseBookPublisher;

 

public class PayOrder implements Order {

    private Map<String, Integer> payList = new HashMap<String, Integer>();

    private double _pay = 0.0;

    private double _discount = 0.0;

    private int _copies = 0;

 

    private double _discountPolicy = 0.0;

    private double _percentagePolicy = 1.0;

 

    public PayOrder( double discountPolicy, double percentagePolicy) {

       _discountPolicy = discountPolicy;

       _percentagePolicy = percentagePolicy;

    }

 

    public void buy( int copies, String name, int price, int type) {

       Book book = null ;

       switch (type) {

       case BookType. NEW_RELEASE :

           book = new NewReleaseBookPublisher().bookFactory(name, price);

           break ;

       case BookType. COMPUTER :

           book = new ComputerBookPublish( _discountPolicy ).bookFactory(name,

                  price);

           break ;

       case BookType. MANAGEMENT :

           book = new ManagementBookPublish( _percentagePolicy ).bookFactory(

                  name, price);

           break ;

       default :

           throw new RuntimeException( "Type not found." );

       }

 

       _copies += copies;

       payList .put(book.getName(), copies);

       _pay += copies * book.getPrice();

       _discount += copies * book.getDiscount();

    }

 

    public double originalPay() {

       return _pay ;

    }

 

    public double actualPay() {

       return _pay - _discount ;

    }

 

    public void setActualPay( double pay) {

       _pay = pay;

    }

 

    public double discount() {

       return _discount ;

    }

 

    public void setDiscount( double discount) {

       _discount = discount;

    }

 

    public int getCopies() {

       return _copies ;

    }

 

    public void printPayList() {

       System.out.println(toString());

    }

 

    public void addPolicy() {

    }

 

    public String toString() {

       return payList .toString();

    }

}

OrderDecorator 是一个装饰角色,它持有一个 Order 的引用。
OrderDecorator.java

package com.zj.order.decorator;

 

import com.zj.order.Order;

 

public class OrderDecorator implements Order {

    protected Order _order ;

 

    public OrderDecorator(Order order) {

       _order = order;

    }

 

    public double actualPay() {

       return _order .actualPay();

    }

 

    public void setActualPay( double pay) {

       _order .setActualPay(pay);

    }

 

    public void buy( int copies, String name, int price, int type) {

       _order .buy(copies, name, price, type);

    }

 

    public double discount() {

       return _order .discount();

    }

 

    public void setDiscount( double discount) {

       _order .setDiscount(discount);

    }

 

    public double originalPay() {

       return _order .originalPay();

    }

 

    public int getCopies() {

       return _order .getCopies();

    }

 

    public void printPayList(){

       _order .printPayList();

    }

   

    public void addPolicy(){

       _order .addPolicy();

    }

}

根据打折消息 [4] :“购买 5 本以上的图书,固定折扣为 20 元”,得到具体装饰角色 CopyDecorator 。它将重写 addPolicy() 方法。
CopyDecorator.java

package com.zj.order.decorator;

 

import com.zj.order.Order;

 

public class CopyDecorator extends OrderDecorator {

 

    public CopyDecorator(Order order) {

       super (order);

    }

 

    public void addPolicy() {

       if (getCopies() > 5)

           setDiscount(discount() + 20);

       super . _order .addPolicy();

    }

}

根据打折消息 [5] :“在所有的折扣计算后,总的折扣价不得超过 50 元”,得到具体装饰角色 PayDecorator 。它将重写 addPolicy() 方法。
必须注意两个装饰类产生的先后顺序。
PayDecorator.java

package com.zj.order.decorator;

 

import com.zj.order.Order;

 

public class PayDecorator extends OrderDecorator {

 

    public PayDecorator(Order order) {

       super (order);

    }

 

    public void addPolicy() {

       if (discount() > 50)

           setDiscount(50);

       super . _order .addPolicy();

    }

}

4. 客户端实现

Client 中先演示了没有装饰类,即只实现打折消息 [1],[2],[3] 的情况,此时原价 300 元的货款折扣为 36 元;而后加上了两个装饰类后,由于购买六本书,另加 29 元折扣后总折扣变为 56 ,超过 50 元的折扣上限,所以最终折扣为 50 元。
Client.java

package com.zj.client;

 

import com.zj.books.BookType;

import com.zj.order.Order;

import com.zj.order.PayOrder;

import com.zj.order.decorator.CopyDecorator;

import com.zj.order.decorator.PayDecorator;

 

public class Client {

    public static void main(String[] args) {

       Order order = new PayOrder(10, 0.1);

       order.buy(1, "Java" , 40, BookType. COMPUTER );

       order.buy(1, "C++" , 60, BookType. COMPUTER );

       order.buy(1, "Design Pattern" , 100, BookType. COMPUTER );

       order.buy(1, "Manager" , 60, BookType. MANAGEMENT );

       order.buy(1, "Apo" , 20, BookType. NEW_RELEASE );

       order.buy(1, "droAq" , 20, BookType. NEW_RELEASE );

      

       order.printPayList();

       System. out .println( "===========" );

      

       System. out .println( "original\t" +order.originalPay());

       System. out .println( "discount\t" +order.discount());

       System. out .println( "actual\t\t" +order.actualPay());

       System. out .println( "===========" );

 

       order= new CopyDecorator( new PayDecorator(order));

       order.addPolicy();

       System. out .println( "original\t" +order.originalPay());

       System. out .println( "discount\t" +order.discount());

       System. out .println( "actual\t\t" +order.actualPay());

    }

}

结果
{Apo=1, Manager=1, droAq=1, C++=1, Design Pattern=1, Java=1}
===========

original   300.0

discount   36.0

actual     264.0

===========

original   300.0

discount   50.0

actual     250.0