装饰器模式

96 阅读3分钟

背景

不断为对象添加装饰的设计模式被称为 装饰器模式 。首先有一个相当于蛋糕的对象,然后不断地装饰蛋糕一样地不断对其增加功能,它就变成了使用目的更加明确的对象

登场角色

Compoent 被装饰的对象

增加功能的核心角色,例如示例程序中的 Display

ConcreteCompoent 具体被装饰的对象

该角色是实现了 Component 角色所定义的接口的具体角色,例如示例程序的 StringDisplay

Decorator 装饰物

该角色具有和Compoent 角色相同的接口,在它内部保存了被装饰的对象-Component角色。Decorator角色知道自己要装饰的对象,例如示例程序中的 border

ConcreteDecorator 具体的装饰物

该角色是具体的Decorator角色,在示例程序中,由 SiderBorderFullBorder 角色扮演

类图

示例代码

该示例程序的功能是显示一个字符串,并在它周围装饰不同的边框

Display 类

public abstract class Display {

    public abstract int getRows();

    public abstract int getColumns();

    public abstract String getRowText(int row);

    public void show() {
        for (int i = 0; i < getRows(); i++) {
            System.out.println(getRowText(i));
        }
    }
}

StringDisplay类

public class StringDisplay extends Display{

    private String text;

    public StringDisplay(String text) {
        this.text = text;
    }

    @Override
    public int getRows() {
        return 1;
    }

    @Override
    public int getColumns() {
        return text.getBytes().length;
    }

    @Override
    public String getRowText(int row) {
        if (row == 0) {
            return text;
        }

        return null;
    }
}

Border类

public abstract class Border extends Display {

    // 被装饰物
    public Display display;

    public Border(Display display) {
        this.display = display;
    }
}

SideBorder类:在字符串左右添加边框

public class SideBorder extends Border{
    private char aChar;

    public SideBorder(Display display, char aChar) {
        super(display);
        this.aChar = aChar;
    }

    @Override
    public int getRows() {
        return display.getRows();
    }

    @Override
    public int getColumns() {
        return 1 + display.getColumns() + 1;
    }

    @Override
    public String getRowText(int row) {
        return aChar + display.getRowText(row) + aChar;
    }
}

FullBorder类:在字符串上下左右添加边框

public class FullBorder extends Border {

    public FullBorder(Display display) {
        super(display);
    }

    @Override
    public int getRows() {
        return 1 + display.getRows() + 1;
    }

    @Override
    public int getColumns() {
        return 1 + display.getColumns() + 1;
    }

    @Override
    public String getRowText(int row) {
        if (row == 0) {
            return "+" + makeLine('-', display.getColumns()) + "+";
        } else if (row == display.getRows() + 1) {
            return "+" + makeLine('-', display.getColumns()) + "+";
        } else {
            return "|" + display.getRowText(row - 1) + "|";
        }
    }

    private String makeLine(char c, int count) {
        StringBuffer stringBuffer = new StringBuffer();

        for (int i = 0; i < count; i++) {
            stringBuffer.append(c);
        }
        return stringBuffer.toString();
    }
}

功能分析

  1. 关键的一步就是把被装饰的对象聚合到装饰器中;
  2. 在Decorator模式中,被装饰物和装饰物具有一致性。因为他们都继承了Component抽象类,这样即使装饰物被被装饰物装饰起来,接口(API)也不会被隐藏起来,其他类依然可以调用抽象类中的方法,这就是接口API的透明性;
  3. 在不改变被装饰物的前提下增加功能,因为被装饰物和装饰物都实现了相同的接口,使用不同的装饰物就可以装饰不同的物品,实现不同的功能,实现不改变了装饰物的代码;
  4. Decorator 模式的缺点就是会导致一些功能类似的很小的类;

使用实例

在 java.io 包下的FileInputstream 和 InputStream 中使用了 Decorator 模式

延伸扩展:继承和委托中的一致性

一致性:可以将不同的东西当作同一种东西看待

继承 —— 父类和子类的一致性

child 实例 可以被保存在父类的类型变量中,调用父类的方法

public class Test {
    public static void main(String[] args) {
        Parent obj = new Child();
        obj.parentMethod();
    }
}

通过类型转换,可以调用子类的方法

public class Test {
    public static void main(String[] args) {
        Parent obj = new Child();
        ((Child)obj).childMethod();
    }
}

委托 —— 自己和被委托对象的一致性

定义一个公共接口

public interface Flower {
    void method();
}

定义两个类 Rose Violet,Rose把method方法委托给Violet

public class Violet implements Flower{
    @Override
    public void method() {

    }
}
public class Rose implements Flower {

    Violet violet;

    @Override
    public void method() {
        violet.method();
    }
}