设计模式之装饰模式

如有转载,请申明:

转载至 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 装饰模式的优点和缺点

 

优点:

 * 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

 * 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

 

缺点:

 * 使用装饰模式会产生比使用继承关系更多的对象。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值