装饰模式

什么装饰模式

装饰器模式(Decorator Pattern)也叫包装器模式,是一种结构型设计模式,允许用户在不改变对象的情况下,动态地给对象增加一些额外的职责(功能)。装饰器模式相比生成子类更为灵活,因为可以在运行时根据需要动态地添加或删除功能。

职责

  • 动态的为一个对象增加新的功能
  • 装饰模式是一种用于替代继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

实现细节

  • 抽象组件(Component):定义一个抽象接口,用于规定准备接收附加责任的对象,即可以给这些对象动态地添加职责。
  • 具体组件(ConcreteComponent):实现了抽象组件的接口,定义了一个具体的“被装饰”对象,这个对象可以被装饰器动态地添加功能。
  • 抽象装饰器(Decorator):继承自抽象组件,并持有一个抽象组件的引用。抽象装饰器中声明了与抽象组件相同的方法,并保留了新增功能的接口,以供具体装饰器添加新的功能。
  • 具体装饰器(ConcreteDecorator):实现抽象装饰器所增加的功能,并在调用原有方法时,增加新的功能。

优点

  • 扩展对象功能,比继承灵活,不会导致类个数的急剧增加
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
  • 具体构件类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。

缺点

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能
  • 装饰器模式容易出错,调试排查比较麻烦

案例

一台普通通话手机,可扩展出具有拍照功能,NCF功能,联网功能

UML

03-JAVA设计模式-装饰模式-LMLPHP

实现步骤:

  1. 定义一个抽象组件,用于规定准备接收附加功能/职责的方法IPHONE
  2. 定义一个具体组件,实现抽象组件,及实现具备基础功能/职责
  3. 定义一个抽象装饰器,继承抽象组件,并持有一个抽象组件的引用。实现抽象组件接口方法中调用传入接口组件引用的具体实例的方法,达到调用已具备功能/职责
  4. 定义一个/多个具体装饰器,继承抽象装饰器,增加新功能,实现抽象接口时调用原有方法,及新增功能/职责

实现代码

IPhone.java

// 抽象组件:
// *  定义一个手机具备某种功能接口的抽象类
public interface IPhone {
     // 具备功能
     void function();
}

Phone.java

// 具体组件:
// 定义一个具体组件包含最基础的功能:普通手机
public class Phone implements IPhone {
    @Override
    public void function(){
        System.out.println("具备通讯功能");
    };
}

PhoneExtendFunction.java

//抽象装饰器
// 继承自抽象组件,并持有一个抽象组件的引用
public class PhoneExtendFunction implements IPhone{
    // 持有一个抽象组件的引用,调用已具有的功能
    private IPhone phone;

    public PhoneExtendFunction(IPhone phone) {
        this.phone = phone;
    }
    @Override
    public void function() {
        phone.function();
    }
}

InternetFunction.java

// 具体装饰器
// 实现抽象装饰器所增加的功能,并在调用原有方法时,增加新的功能。
public class InternetFunction extends PhoneExtendFunction {
    public InternetFunction(IPhone phone) {
        super(phone);
    }
    public void internet(){
        System.out.println("联网功能");
    }
    @Override
    public void function() {
        super.function();
        internet();
    }
}

NfcFunction.java

// 具体装饰器
// 实现抽象装饰器所增加的功能,并在调用原有方法时,增加新的功能。
public class NfcFunction extends PhoneExtendFunction{
    public NfcFunction(IPhone phone) {
        super(phone);
    }
    public void nfc(){
        System.out.println("NFC功能");
    }
    @Override
    public void function() {
        super.function();
        nfc();
    }
}

PhotographFunction.java

// 具体装饰器
// 实现抽象装饰器所增加的功能,并在调用原有方法时,增加新的功能。
public class PhotographFunction extends PhoneExtendFunction{
    public PhotographFunction(IPhone phone) {
        super(phone);
    }
    public void photograph(){
        System.out.println("摄影功能");
    }
    @Override
    public void function() {
        super.function();
        photograph();
    }
}

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        PhotographFunction phone = new PhotographFunction(new InternetFunction(new NfcFunction(new Phone())));
        phone.function();
    }
}

执行结果:

03-JAVA设计模式-装饰模式-LMLPHP

装饰器模式和桥接模式的区别

装饰器模式的主要特点是在不改变现有对象结构的情况下,动态地给对象增加一些职责(功能)。装饰器是继承的有力补充,比继承更加灵活。通过使用不同的装饰类和它们的排列组合,可以实现不同的效果。在装饰器模式中,通常会有一个抽象组件和多个具体装饰器,每个装饰器都可以为对象添加新的功能,而且这些装饰器可以灵活地叠加使用。

桥接模式则是将抽象化与实现化分离,使它们可以独立变化。桥接模式包括两个继承体系:抽象部分和实现部分。抽象部分定义了一个接口,规定了实现部分需要实现的方法。实现部分则是具体的实现细节。通过这种方式,桥接模式可以减少派生类的增长,因为你可以将不同的抽象部分和实现部分进行组合,从而得到不同的功能。

两者的主要区别在于:

  • 关注点不同:装饰器模式关注于在不改变对象结构的情况下动态增加功能,而桥接模式关注于将抽象与实现分离,使它们能够独立演化。
  • 结构差异:装饰器模式是通过包装一个已存在的对象,并为其增加新的功能或行为。桥接模式则是通过抽象与实现的分离来组合不同的功能。
  • 行为的叠加性:在装饰器模式中,装饰的行为可以叠加,装饰后的对象可以拥有多个装饰器所添加的功能。而在桥接模式中,行为通常不会叠加,而是通过抽象与实现的组合来提供不同的功能。
  • 稳定性与灵活性:装饰器模式中的对象本身比较稳定,主要是为了增加新功能。而桥接模式本身可能不太稳定,因为实现部分可以独立变化。然而,这也使得桥接模式更加灵活,可以适应更多的变化和需求。

gitee源码

04-11 00:13