技术开发 频道

用AspectJ实现外观模式



【IT168 技术文档】本文介绍使用AspectJ实现设计模式之装饰模式,文章利用一个打印发票的例子说明如何使用AspectJ来实现装饰模式。

示例说明  

 

       装饰模式是大家都很熟悉的一个模式,最典型的例子就是JavaI/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),它将捕捉Orderprint方法调用。

 

具体方面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方面捕捉同一个方法调用时同一通知的执行顺序。方面中的声明说明HeaderDecoratorFooterDecorator的优先级别高,其通知较先执行。

 

 

抽象定单类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中那样提供具体装饰类的构造行为,这使得客户端代码更加透明,客户可以完全不知道装饰类的存在。当然这样做也有一个缺点,就是客户端无法自己控制所有的装饰行为,例如如果客户无法撤消打印注脚行的行为。

0
相关文章