首页 > 代码库 > 装饰者模式

装饰者模式

    解决的问题是:我有一个类A,里面可能需要很多不同行为的功能。

    现在有三种解决方法,第一种:每个功能都弄成一个类,然后继承类A,这种的问题是,如果功能很多,会造成类爆炸。

    第二种:我将功能类组合上类A上,这种想法是好的,但还没够弹性,因为这样我没法在不修改类A的情况下对功能的动态添删。(我的目的就是在不修改类A的情况下,对类A进行功能扩展)

    第三种:就是我要说的装饰者模式。

    举个例子,我有一个饮品店,我有很多饮料,也有很多调料,我通过对饮料搭配不同种类和不同数量的调料,来组合成我的饮品。对吧,因为有很多种组合,所以这里需要动态组合。

    所以我需要一个被装饰者类(饮料和调料的总类,即需要被装饰的内核类):

public abstract class Beverage {
	String description = "Unknown Beverage";
  
	public String getDescription() {
		return description;
	}
 
	public abstract double cost();
}

    我也需要一个调料的总类(继承被装饰者类,因为我需要调料和饮料的类型相同,等下后面会说):

public abstract class CondimentDecorator extends Beverage {
	public abstract String getDescription();
}

    我有很多饮料类:

public class Espresso extends Beverage {
  
	public Espresso() {
		description = "Espresso";
	}
  
	public double cost() {
		return 1.99;
	}
}

public class HouseBlend extends Beverage {
	public HouseBlend() {
		description = "House Blend Coffee";
	}
 
	public double cost() {
		return .89;
	}
}

    我也有调料类:

public class Mocha extends CondimentDecorator {
	Beverage beverage;
 
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}
 
	public String getDescription() {
		return beverage.getDescription() + ", Mocha";
	}
 
	public double cost() {
		return .20 + beverage.cost();
	}
}

public class Whip 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();
	}
}

public class Soy 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();
	}
}

    最后我来组合几杯不同的饮品:

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 Mocha(beverage2);
		beverage2 = new Mocha(beverage2);/*这里就解析了为什么我需要饮料和调料同一类型了。如果不同的类型,我假设饮料为A类(被装饰者),调料为B类,我第一次装饰时,我实例化后,那个被装饰者就变成B类型的,我在此基础上再装饰另一个调料,因为我需要传一个A类型的进去,但我的已经变成B类型了,所以就不能再装饰第二次了。同类型的就解决这个问题了*/
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.getDescription() 
				+ " $" + beverage2.cost());
 
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() 
				+ " $" + beverage3.cost());
	}
}

    看到现在,你应该对装饰者模式有一定了解了,我在弄一种情况来加深你的理解。

    还记得java中的i/o流不,没错,java.io类就是用装饰者模式的。记得大一时初次学java,看i/o流这部分最烦了,也不懂它的原理,它的类也很多。现在知道它的运作了:

以InputStream为例,它的“饮料”有FileInputStream,StringBufferInputStream,ByteArrayInputStream,

                                  它的“调料”总类是:FilterInputStream,

                                                    装饰者是:BufferedInputStream,DataInputStream,LineNumberInputStream等。

    我来自己写一个i/o的装饰者类来加深理解吧:

import java.io.*;

public class LowerCaseInputStream extends FilterInputStream {

	public LowerCaseInputStream(InputStream in) {
		super(in);
	}
 
	public int read() throws IOException {
		int c = super.read();
		return (c == -1 ? c : Character.toLowerCase((char)c));
	}
		
	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")));

			while((c = in.read()) >= 0) {
				System.out.print((char)c);
			}

			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

对吧,是不是很灵活,自己需要什么条件,写个装饰者类就可以动态改变了。

更多设计模式讲解,关注我吧,我会不时地更新,还有我还会发一些其他如java方法、框架、Linux等一些的心得,我会努力成为大神,并帮助大家一起成为大神的。大笑