本文介绍使用AspectJ实现设计模式之装饰模式,文章利用一个打印发票的例子说明如何使用AspectJ来实现装饰模式。 
  
示例说明 
       装饰模式是大家都很熟悉的一个模式,最典型的例子就是Java的I/O库,它的设计完全按照装饰模式。我们通常在客户端使用new操作符来对一个类进行包装,例如下述代码 
DataOutputStream out=new DataOutputStream(new FileOutputStream(“1.txt”)); 
它使用DataOutputStream来包装FileOutputStream,所以可以使用前者的方法来对文件流进行写入。而采用AspectJ实现的装饰模式中,在客户端我们将不在看见这样的new操作,而是直接将装饰行为织入方法执行内部,在方法内部增加额外的操作。现将<<Java与模式>>一书中有打印发票的例子改用AspectJ实现,我们看看到底AspectJ是如何将额外操作织入方法体中的。系统UML图如下所式 
  
  
源代码 
抽象方面OrderDecorator.java 
abstract aspect OrderDecorator { 
  declare parents : SalesOrder extends Order; 
  public void SalesOrder.print(){ 
    super.print(); 
  } 
  protected pointcut print(Order order) : target(order) && call(public void print()); 
} 
抽象方面利用类型间声明为SalesOrder类声明了父类,并实现了方法print()。同时还定义了一个切点print(Order order),它将捕捉Order类print方法调用。 
  
具体方面HeaderDecorator.java 
public aspect HeaderDecorator extends OrderDecorator{ 
  void around(Order order) : print(order){ 
    printHeader(order); 
    proceed(order); 
  } 
  private void printHeader(Order order){ 
    System.out.println("\t***\tINVOICE\t***"); 
    System.out.println("XYZ Incorporated\nDate of Sale:"); 
    System.out.println(order.getSalesDate()); 
    System.out.println("======================================"); 
    System.out.println("Item\t\tUnits\tUnit Price\tSubtotal"); 
  } 
} 
HeaderDecorator方面实现发票头打印功能,它定义了通知around截获切点print所捕捉的方法调用,然后先打印发票头再利用proceed()方法执行所捕捉到的方法的原有逻辑。 
  
具体方面FooterDecorator.java 
public aspect FooterDecorator extends OrderDecorator{ 
  declare precedence  : FooterDecorator,HeaderDecorator; //声明优先顺序 
  void around(Order order) : print(order){ 
    proceed(order); 
    printFooter(order); 
  } 
  private void printFooter(Order order){ 
    System.out.println("====================================="); 
    System.out.println("Total\t\t\t\t"+order.formatCurrency(order.getGrandTotal())); 
  } 
} 
FooterDecorator方面实现发票注脚打印功能,它定义了通知around截获切点print所捕捉的方法调用,然后先利用proceed()方法执行所捕捉到的方法的原有逻辑再打印注脚信息。还有最重要的一点是它的precedence声明,这个声明决定了FooterDecorator方面和HeaderDecorator方面捕捉同一个方法调用时同一通知的执行顺序。方面中的声明说明HeaderDecorator比FooterDecorator的优先级别高,其通知较先执行。 
  
抽象定单类Order.java 
import java.util.*; 
import java.text.NumberFormat; 
abstract public class Order { 
  private OrderLine InkOrderLine; 
  protected Collection items=new ArrayList(); 
  protected String customerName; 
  protected Date salesDate; 
  
  public void print(){ 
    Iterator it=items.iterator(); 
    while(it.hasNext()){ 
      OrderLine item=(OrderLine)it.next(); 
      item.printLine(); 
    } 
  } 
  public String getCustomerName(){ 
    return customerName; 
  } 
  public void setCustomerName(String customerName){ 
    this.customerName=customerName; 
  } 
  public Date getSalesDate(){ 
    return salesDate; 
  } 
  public void setSalesDate(Date salesDate){ 
    this.salesDate=salesDate; 
  } 
  public void addItem(OrderLine item){ 
    items.add(item); 
  } 
  public void removeItem(OrderLine item){ 
    items.remove(item); 
  } 
  public double getGrandTotal(){ 
    double amount=0.0D; 
    Iterator it=items.iterator(); 
    while(it.hasNext()){ 
      OrderLine item=(OrderLine)it.next(); 
      amount+=item.getSubTotal(); 
    } 
    return amount; 
  } 
  public String formatCurrency(double amount){ 
    return NumberFormat.getCurrencyInstance().format(amount); 
  } 
} 
  
定单条目类OrderLine.java 
import java.text.NumberFormat; 
public class OrderLine { 
  private String itemName; 
  private int units; 
  private double unitPrice; 
  
  public String getItemName(){ 
    return itemName; 
  } 
  public void setItemName(String itemName){ 
    this.itemName=itemName; 
  } 
  public int getUnits(){ 
    return units; 
  } 
  public void setUnits(int units){ 
    this.units=units; 
  } 
  public double getUnitPrice(){ 
    return unitPrice; 
  } 
  public void setUnitPrice(double unitPrice){ 
    this.unitPrice=unitPrice; 
  } 
  public void printLine(){ 
    System.out.println(itemName+"\t"+units+"\t" 
                       +formatCurrency(unitPrice)+"\t" 
                       +formatCurrency(getSubTotal())); 
  } 
  public double getSubTotal(){ 
    return units*unitPrice; 
  } 
  private String formatCurrency(double amount){ 
    return NumberFormat.getCurrencyInstance().format(amount); 
  } 
} 
具体销售定单类SalesOrder.java 
public class SalesOrder {}  
  
客户端演示Demo.java 
public class Demo { 
  private static Order order; 
  public static void main(String [] args){ 
    order=new SalesOrder(); 
    order.setSalesDate(new java.util.Date()); 
    order.setCustomerName("starchu1981"); 
    OrderLine item1=new OrderLine(); 
    item1.setItemName("Fire Wheel Tire"); 
    item1.setUnitPrice(154.23); 
    item1.setUnits(4); 
    order.addItem(item1); 
    OrderLine item2=new OrderLine(); 
    item2.setItemName("Front Fender"); 
    item2.setUnitPrice(300.45); 
    item2.setUnits(1); 
    order.addItem(item2); 
    order.print();  //这里与Java版本的客户端代码不同,稍后解释。 
  } 
} 
  
结果分析 
Demo输出的结果如下 
  
       ***       INVOICE       *** 
XYZ Incorporated 
Date of Sale: 
Thu Jul 24 11:45:10 CST 2003 
================================= 
Item        Units       Unit Price       Subtotal 
Fire Wheel Tire       4       ¥154.23       ¥616.92 
Front Fender   1       ¥300.45       ¥300.45 
================================= 
Total                            ¥917.37 
  
例子输出与我们期望的结果是一致的。特别值得注意的是代码的最后一行,通常在Java中可能会有下面的代码 
Order order=new HeaderDecorator(new FooterDecorator()); 
       order.print(); 
而使用AspectJ构造的装饰模式在客户端完全将装饰现象消除,可以直接使用方法,而不需要向在Java中那样提供具体装饰类的构造行为,这使得客户端代码更加透明,客户可以完全不知道装饰类的存在。当然这样做也有一个缺点,就是客户端无法自己控制所有的装饰行为,例如如果客户无法撤消打印注脚行的行为。 
  
  
  
本文由starchu1981保留版权,如果需要转贴请写明作者和出处。 
   
 
  |