装饰者模式

寵の児 提交于 2019-11-29 16:03:17

装饰者模式

  装饰者模式是23种设计模式之一,是指在不改变原来的类和使用继承的方式,动态的扩展这个类的功能。装饰者允许向一个现有的对象添加特定的功能却不改变它的结构。通过一个类来包装原有的类来提供额外的功能。

 

特点

 

(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。

 

(2) 装饰对象包含一个真实对象的引用(reference)

 

(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。

 

(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

 

装饰者模式的结构:

 

 

Component:抽象组件,被装饰的原始对象,可以是抽象类或者接口

ConcreatCompoent:实现或者继承了抽象组件,被装饰的具体的实现

Decorator:抽象的装饰者,实现或者继承了Component抽象组件,持有抽象组件的引用

ConcreatDecotarorA、ConcreatDecotarorB:具体的装饰者,实现或者继承了抽象装饰者。

 实现

  举个简单的例子,假设有一个科沃斯机器人,它的基本功能是扫地,现在要求这个机器人可以边扫地边唱歌,还可以边扫地边跳舞。

那么,就可以将机器定义为一个抽象的类,里面有一个抽象的方法,sweep();

/**
 * 抽象组件
 */
public abstract class Robot {
    /**
     * 打扫的方法
     */
    public abstract void sweep();
}

被装饰的具体对象:

/**
 * 抽象组件的具体实现类,即被装饰者对象
 */
public class ConcreatRobot extends Robot {
    @Override
    public void sweep() {
        System.out.println("打扫卧室");
    }
}

抽象装饰者:

/**
 * 抽象的装饰者类
 */
public class DecoratorRobot extends Robot {

    private Robot robot;

    public DecoratorRobot(Robot robot) {
        this.robot = robot;
    }

    @Override
    public void sweep() {
        robot.sweep();
    }
}

具体的装饰者A:

/**
 * 装饰者A
 */
public class ConcreatDecoratorA extends DecoratorRobot {

    public ConcreatDecoratorA(Robot robot) {
        super(robot);
    }

    /**
     * 一遍扫地一边唱歌
     */
    @Override
    public void sweep() {
        super.sweep();
        this.sign();
    }

    /**
     * 唱歌
     */
    public void sign(){
        System.out.println("唱一首单身情歌");
    }
}

具体的装饰者B:

/**
 * 装饰者B
 */
public class ConcreatDecoratorB extends RobotDecorator {

    public ConcreatDecoratorB(Robot robot) {
        super(robot);

    }

    /**
     * 一遍扫地一边跳舞
     */
    @Override
    public void sweep() {
        super.sweep();
        dance();
    }

    /**
     * 唱歌
     */
    public void dance(){
        System.out.println("跳一支探戈");
    }
}

客户端调用:

public class DecoratorTest  {
    public static void main(String[] args) {

        Robot robot = new ConcreatRobot();
        RobotDecorator signRobot = new ConcreatDecoratorA(robot);
        signRobot.sweep();
        RobotDecorator danceRobot = new ConcreatDecoratorB(robot);
        danceRobot.sweep();

    }
}

控制台输出:

打扫卧室
唱一首单身情歌
打扫卧室
跳一支探戈

  从例子中可以看出,装饰的模式是一种组合的概念,所谓装饰,就是要去扩展被装饰者从而扩展功能,而我们客户端在调用的时候,可以选择合适的装饰类来达到我们想要的结果。比如我只想让机器人扫地,那么我只需要调用具体的抽象实现类,我想让机器人又扫地又跳舞,那么就使用包装类。

 

  装饰者模式是为已有功能动态的添加更多功能的一种方式。那么,在什么时候使用呢,当系统需要新的功能的时候,向旧代码中添加新的代码显然增加了原有代码的复杂度,并且违反了开闭原则。这个时候,装饰者提供了一个非常好的机会,我们可以通过包装这个主类,把每个要包装的功能单独的写成一个包装类,从而扩展该类的功能。客户端可以根据需要,调用特定功能的包装类。这样做的好处是把类中的装饰功能或者和核心功能分开,简化原有的类。

装饰者模式在java IO中的应用

  装饰者模式在JDK中的典型应用莫过于IO体系了。Java中的IO分为两种,字节流和字符流,其中每一种分别可以分为输入流和输出流,我们来看下IO体系的结构图。

 

  我们以InputStream为例,InputStream是抽象的父类,FilterInputtream和ByteArrayInputStream继承了InputStream,这些类继承了InputStream的基本的字节读取功能,FilterInputtream还持有了InputStream的引用,它的所有的方法,都是调用了这个引用的同名方法,也就是说,他把所有的调用都委托给了InputStream。并且FilterInputtream还有一些子类,比如BufferdInputStream,DataInputStream等。BufferedInputStream提供了缓冲输入流,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。由此可见,BufferedInputStream为我们提供了一个从缓冲区读取数据的功能,就像是科沃斯机器人,经过包装后为我们提供了唱歌的功能一样。我们来看下这几个类的类图。

                                                                                

  正如图中所示,他们的结构完全满足装饰者模式的结构图。同理,像DataInputStream也是一个包装类,他提供了与机器无关方式从底层输入流中读取的功能。

我们来看下以BufferdInputStream实现数据读取的例子。

public class DecoratorTest {
    public static void main(String[] args) {

        InputStream in = null;
        BufferedInputStream bi = null;
        try {
            in = new FileInputStream("E:\\decorator.txt");
            //in 是一个文件输入流,通过BufferedInputStream给它添加从缓冲区读取数据的功能。
            //BufferedInputStream就是一个包装类
            bi = new BufferedInputStream(in);
            int count = 0;
            byte[] buffer = new byte[1024];
            while ((count = bi.read(buffer)) != -1) {
                System.out.println(new String(buffer, 0, count));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bi != null) {
                    bi.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
}

   我自己之前学习IO流的时候,看到这么多种类的流也是很难理解,当我以设计模式角度去解读这些流时,会更加容易理解。装饰者模式的实现对于使用者是透明的,我们必须了解这个装饰者是什么功能,才能恰当的使用这个类。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!