装饰者模式

本文深入解析装饰者模式,探讨其在软件设计中的作用与应用,特别是在Java IO流中的实例,展示了如何通过装饰者模式增强对象功能,同时保持接口的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   概念

装饰者模式又名包装(Wrapper)模式。装饰者模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;装饰者模式动态地将责任附加到对象身上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  结构 

在装饰模式中的角色有:

  抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

    具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

    装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

    具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

抽象构件角色

public interface Component {
    
    public void sampleOperation();
    
}

具体构件角色 

public class ConcreteComponent implements Component {

    @Override
    public void sampleOperation() {
        // 写相关的业务代码
    }
}

装饰角色 

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

    @Override
    public void sampleOperation() {
        // 委派给构件
        component.sampleOperation();
    }   
}

 具体装饰角色

public class ConcreteDecoratorA extends Decorator {

    public ConcreteDecoratorA(Component component) {
        super(component);
    }
    
    @Override
    public void sampleOperation() {
      // 写相关的业务代码
   super.sampleOperation();
    }
}

实例 

抽象构件角色

public interface TheGreatestSage {
    
    public void move();
}

具体构件角色 

public class Monkey implements TheGreatestSage {

    @Override
    public void move() {
        //代码
        System.out.println("Monkey Move");
    }
}

 抽象装饰角色

public class Change implements TheGreatestSage {
    private TheGreatestSage sage;
    
    public Change(TheGreatestSage sage){
        this.sage = sage;
    }
    @Override
    public void move() {
        // 代码
        sage.move();
    }
}

具体装饰角色

public class Fish extends Change {
    
    public Fish(TheGreatestSage sage) {
        super(sage);
    }

    @Override
    public void move() {
        // 代码
        System.out.println("Fish Move");
    }
}

具体装饰角色 

public class Bird extends Change {
    
    public Bird(TheGreatestSage sage) {
        super(sage);
    }

    @Override
    public void move() {
        // 代码
        System.out.println("Bird Move");
    }
}

 实例测试

public class Test {

    public static void main(String[] args) {
        TheGreatestSage sage = new Monkey();
        // 第一种写法  单层装饰
        TheGreatestSage bird = new Bird(sage);
        TheGreatestSage fish = new Fish(bird);
        // 第二种写法 双层装饰
        TheGreatestSage fish1 = new Fish(new Bird(sage));
        fish.move(); 
        fish1.move();
    }
}

 

透明性的要求

装饰者模式对客户端的透明性要求程序不要声明一个ConcreteComponent类型的变量,而应当声明一个Component类型的变量。用孙悟空的例子来说,必须永远把孙悟空的所有变化都当成孙悟空来对待,而如果把老孙变成的鱼儿当成鱼儿,而不是老孙,那就被老孙骗了,而这时不应当发生的。下面的做法是对的:

TheGreatestSage sage = new Monkey();
TheGreatestSage bird = new Bird(sage);

而下面的做法是不对的:

Monkey sage = new Monkey();
Bird bird = new Bird(sage);

半透明的装饰者模式

然而,纯粹的装饰者模式很难找到。装饰者模式的用意是在不改变接口的前提下,增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。即便是在孙大圣的系统里,也需要新的方法。比如齐天大圣类并没有飞行的能力,而鸟儿有。这就意味着鸟儿应当有一个新的fly()方法。再比如,齐天大圣类并没有游泳的能力,而鱼儿有,这就意味着在鱼儿类里应当有一个新的swim()方法。

这就导致了大多数的装饰者模式的实现都是“半透明”的,而不是完全透明的。换言之,允许装饰者模式改变接口,增加新的方法。这意味着客户端可以声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法:

TheGreatestSage sage = new Monkey();
Bird bird = new Bird(sage);
bird.fly();

半透明的装饰者模式是介于装饰者模式和适配器模式之间的。适配器模式的用意是改变所考虑的类的接口,也可以通过改写一个或几个方法,或增加新的方法来增强或改变所考虑的类的功能。大多数的装饰者模式实际上是半透明的装饰者模式,这样的装饰者模式也称做半装饰、半适配器模式。

装饰者模式在Java IO流中的应用

我们看下InputStream的对象结构图:

 

根据上图可以看出:

 抽象构件(Component)角色:由InputStream扮演。这是一个抽象类,为各种子类型提供统一的接口。

          具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。  

              抽象装饰(Decorator)角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。

           具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。

InputStream类型中的装饰者模式

InputStream类型中的装饰者模式是半透明的。为了说明这一点,不妨看一看作装饰者模式的抽象构件角色的InputStream的源代码。这个抽象类声明了九个方法,并给出了其中八个的实现,另外一个是抽象方法,需要由子类实现。

public abstract class InputStream implements Closeable {

    public abstract int read() throws IOException;

 
    public int read(byte b[]) throws IOException {}

    public int read(byte b[], int off, int len) throws IOException {}

    public long skip(long n) throws IOException {}

    public int available() throws IOException {}
    
    public void close() throws IOException {}
    
    public synchronized void mark(int readlimit) {}
    
    public synchronized void reset() throws IOException {}

    public boolean markSupported() {}
}

下面是作为装饰模式的抽象装饰角色FilterInputStream类的源代码。可以看出,FilterInputStream的接口与InputStream的接口是完全一致的。也就是说,直到这一步,还是与装饰模式相符合的。

public class FilterInputStream extends InputStream {
    protected FilterInputStream(InputStream in) {}
    
    public int read() throws IOException {}

    public int read(byte b[]) throws IOException {}
    
    public int read(byte b[], int off, int len) throws IOException {}

    public long skip(long n) throws IOException {}

    public int available() throws IOException {}

    public void close() throws IOException {}

    public synchronized void mark(int readlimit) {}

    public synchronized void reset() throws IOException {}

    public boolean markSupported() {}
}

下面是具体装饰角色PushbackInputStream的源代码。

public class PushbackInputStream extends FilterInputStream {
    private void ensureOpen() throws IOException {}
    
    public PushbackInputStream(InputStream in, int size) {}

    public PushbackInputStream(InputStream in) {}

    public int read() throws IOException {}

    public int read(byte[] b, int off, int len) throws IOException {}

    public void unread(int b) throws IOException {}

    public void unread(byte[] b, int off, int len) throws IOException {}

    public void unread(byte[] b) throws IOException {}

    public int available() throws IOException {}

    public long skip(long n) throws IOException {}

    public boolean markSupported() {}

    public synchronized void mark(int readlimit) {}
 
    public synchronized void reset() throws IOException {}

    public synchronized void close() throws IOException {}
}

查看源码,你会发现,这个装饰类提供了额外的方法unread(),这就意味着PushbackInputStream是一个半透明的装饰类。换言 之,它破坏了理想的装饰者模式的要求。如果客户端持有一个类型为InputStream对象的引用in的话,那么如果in的真实类型是 PushbackInputStream的话,只要客户端不需要使用unread()方法,那么客户端一般没有问题。但是如果客户端必须使用这个方法,就 必须进行向下类型转换。将in的类型转换成为PushbackInputStream之后才可能调用这个方法。但是,这个类型转换意味着客户端必须知道它 拿到的引用是指向一个类型为PushbackInputStream的对象。这就破坏了使用装饰者模式的原始用意。

下面是使用I/O流读取文件内容的简单操作示例。

public class IOTest {

    public static void main(String[] args) throws IOException {
        // 流式读取文件
        DataInputStream dis = null;
        try{
            dis = new DataInputStream(
                    new BufferedInputStream(
                            new FileInputStream("test.txt")
                    )
            );
            //读取文件内容
            byte[] bs = new byte[dis.available()];
            dis.read(bs);
            String content = new String(bs);
            System.out.println(content);
        }finally{
            dis.close();
        }
    }
}

观察上面的代码,会发现最里层是一个FileInputStream对象,然后把它传递给一个BufferedInputStream对象,经过BufferedInputStream处理,再把处理后的对象传递给了DataInputStream对象进行处理,这个过程其实就是装饰器的组装过程,FileInputStream对象相当于原始的被装饰的对象,而BufferedInputStream对象和DataInputStream对象则相当于装饰器。

 

 

 

 

 

 

 

内容概要:本文针对国内加密货币市场预测研究较少的现状,采用BP神经网络构建了CCi30指数预测模型。研究选取2018年3月1日至2019年3月26日共391天的数据作为样本,通过“试凑法”确定最优隐结点数目,建立三层BP神经网络模型对CCi30指数收盘价进行预测。论文详细介绍了数据预处理、模型构建、训练及评估过程,包括数据归一化、特征工程、模型架构设计(如输入层、隐藏层、输出层)、模型编译与训练、模型评估(如RMSE、MAE计算)以及结果可视化。研究表明,该模型在短期内能较准确地预测指数变化趋势。此外,文章还讨论了隐层节点数的优化方法及其对预测性能的影响,并提出了若干改进建议,如引入更多技术指标、优化模型架构、尝试其他时序模型等。 适合人群:对加密货币市场预测感兴趣的研究人员、投资者及具备一定编程基础的数据分析师。 使用场景及目标:①为加密货币市场投资者提供一种新的预测工具和方法;②帮助研究人员理解BP神经网络在时间序列预测中的应用;③为后续研究提供改进方向,如数据增强、模型优化、特征工程等。 其他说明:尽管该模型在短期内表现出良好的预测性能,但仍存在一定局限性,如样本量较小、未考虑外部因素影响等。因此,在实际应用中需谨慎对待模型预测结果,并结合其他分析工具共同决策。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值