装饰边框与被装饰物的一致性-Decorator模式

77 阅读2分钟

1、引入

说起装饰,我第一时间想到的是节日里给礼物的层层包装。

image.png 而装饰模式,就是提供特定的几个装饰器,不断给被装饰品添上一层层装饰,以实现丰富的外观形态。

而能够不断被装饰,需要装饰的“边框”与被装饰物具有一致性,使得被装上边框后还能够作为装饰物为其他边框所装饰。

2、示例

实现功能:能够为字符串不断添加各类边框,形成复杂的结构

2.1、装饰边框与被装饰物的统一父类Dispaly

public abstract class Display {
    public abstract int getColumns();

    public abstract int getRows();

    public abstract String getRowText(int row);

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

2.2、字符串展示StringDislay类

public class StringDisplay extends Display{
    private String string;

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

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

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

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

2.3、边框

因为要求边框与被装饰物一致性,因此边框也要继承Display类

2.3.1、边框统一父类

public abstract class Border extends Display {
    protected Display display;

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

2.3.2、边框1-两边边框

public class SideBorder extends Border{
    private char borderChar;

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

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

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

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

2.3.3、边框2-包围边框

public class FullBorder extends Border{
    public FullBorder(Display display) {
        super(display);
    }

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

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

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

    private String makeLine(char c,int count) {
        StringBuffer s=new StringBuffer();
        for (int i = 0; i < count; i++) {
            s.append(c);
        }
        return s.toString();
    }
}

2.4、测试

public class Main {
    public static void main(String[] args) {
        StringDisplay hello_world = new StringDisplay("Hello World");
        SideBorder b1 = new SideBorder(hello_world, '#');
        FullBorder b2 = new FullBorder(b1);
        hello_world.show();
        b1.show();
        b2.show();
    }
}

运行结果:

image.png

还可以不断进行“套娃”,形成更复杂的结构

3、tips

  • 在先前已经强调过,Border也要作为Display的子类,这样能够和被装饰物一样的,暴露相同的接口,就算被装饰物被隐蔽在边框中,接口也能向外暴露被装饰物的信息,实现了透明性。得益于这种透明性,边框也可以被视为被装饰物。
  • 装饰器运用范围很广,如Spring框架中我们熟悉的AOP,不就是一种框架提供的装饰器;而在python语言中,甚至直接为使用者提供了语言层面的@property装饰器