java设计模式-------装饰者模式
装饰者模式
Decorator模式(别名Wrapper):动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。主要有组件(components)和装饰器(Decorator)组成。要求components和Decorator实现相同的接口或者抽象类(具体类的局限性太大)。
设计原则、模式特点、适用性
- 1. 多用组合,少用继承。
利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。
- 2. 类应设计的对扩展开放,对修改关闭。
是用Decorator去装饰Component、因为实现了同一接口或者抽象类、Decorator同样可以装饰Decorator
> 装饰模式的特点
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。
> 装饰者模式的适用性
> 装饰者模式的适用性
1. 需要扩展一个类的功能,或给一个类添加附加职责。
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
>
>
装饰者模式实例
1、 需求
参照Head First Design Patterns。
-------------- 对与星巴克咖啡我们都不会陌生、当然这里不会介绍他的编年史什么什么的、只是对其出售的咖啡的一种销售策略的价格做一下统计、这样当顾客点了他们想要的搭配的时候我们就可以知道最后的价格是多少。
1、 假设有四种咖啡:HouseBlend、DarkRoaat、Decaf、Espresso。
2、同样有四种可以搭配的选择:Milk、Mocha、Soy、Whip。
-------------这四种咖啡有不同的价格、同样四种搭配也有不同的价格。当我们把这个单子拿给顾客的时候、顾客可以任意点一种咖啡、可以任意搭配。我们的目的是要统计每种搭配的价格、那我们要写多少个类?最后的结果就是类爆炸!
-------------当我们要添加一个新的品种的时候、如果按照上面的方法、那我们就要新添加N多类来实现目的。如何有效的解决这两个问题?也许你有别的很多方法与思想、这里只是说明装饰者模式的使用。
类爆炸
采用装饰者模式
看装饰者模式类图
现在就是要将需求符合上面类图
再看,用beverage代替上面的component,成为所有类的超类
1、装饰者模式要求装饰器和组件是同一类型、那么我们先给所有咖啡一个抽象类———Beverage代码:
public abstract class Beverage {
String description = "no description";
public String getDescription (){
return description;
}
public abstract double cost();
}
2、创建四种咖啡模型、也就是组件、并实现Beverage抽类,HouseBlend、DarkRoaat、Decaf、Espresso 四种模型如下
//HouseHand
public class HouseHand extends Beverage {
public HouseHand() {
super();
description = "HouseHand";
}
/**
* the price of HouseHand is 8.9$ .
*/
@Override
public double cost() {
return 8.9;
}
}
//DarkRoast
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
public double cost() {
return .99;
}
}
//Decaf
public class Decaf extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
public double cost() {
return 1.05;
}
}
//Espresso
public class Espresso extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
public double cost() {
return 1.99;
}
}
3、实现具体装饰类:定义一个所有装饰器的抽象类、继承与Beverage、这样以后如果有新添加的调料我们就可以扩展一个调料装饰器来与当前的几种咖啡组合、而不用动原来的任何代码!保持了对扩展开放原则
public abstract class CondimentDecorator extends Beverage {
/**
* 提供一个必须要实现的获取当前组合的描述信息的方法。
* 方便观察结果。
*/
public abstract String getDescription();
}
4、定义具体的装饰类:MochaDecorator、MilkDecorator、WhipDecorator、SoyDecorator、
// MochaDecorator
public class MochaDecorator extends CondimentDecorator {
Beverage beverage;
public MochaDecorator(Beverage beverage) {
super();
this.beverage = beverage;
}
/**
* 返回被包装的组合或者组件的描述加上当前包装信息。
*/
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return beverage.cost() + 10.3;
}
}
其他的MilkDecorator、WhipDecorator、SoyDecorator采用类似实现方法
//WhipDecorator
public class WhipDecorator extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public double cost() {
return .10 + beverage.cost();
}
}
//SoyDecorator
public class SoyDecorator extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
public double cost() {
return .15 + beverage.cost();
}
}
5、测试代码如下:用配料对象装饰每一个饮料对象
package DecoratorModel;
public class StarbuzzCoffee {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new MochaDecorator(beverage2);
beverage2 = new MochaDecorator(beverage2);
beverage2 = new WhipDecorator(beverage2);
System.out
.println(beverage2.getDescription() + " $" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new SoyDecorator(beverage2);
beverage3 = new MochaDecorator(beverage2);
beverage3 = new WhipDecorator(beverage2);
System.out
.println(beverage3.getDescription() + " $" + beverage3.cost());
}
}
java IO中的装饰者模式
可以看出上面的Io图中的类和实例中设计非常相近。
采用上面的方法来写一个自己的文件输入装饰者,就好比上面例子中配料 ,如:四种可以搭配的选择:Milk、Mocha、Soy、Whip。
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class LowerCaseInputStream extends FilterInputStream
{
protected LowerCaseInputStream(InputStream in)
{
super(in);
}
@Override
public int read() throws IOException
{
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char) c));
}
@Override
public int read(byte[] b, int offset, int len) throws IOException
{
int result = super.read(b, offset, len);
for (int i = offset; i < offset + result; i++)
{
b[i] = (byte) Character.toLowerCase((char) b[i]);
}
return result;
}
}
测试写所写的装饰者
import java.io.*;
public class InputTest
{
public static void main(String[] args) throws IOException
{
int c;
try
{
InputStream in = new LowerCaseInputStream(new BufferedInputStream(
new FileInputStream("test.txt")));//这里的test.txt为文件路径,这里还有一个细节,注意,首先是用BufferedInputStream装饰了FileInputStream,然后再用LowerCaseInputStream装饰
while ((c = in.read()) >= 0)
{
System.out.print((char) c);
}
in.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
装饰者模式缺点
1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。