什么是装饰者模式?
其实我们用Java I/O的时候已经用到过了,只是可能不知道这就是设计模式,在后面有一个设计自己的装饰者模式,再介绍吧。
HeadFirst这样定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
新的设计原则
对扩展开发,对修改关闭
写一写要点
1. 继承属于扩展形式之一,但不见得达到弹性设计的最佳方式。
2. 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
3. 组合和委托可用于在运行时动态加上新的行为。
4. 除了继承,装饰者模式也可以让我们扩展行为。
5. 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6. 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的超类,都经过接口或继承实现)
7. 装饰者可以在被装饰者的行为与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8. 你可以用无数个装饰者包装一个组件。
9. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
10. 装饰者会导致设计中出现许多的小对象,如果过度使用,会让程序变得很复杂。
装饰者模式例子:星巴兹咖啡
怎么开始?
写下星巴兹的代码:
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 饮料抽象类
- */
- public abstract class Beverage {
- String description = "Unknown Beverage"; //饮料描述
- public String getDescription() { //得到饮料描述
- return description;
- }
- public abstract double cost(); //抽象方法,用于计算饮料价格
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 调料抽象类:代表装饰者
- */
- public abstract class CondimentDecorator extends Beverage {
- @Override
- public double cost() {
- return 0;
- }
- /**
- * 所有的调料装饰者都必须重新实现getDescription()方法。
- */
- public abstract String getDescription();
- }
写饮料的代码:
这里有四种:Epresso、HouseBlend、DarkRoast、Decat
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 浓缩咖啡,一种饮料
- */
- public class Espresso extends Beverage {
- public Espresso() {
- description = "Espresso";
- }
- @Override
- public double cost() {
- return 1.99;
- }
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 综合饮料
- */
- public class HouseBlend extends Beverage {
- public HouseBlend(){
- description = "House Blend Coffee";
- }
- @Override
- public double cost() {
- return .89;
- }
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 深培咖啡
- */
- public class DarkRoast extends Beverage {
- public DarkRoast(){
- description = "DarkRoast";
- }
- @Override
- public double cost() {
- return .99;
- }
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 低咖啡因
- */
- public class Decat extends Beverage {
- public Decat(){
- description = "Decat";
- }
- @Override
- public double cost() {
- return 1.05;
- }
- }
写调料的代码
这里有三种调料:Mocha、Soy、Whip
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 具体装饰者:摩卡
- */
- public class Mocha extends CondimentDecorator {
- //定义一个实例变量记录饮料,也就是被装饰者
- Beverage beverage;
- /**
- * 用来把被装饰者记录到实例变量中去
- * @param beverage
- */
- public Mocha(Beverage beverage) {
- this.beverage = beverage;
- }
- /**
- * 这里利用委托的做法,得到一个叙述,然后在其后加上附加的叙述
- */
- @Override
- public String getDescription() {
- return beverage.getDescription() + ", Mocha";
- }
- /**
- * 要计算Mocha饮料的价钱。首先把调用委托给被装饰对象,以计算价钱,然后再加上Mocha的价钱,得到最后结果
- */
- public double cost() {
- return .20 + beverage.cost();
- }
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 豆浆,一种配料
- */
- public class Soy extends CondimentDecorator {
- Beverage beverage;
- public Soy(Beverage beverage) {
- this.beverage = beverage;
- }
- @Override
- public String getDescription() {
- return beverage.getDescription() + ",Soy";
- }
- public double cost() {
- return .15 + beverage.cost();
- }
- }
- package decoratorPattern;
- /**
- *
- * @author mmmm
- * 奶泡,一种配料
- */
- public class Whip extends CondimentDecorator {
- Beverage beverage;
- public Whip(Beverage beverage) {
- this.beverage = beverage;
- }
- @Override
- public String getDescription() {
- return beverage.getDescription() + ",Whip";
- }
- public double cost() {
- return .10 + beverage.cost();
- }
- }
现在可以供应咖啡了
- package decoratorPattern;
- /**
- * 测试类,供应咖啡
- * @author mmmm
- *
- */
- public class StarbuzzCoffee {
- public static void main(String[] args) {
- //订一杯Espresso,不需要调料,打印出它的描述与价钱
- Beverage beverage = new Espresso();
- System.out.println(beverage.getDescription() + " $" + beverage.cost());
- Beverage beverage2 = new DarkRoast();
- beverage2 = new Mocha(beverage2); //用Mocha装饰它
- beverage2 = new Mocha(beverage2); //用第二个Mocha装饰它
- beverage2 = new Whip(beverage2); //用Whip装饰它
- 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());
- }
- }
- Espresso $1.99
- DarkRoast, Mocha, Mocha,Whip $1.49
- House Blend Coffee,Soy, Mocha,Whip $1.34
刚开始的时候已经说过Java中使用装饰者模式的I/O
下面就扩展一下它的行为:将大写字符转换为小写字符
- package javaIo;
- import java.io.FilterInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- /*
- * 编写自己的Java I/O装饰者
- */
- public class LowerCaseInputStream extends FilterInputStream {
- protected 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;
- }
- }
- package javaIo;
- import java.io.BufferedInputStream;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- public class InputTest {
- public static void main(String[] args) throws IOException{
- int c;
- try {
- //设置FileInputStream,先用BufferedInputStream装饰它,再用我们崭新的
- //LowerCaseInputStream过滤器装饰它
- InputStream in =
- new LowerCaseInputStream(
- new BufferedInputStream(new FileInputStream("D:/test.txt")));
- while((c = in.read()) >= 0) {
- System.out.print((char)c);
- }
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
测试结果:
- i know the decorator pattern
ok,装饰者模式就是这样子啦,搞清楚装饰者和被装饰者之间的关系,也没啥不好理解的。下一个设计模式:工厂模式。