1.装饰模式
装饰模式(Decorator)又名包装(wraper)模式。装饰模式以对客户端透明方式扩展对象的功能,是继承关系的一个替代方案。
2.模式结构
装饰模式中的角色:
抽象构件(Component):一个抽象对象,定义被装饰构件的行为;
具体构件(ConcreteComponent):被装饰的类,需要添加功能的类;
抽象装饰角色(Decorator):持有一个Component实例(被装饰的对象),并且实现Component的行为,保持装饰与具体构件接口一致。
具体装饰角色(ConcreteDecorator):具体给构件添加功能的类。
3.装饰模式适用场景
(1)需要扩展一个类的功能,或者给一个类增加附加责任;
(2)需要动态的给一个对象增加功能,这些功能可以再动态地撤销;
(3)需要增加由一些基本功能的排列组合而产生的非常大量的功能,这是使用继承关系不太现实。
4.发票系统例子
需求:
一个电子销售系统需要打印客户所购买的商品发票.一章发票分为3部分
(1)发票头部,包括客户的名字和销售日期
(2)发票主体,购买的货物清单
(3)发票尾部,总金额
头部和尾部有不同的格式,因此需要灵活的为发票添加头部和尾部。可以使用装饰模式。
类图:
SalesOrder 是发票主体,持有一定数量的OrderLine;
OrderLine是发票清单中的一行,包括品名、单价、数量等
OrderDecorator抽象的发票装饰类;
HeaderDecorator 和FooterDecorator:具体装饰类,分别是发票头部和发票尾部,如果有其他格式的头部或者尾部,可以继承OrderDecorator实现。
源码:
Order.java
package com.patterns.decorder;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
abstract public class Order
{
protected String customerName;
protected Date salesDate;
protected List<OrderLine> items = new ArrayList<OrderLine>();
public void print()
{
for (int i = 0 ; i < items.size() ; i++)
{
OrderLine item = (OrderLine) items.get(i);
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 remoteItem(OrderLine item)
{
items.remove(item);
}
public double getGrandTotal()
{
double amnt = 0.0D;
for (int i = 0 ; i < items.size() ; i++)
{
OrderLine item = (OrderLine) items.get(i);
amnt += item.getSubtotal();
}
return amnt;
}
protected String formatCurrency(double amnt)
{
return NumberFormat.getCurrencyInstance().format(amnt);
}
}
OrderLine.java
package com.patterns.decorder;
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 unitPrice * units;
}
private String formatCurrency(double amnt)
{
return NumberFormat.getCurrencyInstance().format(amnt);
}
}
OrderDecorator.java
package com.patterns.decorder;
abstract public class OrderDecorator extends Order
{
/**
* @link aggregation
* @directed
* @supplierCardinality 1
* @clientCardinality 0..1
*/
protected Order order;
public OrderDecorator(Order order)
{
this.order = order;
this.setSalesDate( order.getSalesDate() );
this.setCustomerName( order.getCustomerName() );
}
public void print()
{
super.print();
}
}
FooterDecorator.java
package com.patterns.decorder;
public class FooterDecorator extends OrderDecorator
{
public FooterDecorator(Order anOrder)
{
super(anOrder);
}
public void print()
{
super.order.print();
printFooter();
}
private void printFooter()
{
System.out.println("========================================================");
System.out.println("Total\t\t\t\t" +
formatCurrency(super.order.getGrandTotal()));
}
}
HeaderDecorator.java
public class HeaderDecorator extends OrderDecorator
{
public HeaderDecorator(Order anOrder)
{
super(anOrder);
}
public void print()
{
this.printHeader();
super.order.print();
}
private void printHeader()
{
System.out.println("\t***\tI N V O I C E\t***\nXYZ Incorporated\nDate of Sale: "
+ order.getSalesDate());
System.out.println("========================================================");
System.out.println("Item\t\tUnits\tUnit Price\tSubtotal");
}
}
Client.java(调用代码)
package com.patterns.decorder;
import java.util.Date;
public class Client {
/**
* @directed
*/
private static Order order;
public static void main(String[] args)
{
order = new SalesOrder();
order.setSalesDate(new Date());
order.setCustomerName("XYZ Repair Shop");
OrderLine line1 = new OrderLine();
line1.setItemName("FireWheel Tire");
line1.setUnitPrice(154.23);
line1.setUnits(4);
order.addItem(line1);
OrderLine line2 = new OrderLine();
line2.setItemName("Front Fender");
line2.setUnitPrice(300.45);
line2.setUnits(1);
order.addItem(line2);
order = new HeaderDecorator(new FooterDecorator(order));
order.print();
}
}
5.装饰模式的优缺点
优点:
(1)装饰模式与继承都可以扩展对象的功能,但是装饰模式更加灵活。而且装饰模式可以动态的添加与去除功能,而继承则是系统运行前定义好的。
(2)不同装饰类对象的排列组合,更加多样化的功能扩展。
缺点:
会使系统产生比使用继承更多的对象。