如有转载,请申明:
转载至 http://blog.youkuaiyun.com/qq_35064774/article/details/52122073
1 什么是装饰模式
装饰模式又名包装模式。装饰模式对客户端透明方式扩展对象的功能,是继承关系的一个替代方案。
通俗来说,就是包装一个对象,让其拥有更强的功能。
2 如何实现装饰模式
同样我们举个简单的例子。
* 现需要一个发票打印程序
* 一张发票可以分成三个部分:头部、主体、尾部
* 要求打印的发票格式如下:
*
* *** XXX ***
* Data of Sale: 2016.07.25
* =================================
* Name Price
* Goods1 ¥100.00
* Goods2 ¥10.00
* =================================
* Total ¥110.00
* Looking Forward to Your Next Visit
*
* 要求头部尾部均可方便的切换组合
看到这个可能会有同学想到代理模式,虽然可以实现功能,但代理模式的目的是控制而不是增强功能,用在这里不太合适。
首先我们把发票的主体部分打印功能实现了。
package com.ittianyu.decorator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Date;
public class Order {
private Date date;
private String name;
private List<OrderItem> list = new ArrayList<OrderItem>();
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<OrderItem> getList() {
return list;
}
public void setList(List<OrderItem> list) {
this.list = list;
}
public Order addItem(OrderItem orderItem) {
list.add(orderItem);
return this;
}
public void print() {
System.out.println("Name\t\t\tPrice");
Iterator<OrderItem> it = list.iterator();
while (it.hasNext()) {
OrderItem item = it.next();
String string = String.format("%s\t\t\t¥%.2f",
item.getName(), item.getPrice());
System.out.println(string);
}
}
}
发票主体部分是各项物品明细。
所以这里用一个容器来存放。
这里用到了OrderItem,这个类其实就是存储了每一项物品的属性。
package com.ittianyu.decorator;
public class OrderItem {
private String name;
private float price;
public String getName() {
return name;
}
public float getPrice() {
return price;
}
public OrderItem(String name, float price) {
super();
this.name = name;
this.price = price;
}
}
有了这两个类后,我们已经可以实现打印发票主体了。
为了支持头部和尾部的信息,我们就需要装饰类,首先抽象一个装饰类。
package com.ittianyu.decorator;
public abstract class OrderDecorator extends Order {
protected Order order;
public OrderDecorator(Order order) {
super();
// 保存被装饰对象的引用
this.order = order;
// 复制数据
this.setDate(order.getDate());
this.setName(order.getName());
this.setList(order.getList());
}
}
这个抽象的装饰类很简单,只有一个Order引用和构造方法。构造方法中,我们对传入的对象的数据进行复制,目的是为了在包装过程中,不会丢失原始数据。
那你会问为什么还要保留引用。这个是为了调用被装饰类对象的方法。
接下来是头部和尾部的装饰类。
package com.ittianyu.decorator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class HeaderDecoratorOrder extends OrderDecorator{
public HeaderDecoratorOrder(Order order) {
super(order);
}
@Override
public void print() {
this.printHeader();
// 调用被装饰对象的print
order.print();
}
private void printHeader() {
System.out.println("***\t" + this.getName() + "\t***");
DateFormat format = new SimpleDateFormat("yyyy.MM.dd");
String dateString = format.format(this.getDate());
System.out.println("Data of Sale: " + dateString);
System.out.println("===================================");
}
}
package com.ittianyu.decorator;
import java.util.Iterator;
public class FooterDecoratorOrder extends OrderDecorator{
public FooterDecoratorOrder(Order order) {
super(order);
}
@Override
public void print() {
// 调用被装饰对象的print
order.print();
this.printFooter();
}
private void printFooter() {
System.out.println("===================================");
float totalPrice = 0.0f;
Iterator<OrderItem> it = this.getList().iterator();
while (it.hasNext()) {
OrderItem item = it.next();
totalPrice += item.getPrice();
}
System.out.println(String.format(
"Total\t\t\t¥%.2f", totalPrice));
System.out.println("Looking Forward to Your Next Visit");
}
}
这两个类都重写了print方法,分别在被装饰类对象的print方法前和后调用了自己的方法。
最后是测试类。
package com.ittianyu.decorator;
import java.util.Date;
public class Test {
public static void main(String[] args) {
Order order = new Order();
order.setDate(new Date());
order.setName("Tian Yu Food Shop");
order
.addItem(new OrderItem("Bread", 3.00f))
.addItem(new OrderItem("Banana", 15.00f));
//Order decoratedOrder = new HeaderDecoratorOrder(order);
Order decoratedOrder = new HeaderDecoratorOrder(
new FooterDecoratorOrder(order));
decoratedOrder.print();
}
}
3 在什么情况下使用装饰模式
引用《Java与模式》中的说明。
* 需要扩展一个类的功能,或给一个类增加附加责任。
* 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
* 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
4 装饰模式的优点和缺点
优点:
* 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
* 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
* 使用装饰模式会产生比使用继承关系更多的对象。