java设计模式六、装饰器模式

什么是装饰器模式

装饰器模式是一种结构型设计模式,它允许在不改变对象原有结构的情况下,动态地给对象添加额外的功能。这种模式通过创建包装对象来实现功能的扩展,是继承的一个替代方案。

核心思想:在保持接口不变的前提下,对原有功能进行扩展和增强。

装饰器模式的结构

装饰器模式的优势

  1. 灵活性:比继承更灵活,可以在运行时动态添加功能
  2. 符合开闭原则:对扩展开放,对修改关闭
  3. 避免类爆炸:不需要为每个功能组合创建子类
  4. 职责分离:不同的装饰器负责不同的功能

缓冲输入流装饰器

java

复制代码
package com.YA33.design.decorator;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 缓冲输入流装饰器 - 为文件输入流添加缓冲功能
 * 作者:YA33
 */
public class BufferedInputStream extends InputStream {

    private final FileInputStream fileInputStream;

    // 缓冲区,存储从文件读取的数据
    private final byte[] buffer = new byte[8192]; // 8KB缓冲区

    // 当前读取位置指针
    private int position = -1;

    // 缓冲区中有效数据的容量
    private int capacity = -1;

    public BufferedInputStream(FileInputStream fileInputStream) {
        this.fileInputStream = fileInputStream;
    }

    @Override
    public int read() throws IOException {
        /**
         * 读取逻辑:
         * 1. 如果缓冲区有数据,直接从缓冲区读取
         * 2. 如果缓冲区为空,先刷新缓冲区
         * 3. 如果刷新后仍无数据,返回-1表示结束
         */
        if (bufferCanRead()) {
            return readFromBuffer();
        }
        refreshBuffer();
        if (!bufferCanRead()) {
            return -1;
        }
        return readFromBuffer();
    }

    /**
     * 从缓冲区读取一个字节
     */
    private int readFromBuffer() {
        // 与上0xff去掉符号位,将byte转为无符号int
        // 因为byte的范围是-128~127,而我们需要0~255的范围
        return buffer[position++] & 0xff;
    }

    /**
     * 刷新缓冲区,从文件流中读取新数据
     */
    private void refreshBuffer() throws IOException {
        capacity = this.fileInputStream.read(buffer);
        position = 0;
    }

    /**
     * 检查缓冲区是否可读
     */
    private boolean bufferCanRead() {
        // 容量为-1表示未初始化
        if (-1 == capacity) return false;
        // 位置等于容量表示已读完
        if (capacity == position) return false;
        return true;
    }

    @Override
    public void close() throws IOException {
        fileInputStream.close();
        super.close();
    }
}

历史记录集合装饰器

java

复制代码
package com.YA33.design.decorator;

import java.util.*;

/**
 * 历史记录集合装饰器 - 记录被删除的元素历史
 * YA
 */
public class HistorySet<E> implements Set<E> {

    // 记录被删除的元素
    private final List<E> removeHistory = new ArrayList<>();

    // 被装饰的原始集合
    private final Set<E> delegate;

    public HistorySet(Set<E> delegateSet) {
        this.delegate = delegateSet;
    }

    @Override
    public boolean remove(Object o) {
        /**
         * 增强的删除方法:
         * - 执行原始删除操作
         * - 如果删除成功,记录到删除历史中
         */
        if (delegate.remove(o)) {
            removeHistory.add((E) o);
            return true;
        }
        return false;
    }

    /**
     * 获取删除历史记录
     */
    public List<E> getRemoveHistory() {
        return new ArrayList<>(removeHistory);
    }

    /**
     * 清空删除历史
     */
    public void clearHistory() {
        removeHistory.clear();
    }

    // 以下方法直接委托给原始集合
    @Override
    public int size() {
        return delegate.size();
    }

    @Override
    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return delegate.contains(o);
    }

    @Override
    public Iterator<E> iterator() {
        return delegate.iterator();
    }

    @Override
    public Object[] toArray() {
        return delegate.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return delegate.toArray(a);
    }

    @Override
    public boolean add(E e) {
        return delegate.add(e);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return delegate.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return delegate.addAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return delegate.retainAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean modified = false;
        for (Object item : c) {
            modified |= remove(item);
        }
        return modified;
    }

    @Override
    public void clear() {
        // 清空时记录所有元素到删除历史
        removeHistory.addAll(delegate);
        delegate.clear();
    }

    @Override
    public String toString() {
        return "当前集合: " + delegate.toString() + 
               "\n删除历史: " + removeHistory;
    }
}

日志输出流装饰器

java

复制代码
package com.YA33.design.decorator;

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 日志输出流装饰器 - 为输出流添加日志记录功能
 * 作者:YA33
 */
public class LoggingOutputStream extends OutputStream {

    private final OutputStream delegate;
    private final SimpleDateFormat dateFormat = 
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public LoggingOutputStream(OutputStream delegate) {
        this.delegate = delegate;
    }

    @Override
    public void write(int b) throws IOException {
        log("写入单个字节: " + b);
        delegate.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        log("写入字节数组,长度: " + b.length);
        delegate.write(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        log(String.format("写入字节数组,偏移量: %d, 长度: %d", off, len));
        delegate.write(b, off, len);
    }

    @Override
    public void flush() throws IOException {
        log("刷新输出流");
        delegate.flush();
    }

    @Override
    public void close() throws IOException {
        log("关闭输出流");
        delegate.close();
    }

    /**
     * 记录日志信息
     */
    private void log(String message) {
        String timestamp = dateFormat.format(new Date());
        System.out.println("[" + timestamp + "] " + message);
    }
}

测试代码

java

复制代码
package com.YA33.design.decorator;

import java.io.*;
import java.time.Instant;
import java.util.*;

public class DecoratorPatternDemo {

    public static void main(String[] args) {
        System.out.println("=== 装饰器模式演示 ===\n");
        
        // 测试历史记录集合
        testHistorySet();
        
        // 测试缓冲输入流
        testBufferedInputStream();
        
        // 测试日志输出流
        testLoggingOutputStream();
    }

    /**
     * 测试历史记录集合装饰器
     */
    public static void testHistorySet() {
        System.out.println("1. 历史记录集合测试:");
        
        Set<String> historySet = new HistorySet<>(new HashSet<>());
        
        // 添加元素
        historySet.add("Apple");
        historySet.add("Banana");
        historySet.add("Orange");
        historySet.add("Grape");
        
        System.out.println("初始集合: " + historySet);
        
        // 删除元素
        historySet.remove("Banana");
        historySet.remove("Grape");
        
        System.out.println("删除后集合: " + historySet);
        
        // 获取删除历史
        HistorySet<String> typedSet = (HistorySet<String>) historySet;
        System.out.println("删除历史: " + typedSet.getRemoveHistory());
        System.out.println();
    }

    /**
     * 测试缓冲输入流性能
     */
    public static void testBufferedInputStream() {
        System.out.println("2. 缓冲输入流性能测试:");
        
        try {
            // 创建一个测试文件
            File testFile = createTestFile();
            
            // 测试普通文件流
            long startTime = Instant.now().toEpochMilli();
            readWithFileInputStream(testFile);
            long endTime = Instant.now().toEpochMilli();
            System.out.println("普通文件流耗时: " + (endTime - startTime) + "ms");
            
            // 测试缓冲文件流
            startTime = Instant.now().toEpochMilli();
            readWithBufferedInputStream(testFile);
            endTime = Instant.now().toEpochMilli();
            System.out.println("缓冲文件流耗时: " + (endTime - startTime) + "ms");
            
            // 清理测试文件
            testFile.delete();
            
        } catch (IOException e) {
            System.err.println("文件操作错误: " + e.getMessage());
        }
        System.out.println();
    }

    /**
     * 测试日志输出流
     */
    public static void testLoggingOutputStream() {
        System.out.println("3. 日志输出流测试:");
        
        try {
            ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
            LoggingOutputStream loggingOutput = new LoggingOutputStream(byteArrayOutput);
            
            loggingOutput.write("Hello, Decorator Pattern!".getBytes());
            loggingOutput.write(65); // 写入字符'A'
            loggingOutput.flush();
            loggingOutput.close();
            
            System.out.println("输出内容: " + byteArrayOutput.toString());
            
        } catch (IOException e) {
            System.err.println("输出流错误: " + e.getMessage());
        }
        System.out.println();
    }

    /**
     * 创建测试文件
     */
    private static File createTestFile() throws IOException {
        File file = new File("test_data.txt");
        try (FileOutputStream fos = new FileOutputStream(file)) {
            // 写入1MB测试数据
            byte[] data = new byte[1024]; // 1KB
            Arrays.fill(data, (byte) 'A');
            for (int i = 0; i < 1024; i++) { // 写入1024次,共1MB
                fos.write(data);
            }
        }
        return file;
    }

    private static void readWithFileInputStream(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            while (fis.read() != -1) {
                // 逐个字节读取
            }
        }
    }

    private static void readWithBufferedInputStream(File file) throws IOException {
        try (InputStream bis = new BufferedInputStream(new FileInputStream(file))) {
            while (bis.read() != -1) {
                // 通过缓冲流读取
            }
        }
    }
}

装饰器模式的应用场景

  1. IO流处理:Java中的BufferedInputStream、DataInputStream等
  2. 集合框架:Collections.synchronizedCollection()等工具方法
  3. Web开发:HTTP请求/响应的过滤器链
  4. GUI开发:为可视化组件添加滚动、边框等功能
  5. 中间件:为服务调用添加日志、监控、重试等功能

总结

装饰器模式通过组合的方式,在不修改原有代码的基础上为对象动态添加功能,体现了"组合优于继承"的设计原则。在实际开发中,合理使用装饰器模式可以让代码更加灵活、可扩展,同时保持良好的结构清晰度。

通过本文的三个示例,我们可以看到装饰器模式在不同场景下的应用:缓冲功能、历史记录功能和日志功能。每种装饰器都专注于单一职责,通过组合可以创造出功能丰富的对象,这正是装饰器模式的魅力所在。

相关推荐
回忆是昨天里的海6 小时前
k8s集群-节点间通信之安装kube-flannel插件
java·docker·kubernetes
信仰_2739932436 小时前
Mybatis-Spring重要组件介绍
java·spring·mybatis
盖世英雄酱581366 小时前
java深度调试【第二章通过堆栈分析性能瓶颈】
java·后端
没有bug.的程序员7 小时前
AOP 原理深剖:动态代理与 CGLIB 字节码增强
java·spring·aop·动态代理·cglib
2401_837088507 小时前
ResponseEntity - Spring框架的“标准回复模板“
java·前端·spring
lang201509287 小时前
Spring Boot RSocket:高性能异步通信实战
java·spring boot·后端
默默coding的程序猿7 小时前
1.北京三维天地公司-实施实习生
java·sql·技术支持·面经·实施·实施工程师·三维天地
天天摸鱼的java工程师8 小时前
解释 Spring 框架中 bean 的生命周期:一个八年 Java 开发的实战视角
java·后端
尤老师FPGA8 小时前
LVDS系列32:Xilinx 7系 ADC LVDS接口参考设计(三)
android·java·ui