23种常用设计模式-装饰器(Decorator)模式

246次阅读
没有评论

共计 3722 个字符,预计需要花费 10 分钟才能阅读完成。

内容目录

装饰器模式

概述

 装饰器(Decorator)模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它是作为现有类的一个包装。

 装饰器模式通过将对象包装在装饰器类中,以便动态地修改其行为。

类型

 装饰器模式模式属于结构型模式。

介绍

  • 目的:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比通过继承父类的子类来说更为灵活。
  • 主要解决:我们在扩展一个类时普遍使用继承方式实现,但由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。而装饰器模式就可以解决这一点。
  • 使用场景:不想增加很多子类,但需要扩展类的功能。

示例代码

 这里以一段程序为例,该程序的功能是给一段文字添加装饰边框。这里的边框是指用“-”、“+”和“|”字符组成的边框。

+-----------+
|Hello,Java!|
+-----------+

Display抽象类

 该类的含义是用于显示字符串的抽象类,在该类中,定义了三个抽象方法:getColumnsgetRowsgetRowText,功能是:获取横向字符串、获取纵向行数和获取第row行的字符串,同时使用了模板方法模式,实现了一个show方法,该方法会调用这三个抽象方法。

package decorator;

public abstract class Display {
    abstract int getColumns(); // 获取列数(横向的字符数)
    abstract int getRows(); // 获取行数
    abstract String getRowText(int row);
    public final void show(){
        for (int i = 0; i < getRows(); i++) {
            System.out.println(getRowText(i));
        }
    }
}

StringDisplay类

 该类是Display的子类,用于显示单行字符串的类,它需要实现Display抽象类中声明的抽象方法。由于StringDisplay是用于显示一行字符串,因此它的getColumns返回的是字符串的长度,getRows返回1。

package decorator;

public class StringDisplay extends Display{

    private String string;

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

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

    @Override
    int getRows() {
        return 1;
    }

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

Border抽象类

 Border抽象类是装饰边框的抽象类,同时它也是Display的子类。这意味着装饰边框与被装饰物具有了相同的方法。从接口角度而言,装饰边框与被装饰物具有相同的方法,这也就意味着它们具有一致性。

package decorator;

public abstract class Border extends Display {
    private Display display;
    public Border(Display display){
        this.display = display;
    }
}

SideBorder类

 SideBorder类是一种具体的装饰边框类,是Border类的子类。在这个装饰类中,它用于指定某一字符装饰字符串的左右两侧。

package decorator;

public class SideBorder extends Border{

    private char borderChar;

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

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

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

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

FullBorder类

 FullBorder类与SideBorder类一样,也是Border类的子类。FullBorder类会在字符串的上下左右都加上装饰边框,而在FullBorder类中,边框的字符是固定的。同时内部还额外声明了一个makeLine方法,该方法可以连续地显示某个指定的字符。

package decorator;

public class FullBorder extends Border{

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

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

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

    @Override
    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){
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < count; i++) {
            buf.append(ch);
        }
        return buf.toString();
    }
}

 测试代码及运行结果如下:

package decorator;

import org.junit.Test;

public class DecoratorTest {
    @Test
    public void decoratorTest(){
        Display s1 = new StringDisplay("Hello World!");
        Display s2 = new SideBorder(s1, '$');
        Display s3 = new FullBorder(s2);
        s1.show();
        s2.show();
        s3.show();

        Display s4 = new StringDisplay("Hello Java!");
        // 给s4左右两边加$符号
        s4 = new SideBorder(s4, '$');
        // 再给s4加两层全边框
        s4 = new FullBorder(s4);
        s4 = new FullBorder(s4);
        // 再给s4两边加一次'/'符号、两次$符号
        s4 = new SideBorder(s4,'/');
        s4 = new SideBorder(s4,'$');
        s4 = new SideBorder(s4,'$');
        // 在给s4加一层全边框
        s4 = new FullBorder(s4);
        s4.show();
    }
}
Hello World!
$Hello World!$
+--------------+
|$Hello World!$|
+--------------+
+-----------------------+
|$$/+---------------+/$$|
|$$/|+-------------+|/$$|
|$$/||$Hello Java!$||/$$|
|$$/|+-------------+|/$$|
|$$/+---------------+/$$|
+-----------------------+

 类图如下

23种常用设计模式-装饰器(Decorator)模式

装饰器模式中的角色

  • Component:增加功能时的核心角色,该角色定义了被装饰对象和装饰器对象的公共接口抽象类。上述示例中,Display扮演着这一角色。
  • ConcreteComponent:具体的被装饰对象。StringDisplay扮演着这一角色。
  • Decorator:该角色具有与Component角色相同的接口。在它内部保存了被装饰对象实例,也就是说,Decorator知道自己需要装饰的对象。Border扮演着这一角色。
  • ConcreteDecorator:该角色是具体的装饰类。SideBorderFullBorder类均扮演着这一角色。

23种常用设计模式-装饰器(Decorator)模式

其它

 装饰器模式通过嵌套包装多个装饰器对象,可以实现多层次的功能增强。每个具体装饰器类都可以选择性地增加新的功能,同时保持对象接口的一致性。

正文完
 
PG Thinker
版权声明:本站原创文章,由 PG Thinker 2023-10-17发表,共计3722字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
热评文章
Rust中所有权与借用规则概述

Rust中所有权与借用规则概述

在GC与手动管理内存之间,Rust选择了第三种:所有权机制...