设计模式之装饰模式(Decorator Pattern)

  • 装饰模式定义

    • 官方定义:Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionally. 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
    • 自己的理解:1️⃣在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象上,从而实现动态拓展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。2️⃣通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,这也是装饰器模式设计的初衷。3️⃣符合开闭原则:类应该对拓展开放,对修改关闭。
  • 装饰模式通用类图
    设计模式之装饰模式(Decorator Pattern)
    装饰模式主要有四个角色:

    • Component是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。
    • ConcreteComponent是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。
    • Decorator也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。
    • ConcreteDecorator是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰。
  • 装饰模式通用代码
    抽象构件

public abstract class Component {

	public abstract void operate();
}

具体构件

public class ConcreateComponent extends Component{

	@Override
	public void operate() {
		System.out.println("do something...");
	}
}

抽象装饰者

public abstract class Decorator extends Component {

	private Component component = null;

	public Decorator(Component _component) {
		this.component = _component;
	}

	@Override
	public void operate() {
		this.component.operate();
	}
}

具体装饰类

public class ConcreteDecorator1 extends Decorator{

	public ConcreteDecorator1(Component _component) {
		super(_component);
	}
	
	private void method1() {
		System.out.println("method1 装饰");
	}
	
	public void operate() {
		this.method1();
		super.operate();
	}
}

public class ConcreteDecorator2 extends Decorator{

	public ConcreteDecorator2(Component _component) {
		super(_component);
	}
	
	private void method2() {
		System.out.println("method2 装饰");
	}
	
	public void operate() {
		this.method2();
		super.operate();
	}
}

场景类

public class Client {

	public static void main(String[] agrs) {
		
		Component component = new ConcreateComponent();
		
		// first 
		component = new ConcreteDecorator1(component);
		
		// second
		component = new ConcreteDecorator2(component);
		
		// run
		component.operate();
	}
}
  • 装饰模式实例类图
    装饰模式在Java中经常出现的地方就是JavaIO。提到JavaIO,脑海中就冒出了大量的类:InputStream、FileInputStream、BufferedInputStream……等。
    设计模式之装饰模式(Decorator Pattern)
  • 装饰模式实例代码
    完成一个将读取到的英文字符全切换成大写的转换流。
public class ToUpperCaseInputStream extends FilterInputStream {

	InputStream inputStream;

	protected ToUpperCaseInputStream(InputStream in) {
		super(in);
		this.inputStream = in;
	}

	/**
	 * 读取单个字节
	 * 
	 * @return
	 * @throws IOException
	 */
	@Override
	public int read() throws IOException {
		// 获取父类读取的结果
		int result = super.read();
		// 如果读取到字符a,就抛出异常
		if (result == 'a') {
			throw new ToUpperException();
		}
		// 如果等于-1,说明无内容
		// 否则,将字节转成char,再将char转换成大写的后返回
		// 返回值类型是int类型,这里返回一个字符会被自动转型
		return (result == -1 ? result : Character.toUpperCase(Character.toChars(result)[0]));
	}

	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		int result = super.read(b, off, len);
		for (int i = off; i < off + result; i++) {
			// 将字节转成大写字符后再转成字节
			b[i] = (byte) Character.toUpperCase((char) b[i]);
		}
		return result;
	}

	/***
	 * 这里是一个内部类,自定义异常
	 */
	class ToUpperException extends IOException {
		@Override
		public void printStackTrace() {
			System.out.println("不好意思我遇到异常了,向上转型失败啦");
		}
	}
}
public class Client {
	public static void main(String[] args) {
		int result = 0;
		InputStream inputStream = null;
		try {
			inputStream = new ToUpperCaseInputStream(new BufferedInputStream(new FileInputStream("file path")));

			while ((result = inputStream.read()) >= 0) {
				System.out.print((char) result);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 装饰模式总结
    • 装饰者模式可以非常的贴合六大原则之一的开闭原则。
    • 继承虽然也可以实现扩展,但是继承的扩展需要已经扩展需求,在编译期扩展,而装饰者模式可以实现在运行期扩展。
    • 使用装饰者模式,类的数量会增长很多,不能过度使用。

参考书籍:设计模式之禅
实例代码放在这里