Java设计模式之装饰器模式

装饰器模式

装饰器模式又称为包装(Wrapper)模式,是一种结构型模式。装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

A类是原始功能的类,B是装饰模式中对A类扩展之后的类,C是代理模式中对A类扩展之后的类。于用户调用来说,使用装饰模式,用户更关心的是B的功能(包含A的原始功能),使用代理模式,用户更关心A的功能,并不关心C的功能。

装饰器模式主要用于对原有的功能进行扩展和增强,代理模式主要用于对原有的部分功能进行代理或者增强。装饰器模式与代理模式都可以对原有功能进行增强,没有必要过于纠结两种模式的区别,只要在实际工作中会用即可。

当要扩展一个类的功能时,用装饰器模式,如果要对原有的方法进行改进,则用代理模式

举个简单的例子:一家饭馆,有点餐、做餐和出餐的流程。有一种高科技眼镜,厨师带上之后可以根据眼镜提供的高端技巧提高做菜的水平。为了提高点餐和出餐的效率,商家可以委托外卖平台代理完成点餐和出餐的功能。在这个例子中,高科技眼镜对于厨师来说就是使用了装饰器模式,外卖平台对于商家来说就是使用了代理模式。

装饰器模式的结构

通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生子类来扩展,抑或是使用对象组合的方式。显然,直接修改对应的类的方式并不可取,在面向对象的设计中,我们应该尽量使用组合对象而不是继承对象来扩展和复用功能,装饰器模式就是基于对象组合的方式的。

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

装饰器模式中的角色有:

1、抽象构件角色

给出一个抽象接口,以规范准备接受附加责任的对象

2、具体构件角色

定义一个将要接受附加责任的类

3、装饰角色

持有一个构建对象的实例,并定义一个与抽象构件接口一致的接口

4、具体装饰角色

负责给构件对象贴上附加的责任

1、装饰器模式的例子

假如有这么一个场景:

有一家餐馆的厨师水平一般,为了提高厨师的水平,给厨师配了一副高科技眼镜,厨师带上之后可以根据眼镜提供的高端技巧提高做菜的水平。

按照装饰器模式,第一步先定义出抽象构建角色,如厨师接口Cook:

public interface Cook {
    public void cookDinner();
}

第二步创建具体构建角色,如中餐厨师:

public class ChineseCook implements Cook {

    @Override
    public void cookDinner() {
        System.out.println("中餐厨师做晚餐");
    }
    
}

第三步定义一个抽象装饰器角色,例如高科技眼镜,如果外国厨师也想提高做餐水平,也可以配备这种眼镜。装饰器角色定义为FilterCook,实现Cook接口并持有Cook的引用:

public abstract class FilterCook implements Cook {

    protected Cook cook;
    
}

最后定义具体的装饰角色:

public class GlassesCook extends FilterCook {

    public GlassesCook(Cook cook) {
        this.cook = cook;
    }
    
    @Override
    public void cookDinner() {
        System.out.println("带上眼镜提高做餐水平");
        cook.cookDinner();
    }
    
}

调用方这么实现:

@Test
public void testDecorate() {
    Cook cook = new GlassesCook(new ChineseCook());
        
    cook.cookDinner();
}

运行结果为:

带上眼镜提高做餐水平

中餐厨师做晚餐

简单的一个例子,实现了装饰器模式的两个功能点:

1、客户端只定义了Cook接口,并不关心具体实现。

2、给中国厨师提高了做餐水平,也可以给其他国家的厨师类复用。

这就是装饰器模式。

2、装饰器模式与InputStream

装饰器模式在Java体系中的经典应用是Java I/O。首先看一下字节输入流InputStream的类结构体系:

InputStream是一个顶层的接口,文章开头就说,装饰器模式是继承关系的一种替代方案,看一下为什么:

InputStream有多个实现类,其中FileInputStream和ObjectInputStream分别表示文件字节输入流和对象字节输入流。现在要给这两个输入流加入一点缓冲功能以提高输入流效率,使用继承的方式,那么就创建一个FileBufferedInputStream继承FileInputStream,再创建一个ObjectBufferedInputStream继承ObjectInputStream,给它们加上相同的功能,两个子类具有相同的缓冲功能,代码重复。现在有另外一个需求,需要给这两个输入流加入一点网络功能,那么就再写两个子类FileSocketInputStream和ObjectSocketInputStream,分别继承FileInputStream和ObjectInputStream,给它们加功能。

这样就导致两个问题:

1、因为要给哪个类加功能就必须继承它,比如要给FileInputStream,ObjectInputStream加上缓冲功能、网络功能就得扩展出2*2=4个类,更多的以此类推,这样势必导致类数量不断膨胀。

2、代码无法复用,给FileInputStream,ObjectInputStream加入缓冲功能,本身代码应该是一样的,现在却必须继承之后把一样的代码重写一遍,多此一举,代码修改的时候必须修改多个地方,可维护性很差。

所以,这个时候就想到了一种解决方案:

在要扩展的类比如BufferedInputStream中持有一个InputStream的引用,通过BufferedInputStream的构造方法来初始化InputStream的具体实例对象,在BufferedInputStream中调用InputStream中的方法,将需要增加功能的具体实例对象作为参数传给BufferedInputStream的构造方法,这样扩展的代码就可以复用起来。

将BufferedInputStream作为InputStream的子类,这样客户端只知道我用的是InputStream而不需要关心具体实现,可以在客户端不知情的情况下,扩展InputStream的功能,加上缓冲功能。

这就是装饰器模式简单的由来,一切都是为了解决实际问题而诞生。下一步,根据UML图,我们来划分一下装饰器模式的角色。

1、InputStream是一个抽象构建角色

public abstract class InputStream implements Closeable {

    // SKIP_BUFFER_SIZE is used to determine the size of skipBuffer
    private static final int SKIP_BUFFER_SIZE = 2048;
    // skipBuffer is initialized in skip(long), if needed.
    private static byte[] skipBuffer;
    ...
}

2ByteArrayInputStreamFileInputStreamObjectInputStreamPipedInputStream都是具体构建角色,比如FileInputStream

public class FileInputStream extends InputStream {
    /* File Descriptor - handle to the open file */
    private FileDescriptor fd;

    private FileChannel channel = null;
    ...
}

3FilterInputStream无疑就是一个装饰角色,因为FilterInputStream实现了InputStream内的所有抽象方法并且持有一个InputStream的引用

public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered. 
     */
    protected volatile InputStream in;
    ...
}

4、具体装饰角色就是InflaterInputStreamBufferedInputStreamDataInputStream,比如BufferedInputStream

public class BufferedInputStream extends FilterInputStream {

    private static int defaultBufferSize = 8192;

    /**
     * The internal buffer array where the data is stored. When necessary,
     * it may be replaced by another array of
     * a different size.
     */
    protected volatile byte buf[];
	...
	public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

	/**
	* Creates a <code>BufferedInputStream</code>
	* with the specified buffer size,
	* and saves its  argument, the input stream
	* <code>in</code>, for later use.  An internal
	* buffer array of length  <code>size</code>
	* is created and stored in <code>buf</code>.
	*
	* @param   in     the underlying input stream.
	* @param   size   the buffer size.
	* @exception IllegalArgumentException if {@code size <= 0}.
	*/
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

}

具体使用:

public static void main(String[] args) throws Exception
{
    File file = new File("D:/aaa.txt");
    InputStream in0 = new FileInputStream(file);
    InputStream in1 = new BufferedInputStream(new FileInputStream(file)); 
    InputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
}

这里实例化出了三个InputStream的实现类:

in0这个引用指向的是new出来的FileInputStream,这里简单构造出了一个文件字节输入流。

in1这个引用指向的是new出来的BufferedInputStream,它给FileInputStream增加了缓冲功能,使得FileInputStream读取文件的内容保存在内存中,以提高读取的性能。

in2这个引用指向的是new出来的DataInputStream,它也给FileInputStream增加了功能,因为它有DataInputStream和BufferedInputStream两个附加的功能。

同理,我要给ByteArrayInputStream、ObjectInputStream增加功能,也可以使用类似的方法,整个过程中,最重要的是要理解几个问题:

1、哪些是具体构建角色、哪些是具体装饰角色,尤其是后者,区分的关键就是,角色中是否持有顶层接口的引用

2、每个具体装饰角色有什么作用,因为只有知道每个具体装饰角色有什么作用后,才可以知道要装饰某个功能需要用哪个具体装饰角色。

3、使用构造方法的方式将类进行组合,给具体构建角色加入新的功能。

<think>好的,用户想了解Java中的装饰器模式的示例及用法。首先,我需要回忆装饰器模式的基本概念。装饰器模式属于结构型设计模式,允许动态地给对象添加职责,而不需要修改其类代码。这通过将对象包装在装饰器类中来实现。 接下来,我应该确认用户可能的背景。用户可能已经有一定的Java基础,但不太清楚如何具体应用装饰器模式。可能需要实际示例来理解其用法。根据用户提供的引用内容,装饰器模式Java IO类中应用广泛,比如FileReader和BufferedReader。这点可以作为例子来说明。 然后,我需要整理示例代码的结构。装饰器模式通常包含组件接口、具体组件、装饰器抽象类和具体装饰器。比如,用户提供的引用中有XIAOMIPhone的例子,可能是一个被装饰者类。但需要确保示例的准确性和易懂性。 另外,用户可能想知道装饰器模式的应用场景。根据引用内容,装饰器模式适用于动态扩展功能,比如Java IO、日志记录、权限校验等。这些场景需要在不修改原有对象结构的情况下添加功能。 需要注意用户的问题可能还涉及到装饰器模式与继承的区别。装饰器模式提供了更灵活的扩展方式,避免了类爆炸的问题。这部分也应该在回答中提及,以帮助用户理解其优势。 最后,要确保回答符合用户的要求,结构清晰,分步骤解释,并提供相关的问题供进一步思考。同时,正确引用提供的参考资料,比如Java IO类的例子,并在适当位置添加引用标识。</think>### Java 装饰器模式 示例及用法 #### 1. 装饰器模式核心思想 装饰器模式通过**包装对象**的方式动态扩展功能,而无需修改原有类代码。其结构包含: - **组件接口**(Component):定义被装饰对象的基本功能,如`InputStream`。 - **具体组件**(ConcreteComponent):实现组件接口的基础对象,如`FileInputStream`。 - **装饰器抽象类**(Decorator):继承或实现组件接口,并持有组件对象的引用。 - **具体装饰器**(ConcreteDecorator):添加扩展功能,如`BufferedInputStream`[^2]。 --- #### 2. 示例代码实现 以“手机功能扩展”为例: ```java // 1. 组件接口:定义基础功能 interface Phone { void call(); } // 2. 具体组件:基础实现 class BasicPhone implements Phone { @Override public void call() { System.out.println("拨打普通电话"); } } // 3. 装饰器抽象类 abstract class PhoneDecorator implements Phone { protected Phone decoratedPhone; public PhoneDecorator(Phone phone) { this.decoratedPhone = phone; } public void call() { decoratedPhone.call(); } } // 4. 具体装饰器:添加视频通话功能 class VideoCallDecorator extends PhoneDecorator { public VideoCallDecorator(Phone phone) { super(phone); } @Override public void call() { super.call(); addVideoCall(); } private void addVideoCall() { System.out.println("支持视频通话"); } } // 使用示例 public class Main { public static void main(String[] args) { Phone basicPhone = new BasicPhone(); Phone videoPhone = new VideoCallDecorator(basicPhone); videoPhone.call(); // 输出:拨打普通电话 → 支持视频通话 } } ``` --- #### 3. 典型应用场景 1. **Java IO流** `BufferedReader`通过包装`FileReader`添加缓冲功能: ```java Reader reader = new BufferedReader(new FileReader("test.txt")); // [^1] ``` 2. **动态扩展对象功能** 如日志记录、权限校验、数据加密等场景,无需修改原有类[^3]。 3. **避免子类爆炸** 当通过继承扩展功能会导致类数量指数增长时,装饰器模式更灵活[^2]。 --- #### 4. 模式优势与对比 | 对比维度 | 装饰器模式 | 继承 | |----------------|---------------------------|-------------------------| | 扩展方式 | 动态组合 | 静态编译期绑定 | | 类数量 | 按需添加装饰类 | 每扩展一次需新增子类 | | 功能叠加 | 支持多层嵌套(如A→B→C) | 单层继承 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值