java I/O系统(2)-装饰器模式

引言

IO系统是使用了装饰器模式的典型。所以对装饰器模式的深入研究对IO系统的理解肯定大有裨益。在本文中会详细介绍装饰器模式,会用以demo展示,同时会举出例子在IO系统中是如何呈现了这种模式,最后,我们探讨一下装饰器模式与代理模式之间的异同。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

装饰器模式

装饰器(Decorator )模式是指允许向一个现有的对象添加新的功能,同时又不改变其结构。一定意义上来说是继承的替代品。
比如说一幅画,我们给它上了一个边框。那么这个时候画还是那副画,但是它多了个边框,这个边框就是装饰器给它赋予的。但是如果你要通过继承来实现的话,就相当于是在画上,在画的周围给画上了边框,这个时候的改变就变成了静态的了,不够灵活的增加和撤销功能。
而且,还有一个极具重要的作用,比如上面说的这幅画,画和画框是相互耦合的,他们两者可以独立发展,画可以画的更精妙,边框也可以改的更好看,但是对他们组合的效果不会影响。

解决的问题

通过继承来扩展类的功能,逐渐冗余会产生很多的子类,或者保证原本的类的功能不变,需要额外的增加类的功能。

组织架构

下面这张图就是装饰器模式的设计方式:
这里写图片描述
我们解释一下每个类是干什么的:
1、component:是存在的一个抽象。可以是abstract类也可以是一个interface。也可以称为抽象被装饰类

2、concreteComponent:真实被装饰类,也是针对与component的具体实现。

3、Decorator:是装饰的一个抽象,可以称为抽象装饰类用abstract描述,因为它要包含一个真实对象的引用,无法用interface表示,而且必须要有和component一样的待实现方法。可以称为抽象装饰类

4、concreteDecorator:真实装饰类,具体的呈现对真实被装饰类的功能进行强化。

有的小伙伴就很疑惑了。
为什么真实被装饰类和抽象装饰类都要实现component接口呢?
因为装饰器只是进行装饰,东西还是原来那个东西。变形金刚可以变成人,但是改变不了他是汽车的根本,本质上来说它还是一辆车,只是多了类人的功能而已。
再者,我们需要对一个需要装饰的对象进行多次装饰。比如说一只猫是一半红色一半白色,那么我们就需要用白色装饰器装饰完接着用红色装饰器装饰。如果不实现这个接口,那么就无法嵌套装饰。

为什么装饰器还需要进行抽象呢?跳过Decorator类,直接由concreteDecorator来实现Component不就好了?
的确,如果是简单的装饰器模式来说,跳过Decorator也未尝不可,也不会影响既有功能。但是这样会违背面对对象编程的概念,如果新增的功能非常多,会产生很多相同的代码。

代码实现

存在对动物的描述,我们要给它赋予它的颜色。

animal类:描述一种动物

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:54 
 * 关于类Animal.java的描述:抽象被装饰类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public interface Animal {

    //描述一种动物
    public void name();
}

Cat类:描述具体的动物

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:07 
 * 关于类Cat.java的描述:真实被装饰类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Cat implements Animal{

    @Override
    public void name() {
        System.out.println("猫");
    }

}

Decorator类,抽象装饰器类:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:42 
 * 关于类Decorator.java的描述:抽象装饰类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public abstract class Decorator implements Animal{

    //一个真实对象的引用
    private Animal animal;

    //构造方法
    public Decorator(Animal animal) {
        this.animal = animal;
    }


    //把请求转发给被代理对象
    public void name(){
        animal.name();
    }
}

RedDecorator类,红色动物装饰器:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:09:17 
 * 关于类RedDecorator.java的描述:具体装饰类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class RedDecorator extends Decorator{

    //构造函数
    public RedDecorator(Animal animal) {
        super(animal);
    }


    //重写name方法,注入新的功能
    @Override
    public void name() {
        super.name();
        red();
    }


    //新增对动物颜色的描述
    private void red(){
        System.out.println("红色");
    }
}

WhiteDecorator类,白色动物装饰器:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:10:24 
 * 关于类WhiteDecorator.java的描述:具体装饰类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class WhiteDecorator extends Decorator{
    //构造函数
    public WhiteDecorator(Animal animal) {
        super(animal);
    }


    //重写name方法,注入新的功能
    @Override
    public void name() {
        super.name();
        white();
    }


    //新增对动物颜色的描述
    private void white(){
        System.out.println("白色");
    }
}

以上就是一个完整的装饰器模式,接下来我们对它进行测试:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:50 
 * 关于类DecoratorTest.java的描述:装饰器类测试
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class DecoratorTest {


    public static void main(String[] args) {
        //定义猫
        Animal animal = new Cat();
        animal.name();
        System.out.println("==============================");

        //红色的猫
        RedDecorator redDecorator = new RedDecorator(animal);
        redDecorator.name();
        System.out.println("==============================");
        //白色的猫
        WhiteDecorator whiteDecorator = new WhiteDecorator(animal);
        whiteDecorator.name();
        System.out.println("==============================");

        //一直白色的带红色的猫
        RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));
        redDecorator2.name();
        System.out.println("==============================");
    }
}
//运行结果:
//猫
//==============================
//猫
//红色
//==============================
//猫
//白色
//==============================
//猫
//白色
//红色
//==============================
//
//

如果要说装饰器模式复杂,那么复杂的一定是它一层层的嵌套。比如例子中的“一只白色带红色的猫”。但是你有没有想过我为什么描述成这样,而不描述成“一只红色带白的的猫”呢?在装饰器模式中,我们已最后一层来代表真实装饰的对象。比如说:

    RedDecorator redDecorator = new RedDecorator(animal);

那么我们装饰的就是这只猫。但是如果这样:

RedDecorator redDecorator2 = new RedDecorator(new WhiteDecorator(animal));

那么我们装饰的其实是一只白色的猫。所以我上面的描述就是“一只白色带红色的猫”。

在IO系统中装饰器模式体现

我们以InputStream为例来说明装饰器模式,我在网上找了一些比较好的图:
InputStream
这里写图片描述
从图中,我们进行分析,或者自己去查看代码可以发现InputStream是祖宗,它就相当于我们前面说的component类,它是抽象的被装饰类。

在直接实现InputStream的子类中,我们发现FilterInputStream就是一个装饰器类,同时它有4个具体的装饰器子类。

那么按照我们前面的逻辑,我们要定义一个从File文件读入生产一个有行号标记的流要怎么写呢?从上面的图可以知道,我们其实需要对FileInputStream用LineNumberInputStream进行装饰,代码如下:

package com.brickworkers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.LineNumberInputStream;

/**
 * 
 * @author Brickworker
 * Date:2017年5月12日下午4:11:50 
 * 关于类DecoratorTest.java的描述:装饰器类测试
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class DecoratorTest {


    public static void main(String[] args) throws FileNotFoundException {

        //对FileInputStream用LineNumberInputStream进行装饰
        LineNumberInputStream inputStream = new LineNumberInputStream(new FileInputStream(new File("C:/user/brickworker/test.txt")));
    }
}

与代理模式的区别

装饰器模式与代理模式实现非常相似,都使用了组合的方式,而且装饰类与被装饰类都实现了同一个接口,代理类与被代理类也都实现了同一个接口。个人感觉而言总结了几点区别:
①功能不同,装饰器模式是为了更灵活的丰富对象功能;而代理模式是限制了对象访问。
②装饰器模式可以装饰多个对象,对象变动是灵活的。代理模式中代理类与被代理类是固定的,甚至是写死在代理类内部。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值