zoukankan      html  css  js  c++  java
  • 《图解设计模式》读书笔记5-2 decorator模式


    Decorator模式即装饰器模式,就是对类进行装饰,下面通过代码说明。

    代码演示

    代码展示的内容

    有一个类StringDisplay:表示一句话,比如hello world。

    我们通过装饰器对这句话进行装饰——加上左右边框形成|hello world|,再加上上下边框线包围这句话,形成

    +-------------+
    |Hello, world.|
    +-------------+
    

    UML图

    每个类的解释

    Display类:代表一段话的显示。使用了模版方法模式,定义了获取列数,行数等抽象方法,规定了这些方法的使用方式。

    StringDisplay类:代表一句话的显示。继承并实现了Display中的所有抽象方法。

    Border类:代表对一句话的装饰。继承并委托了Display类。是个抽象的装饰类。

    SideBorder类:代表对某一行的左右添加装饰字符。继承Border类(意味着同时继承了Display类),实现了他们的抽象方法。

    FullBorder类:代表对某一行的上下左右添加装饰字符。和SideBorder类一样,继承并实现了Border类和Display类的抽象方法。

    代码

    public abstract class Display {
        public abstract int getColumns();               // 获取横向字符数
        public abstract int getRows();                  // 获取纵向行数
        public abstract String getRowText(int row);     // 获取第row行的字符串
        public void show() {                            // 全部显示
            for (int i = 0; i < getRows(); i++) {
                System.out.println(getRowText(i));
            }
        }
    }
    
    public class StringDisplay extends Display {
        private String string;                          // 要显示的字符串
        public StringDisplay(String string) {           // 通过参数传入要显示的字符串
            this.string = string;
        }
        public int getColumns() {                       // 字符数
            return string.getBytes().length;
        }
        public int getRows() {                          // 行数是1
            return 1;
        }
        public String getRowText(int row) {             // 仅当row为0时返回值
            if (row == 0) {
                return string;
            } else {
                return null;
            }
        }
    }
    
    public abstract class Border extends Display {
        protected Display display;          // 表示被装饰物
        protected Border(Display display) { // 在生成实例时通过参数指定被装饰物
            this.display = display;
        }
    }
    
    public class SideBorder extends Border {
        private char borderChar;                        // 表示装饰边框的字符
        public SideBorder(Display display, char ch) {   // 通过构造函数指定Display和装饰边框字符 
            super(display);
            this.borderChar = ch;
        }
        public int getColumns() {                       // 字符数为字符串字符数加上两侧边框字符数 
            return 1 + display.getColumns() + 1;
        }
        public int getRows() {                          // 行数即被装饰物的行数
            return display.getRows();
        }
        public String getRowText(int row) {             // 指定的那一行的字符串为被装饰物的字符串加上两侧的边框的字符 
            return borderChar + display.getRowText(row) + borderChar;
        }
    }
    
    public class FullBorder extends Border {
        public FullBorder(Display display) {
            super(display);
        }
        public int getColumns() {                   // 字符数为被装饰物的字符数加上两侧边框字符数
            return 1 + display.getColumns() + 1;
        }
        public int getRows() {                      // 行数为被装饰物的行数加上上下边框的行数
            return 1 + display.getRows() + 1;
        }
        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 ch, int count) {         // 生成一个重复count次字符ch的字符串 
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < count; i++) {
                buf.append(ch);
            }
            return buf.toString();
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Display b1 = new StringDisplay("Hello, world.");
            Display b2 = new SideBorder(b1, '#');
            Display b3 = new FullBorder(b2);
            b1.show();
            System.out.println();
            b2.show();
            System.out.println();
            b3.show();
            System.out.println();
            Display b4 = 
                        new SideBorder(
                            new FullBorder(
                                new FullBorder(
                                    new SideBorder(
                                        new FullBorder(
                                            new StringDisplay("hello world!!!")
                                        ),
                                        '*'
                                    )
                                )
                            ),
                            '~'
                        );
            b4.show();
        }
    }
    
    /*
    结果
    Hello, world.
    
    #Hello, world.#
    
    +---------------+
    |#Hello, world.#|
    +---------------+
    
    ~+--------------------+~
    ~|+------------------+|~
    ~||*+--------------+*||~
    ~||*|hello world!!!|*||~
    ~||*+--------------+*||~
    ~|+------------------+|~
    ~+--------------------+~
    */
    
    

    模式的角色和类图

    角色

    • Component:被装饰的角色,只定义了API。在本例中,由Display类扮演此角色。
    • ConcreteComponent:实现了Component的API,是具体的被装饰的角色。本例中,由StringDisplay类扮演此角色。
    • Decorator:装饰者,具有和Component相同的API,内部保留了被装饰对象Component。本例中,由Border类扮演此角色。
    • ConcreteDecorator:具体的装饰者。本例中由SideBorder和FullBorder扮演此角色。

    类图

    思路拓展

    接口(API)的透明性

    Decorator继承了Component,装饰物和被装饰物具有一致性——他们有着相同的API接口。即便API接口被装饰了一遍,也不会被隐藏起来,其他类依然可以调用被装饰后的API接口。可以用这个特性实现递归装饰。

    为什么使用继承和委托

    使用继承,是为了获得一致性,如上所说。

    为什么使用委托呢?

    如果只有继承,由于Decorator继承的是抽象的被装饰类,意味着我们要再实现一遍被装饰者的API,一旦被装饰类的API的逻辑发生改变,被装饰者也要改一次。

    那如果Decorator继承的是具体的被装饰类呢?这样可以实现类似于委托那样的实现,直接调用父类的方法就可以了。

    这样做的坏处至少有一个:有多少个具体的被装饰类,就要写多少遍装饰类。麻烦死了,而且重复代码一堆,绝不是什么好事情。

    所以还是用委托吧。

    java.io包和装饰者模式

    //读取文件
    Reader reader = new FileReader("xxxx.txt");
    //读取时将文件放入缓冲区
    Reader reader = new BufferReader(
        				new FileReader("xxxx.txt") 
    			   );
    //还要管理行号
    Reader reader = new LineNumberReader(
        				new BufferReader(
        					new FileReader("xxxx.txt") 
    			  		)
    			   );
    ...
    

    缺点

    增加很多功能类似的很小的修饰类

  • 相关阅读:
    嵌入式设备之web服务器
    动环监控的设备架构设计
    墓碑机制
    淘宝APP消息推送模型
    Service Mesh架构的持续演进 单体模块化 SOA 微服务 Service Mesh
    网关架构演进之路
    理解了云原生,才能正确迎接云时代的到来 | 技术前沿 百度智能云 2019-09-10
    策略模式 VS 状态模式
    权限管理机制支持多种访问控制模型。
    二维码生成服务架构的演进与思考
  • 原文地址:https://www.cnblogs.com/qianbixin/p/10992969.html
Copyright © 2011-2022 走看看