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