一、介绍

装饰(Decorator)模式又叫包装(Wrapper)模式,装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

装饰模式以对客户透明的方式动态的给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

二、代码部分

1、装饰器原理

原理1-1.png

首先我们新建一个接口Component,他有一个sampleOperation( )方法

public interface Component {
    void sampleOperation();
}

他的一个实现类如下

public class ConcreteComponent implements Component {
    @Override
    public void sampleOperation() {
        System.out.println("this is concreteComponent's sampleOperation...");
    }
}

现在我们创建一个装饰器,将Component当成属性

通过构造器的形式来给Decorator类中的component赋值

同时实现了传入的component的sampleOperation( )方法

public class Decorator implements Component {
    /**
     * Component当属性,使用构造器来赋值
     */
    private Component component;

    public Decorator(Component component){
        this.component = component;
    }

    public Decorator(){

    }

    @Override
    public void sampleOperation() {
        component.sampleOperation();
    }
}

具体的装饰器继承了装饰器类

增加了方法addAction( )

public class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component){
        super(component);
    }

    public void sampleOperation(){
        super.sampleOperation();
    }

    public void addAction(){
        System.out.println("dkdkdkd");
    }
}

测试类

public class DecoratorClient0 {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        System.out.println("==========以下是ConcreteComponent调用的方法==========");
        component.sampleOperation();
        ConcreteDecorator cc = new ConcreteDecorator(component);
        System.out.println("==========以下是ConcreteDecorator调用的方法==========");
        cc.sampleOperation();
        cc.addAction();
    }
}
//结果如下
==========以下是ConcreteComponent调用的方法==========
this is concreteComponent's sampleOperation...
==========以下是ConcreteDecorator调用的方法==========
this is concreteComponent's sampleOperation...
dkdkdkd

这就是简单的装饰器原理的例子,到此想必大家对装饰器有了一定的理解,那么举一个实例吧。

2、实例

现在有一个简单的需求,需要完成日志的控制台打印和存储到txt文件

先看类图

实例1-1.png

来一个ILogger接口

public interface ILogger {
    public void log(String msg);
}

两个子类(ConsoleLogger和FileLogger)实现ILogger接口

//打印日志到控制台
public class ConsoleLogger implements ILogger {
    @Override
    public void log(String msg) {
        System.out.println(msg);
    }
}

//存储日志到文件
public class FileLogger implements ILogger {
    @Override
    public void log(String msg) {
        DataOutputStream dataOutputStream = null;
        try {
            dataOutputStream = new DataOutputStream(new FileOutputStream("Decorator/src/intro0/decorator0.txt"));
            dataOutputStream.writeBytes(msg);
            dataOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

主类实现

public class DecoratorIntroClient {
    public static void main(String[] args) {
        ILogger[] iLoggers = {new ConsoleLogger(),new FileLogger()};
        for (ILogger iLogger : iLoggers) {
            iLogger.log("this is decorator pattern...");
        }
    }
}

到此我们实现了需求,现在需求增加了,我们需要增加需求使日志变为大写的XML形式存入到文件中

根据下方UML图可以看出,红框是我们之前实现的需求,右边是新的需求,通过装饰器来实现

实例1-2.png

在上面那个项目中加入

抽象的装饰器类,实现ILogger接口

将ILogger作为一个字段,通过构造器给这个字段赋值

public abstract class Decorator implements ILogger {

    protected ILogger logger;

    public Decorator(ILogger logger){
        this.logger = logger;
    }
}

UPCaseLogger类和XMLLogger类,他们都继承Decorator类

public class UPCaseLogger extends Decorator {

    public UPCaseLogger(ILogger logger) {
        super(logger);
    }

    @Override
    public void log(String msg) {
        msg = msg.toUpperCase();
        //往外面写,因为他的父类中定义了logger,便可直接调用
        logger.log(msg);
    }
}


public class XMLLogger extends Decorator {

    public XMLLogger(ILogger logger) {
        super(logger);
    }

    @Override
    public void log(String msg) {
        String str = "<msg>\r\n"
                +"<content>"+msg+"</content>\r\n"
                +"<time>"+new Date().toString() + "</time>\r\n"
                +"</msg>\r\n";
        logger.log(str);
    }
}

来一个测试类

public class Decorator1Client {
    public static void main(String[] args) {
        ILogger fileLogger = new FileLogger();
        ILogger upLogger = new UPCaseLogger(fileLogger);
        ILogger xmlLogger = new XMLLogger(upLogger);
        String str = "abc123xyz";
        xmlLogger.log(str);
        System.out.println("completed!");
    }
}

//执行后txt中信息如下
<MSG>
<CONTENT>ABC123XYZ</CONTENT>
<TIME>MON APR 13 13:04:36 CST 2020</TIME>
</MSG>
Last modification:October 24th, 2020 at 02:51 pm
如果觉得我的文章对你有用,请随意赞赏