阅读 372

DesignPattern - 装饰器模式【结构型】

欢迎关注微信公众号:FSA全栈行动 👋

一、装饰器模式介绍

装饰器模式(Decorator Pattern)也叫包装设计模式,属于结构型模式,它是作为现在的类的一个包装,允许向一个现有的对象添加新的功能,同时又不改变其结构。

给对象增加功能,一般两种方式 继承 和 关联组合 。装饰器模式使用的是关联组合,将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用对象的行为来增强功能,比使用继承更加灵活。

  • 核心组成

    • 抽象组件(Component):定义组件的方法
    • 具体组件(ConcreteComponent):Component 的具体实现,是要装饰的具体对象,即被装饰者
    • 抽象装饰器(Decorator):定义具体装饰者的行为规范,和 Component 角色有相同的接口,持有组件(Component) 对象的实例引用
    • 具体装饰器(ConcreteDecorator):负责给 Component 对象装饰具体的附加功能

    注意:装饰者和被装饰者有相同的超类(Component)

  • 应用场景

    • 以动态、透明的方式给单个对象添加职责,但又能不改变其结构
    • JDK 源码里应用的最多的就是 IO 流,大量使用装饰设计模式(InputStream、BufferedInputStream...)
  • 优点

    • 装饰器模式与继承关系的目的都是扩展对象的功能,但装饰器模式可以提供比继承更多的灵活性
    • 使用不同的具体装饰器以及这些装饰类的排列组合,可以创造出很多不同行为的组合,原有代码无须改变,符合”开闭原则“
  • 缺点

    • 装饰器模式增加了许多子类,如果过度使用会使程序变得很复杂(多层包装)
    • 增加系统的复杂度,加大学习与理解的难度

二、装饰器模式代码实现

创建抽象组件类:

/**
 * 抽象组件:枪
 *
 * @author GitLqr
 */
public abstract class Gun {
	String name;

	public Gun(String name) {
		super();
		this.name = name;
	}

	/**
	 * 攻击力
	 */
	public abstract float attack();

	/**
	 * 后坐力
	 */
	public abstract float recoil();

	@Override
	public String toString() {
		return name + "的攻击力为: " + attack() + " , 后坐力为:" + recoil();
	}
}
复制代码

创建具体组件类:

/**
 * 具体组件:UMP9
 *
 * @author GitLqr
 */
public class Ump9Gun extends Gun {

	public Ump9Gun() {
		super("UMP9");
	}

	@Override
	public float attack() {
		return 100;
	}

	@Override
	public float recoil() {
		return 20;
	}
}
复制代码

创建抽象装饰器类:

/**
 * 抽象装饰器:配件
 *
 * @author GitLqr
 */
public abstract class Part extends Gun {

	// 持有组件引用(注意:这个组件可能是已经被装饰过的)
	Gun gun;

	public Part(String name, Gun gun) {
		super(name);
		this.gun = gun;
	}

	@Override
	public String toString() {
		return "装配【" + name + "】,攻击力为: " + attack() + " , 后坐力为:" + recoil();
	}
}
复制代码

创建具体装饰器类:

/**
 * 具体装饰器:消声器
 *
 * @author GitLqr
 */
public class MufflerPart extends Part {

	public MufflerPart(Gun gun) {
		super("消声器", gun);
	}

	@Override
	public float attack() {
		return gun.attack() - 5;
	}

	@Override
	public float recoil() {
		return gun.recoil() + 5;
	}
}
/**
 * 具体装饰器:火焰子弹
 *
 * @author GitLqr
 */
public class FireBulletPart extends Part {

	public FireBulletPart(Gun gun) {
		super("火焰子弹", gun);
	}

	@Override
	public float attack() {
		return gun.attack() + 30;
	}

	@Override
	public float recoil() {
		return gun.recoil() + 5;
	}
}
复制代码

使用:

public static void main(String[] args) {
    Gun gun = new Ump9Gun();
    System.out.println(gun); // UMP9的攻击力为: 100.0 , 后坐力为:20.0

    gun = new MufflerPart(gun);
    System.out.println(gun); // 装配【消声器】,攻击力为: 95.0 , 后坐力为:25.0

    gun = new FireBulletPart(gun);
    System.out.println(gun); // 装配【火焰子弹】,攻击力为: 125.0 , 后坐力为:30.0

    // Gun gun = new FireBulletPart(new MufflerPart(new Ump9Gun()));
}
复制代码

三、装饰器模式在 JDK 源码 IO 流中的应用

  • 抽象组件:InputStream
  • 具体组件:FileInputStream、ByteArrayInputStream
  • 抽象装饰器:FilterInputStream
  • 具体装饰器:BufferedInputStream、DataInputStream

抽象组件 InputStream 定义了 read() 方法:

public abstract class InputStream implements Closeable {
    ...
    public abstract int read() throws IOException;
}
复制代码

具体组件 FileInputStream 实现了 read() 方法:

public class FileInputStream extends InputStream {
    ...
    public int read() throws IOException {
        return read0();
    }

    private native int read0() throws IOException;
}
复制代码

抽象装饰器 FilterInputStream 继承自抽象组件 InputStream,持有组件对象实例引用:

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

具体装饰器 BufferedInputStream 对 read() 进行增强,增加缓存功能:

public class BufferedInputStream extends FilterInputStream {
    protected volatile byte buf[];
    ...
    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }
    ...
}
复制代码

使用:

// 添加了Buffer缓冲功能
InputStream inputStream = new BufferedInputStream(new FileInputStream(""));
复制代码

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有Android技术, 还有iOS, Python等文章, 可能有你想要了解的技能知识点哦~

文章分类
代码人生
文章标签