欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 重修设计模式-结构型-装饰器模式

重修设计模式-结构型-装饰器模式

2024/12/1 0:38:38 来源:https://blog.csdn.net/weixin_39397471/article/details/142218121  浏览:    关键词:重修设计模式-结构型-装饰器模式

重修设计模式-结构型-装饰器模式

在不修改原有类代码的情况下,通过创建包装类(即装饰器)给对象添加一些额外的功能。

装饰器模式(Decorator Pattern)允许在不修改原有类代码的情况下,通过创建一系列包装类来给对象动态地添加一些额外的功能,且增强的功能支持相互叠加。装饰器模式将“组合优于继承”的思想体现的淋漓尽致,当原始类需要增加额外功能时,相比直接生成子类的实现方式,这种模式更为灵活。

举个例子,日常大家都穿着衣服,衣服起着基础的蔽体的功能,首先将衣服和T恤用代码表示出来:

interface Clothe {fun feature()  //衣服的基础功能是蔽体
}open class TShirt : Clothe {open override fun feature() {println("T恤轻薄透气,适合夏天")}
}

但天气是多变的,下雨时需要防水,出太阳了需要防晒,这时需要为衣服增加防水、防晒功能,首先能想到的是用继承的方式去实现:

class WaterproofTShirt(): TShirt() {override fun feature() {super.feature()println("增加防水功能")}
}class SunscreenTShirt(): TShirt() {override fun feature() {super.feature()println("增加防晒功能")}
}class SunscreenWaterproofTShirt(): TShirt() {override fun feature() {super.feature()println("既能防水,又能防晒")}
}//调用时:
val c1 = WaterproofTShirt()  //下雨了,穿防水T恤
c1.feature()
val c2 = SunscreenTShirt()  //出太阳了,穿防晒T恤
c2.feature()

一下就为T恤扩展出三个子类,用于不同的天气。如果需求的扩展到此为止,这样设计是可以接受的,毕竟继承结构还算简单。但如果需求继续增加:下雪了需要防雪,这时不仅需要为T恤增加防雪功能,已有的功能同样也要增加防雪功能,毕竟有雨夹雪的场景,这时需要再扩张出4个子类了:

class SnowTShirt(): TShirt()  //防雪
class SnowWaterproofTShirt(): TShirt()  //防水、防雪
class SnowSunscreenTShirt(): TShirt()   //防晒、防雪
class SnowSunscreenWaterproofTShirt(): TShirt() //防晒、防水、防雪

如果再增加防风功能呢?常穿的衣服不仅有T恤还有毛衣,如果毛衣同样需要支持这些功能呢?

可以看到,用继承的方式去进行功能增强,随着需求不断发展,最终会造成子类爆炸增长的局面。其实映射到真实世界,也并不存在防晒、防风又防水的T恤,大家只会在T恤或毛衣外再套上防晒衣、风衣或雨衣来应对不同天气,这和装饰器模式非常类似。

继续这个例子,下面用装饰器模式去实现:

class Waterproof(var clothes: Clothe): Clothe {override fun feature() {println("套个雨衣,可以防水")clothes.feature()}
}class Sunscreen(var clothes: Clothe) : Clothe {override fun feature() {println("套个防晒衣,可以防晒")clothes.feature()}
}//调用时:
val clothe = TShirt()
val wClothe = Waterproof(clothe)    //下雨了,套上雨衣
val sClothe = Sunscreen(wClothe)    //出太阳了,套上防晒衣
sClothe.feature()

其实就是用了组合的思想,在功能扩展时,只需增加对应的功能类,并包裹真实对象,使用起来也非常灵活。比如在增加防雪功能时,只需增加防雪的装饰类即可:

class SnowClothe(var clothes: Clothe) : Clothe {override fun feature() {println("套个防雪衣,可以防雪?")clothes.feature()}
}//使用时:
val sClothe = SnowClothe(TShirt())
sClothe.feature()

在 Java 中,输入输出流( InputStreamOutputStream)就是通过装饰器模式进行扩展。例如,BufferedInputStreamBufferedOutputStream 就是对 InputStreamOutputStream 的装饰,它们通过添加缓冲区来提高读写效率;DataInputStreamDataOutputStream 支持按照基本数据类型(int、boolean、long 等)来读取数据。虽然 Java IO 的 API 比较杂乱,但只要理解了装饰器模式的思想,相信会很快掌握。

值得注意的一点是,装饰器类中,功能增强可能只涉及共同父类的部分方法重写,但还是需要将所有父类方法都实现一遍,并调用传入对象的对应方法。因为传入对象不一定是原始对象了,可能是包装了其他功能的装饰类对象,不能破坏它们的方法调用结构。举个例子,为上面 Clothe 接口新增个 color 方法:

interface Clothe {fun feature()fun color() {	//Kotlin中接口可以有默认实现,高版本Java也支持了println("衣服颜色")  }
}//装饰类-防雨
class Waterproof(var clothes: Clothe): Clothe {override fun feature() {println("套个雨衣,可以防水")clothes.feature()}override fun color() {println("增加一层透明颜色...")clothes.color()}
}//装饰类-防晒
class Sunscreen(var clothes: Clothe) : Clothe {override fun feature() {println("套个防晒衣,可以防晒")clothes.feature()}override fun color() {clothes.color() //为什么不能用super.color()呢?}
}

结合上面说明,理解一下为什么一定要实现 color 方法,并调用 clothes.color(),而非不实现或调用 super.color()

Java 中 DataInputStreamBufferedInputStream 也存在同样的问题,所以为了避免代码重复,Java IO 又抽象出了一个装饰器父类 FilterInputStream,在其内部实现了所有方法并做委托操作。这样,装饰器类只需要实现它需要增强的方法就可以了,其他方法由装饰器父类默认委托给传入的 InputStream 对象,FilterInputStream 源码如下:

public class FilterInputStream extends InputStream {protected volatile InputStream in;protected FilterInputStream(InputStream in) {this.in = in;}public int read() throws IOException {return in.read();}public int read(byte b[]) throws IOException {return read(b, 0, b.length);}public int read(byte b[], int off, int len) throws IOException {return in.read(b, off, len);}public long skip(long n) throws IOException {return in.skip(n);}public int available() throws IOException {return in.available();}public void close() throws IOException {in.close();}public synchronized void mark(int readlimit) {in.mark(readlimit);}public synchronized void reset() throws IOException {in.reset();}public boolean markSupported() {return in.markSupported();}
}

装饰器模式优点是扩展性好,灵活性高,符合开闭原则;缺点是如果装饰器过多,可能造成代码阅读性变差,比如 Java IO 流相关API。

装饰器模式有两个特点:

  1. 装饰器类包裹了所要装饰的对象实例,以便在原有功能上增加新的功能。
  2. 装饰器类和原始类都继承同样的父类,这样可以嵌套的增加其他装饰器。

装饰器模式和代理模式对比:

装饰器模式和静态代理实现非常相似,区别主要有以下几点:

  • 代理模式中,代理类附加的是跟原始类无关的功能,而在装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
  • 代理模式目标对象往往不直接对外提供服务,而是由代理类全权代理;装饰器模式目标对象仍然可以自行对外提供服务,装饰器只起增强和辅助作用。
  • 代理模式希望对原始类对象有访问控制,隐藏对象的内部细节;装饰器模式用的就是原始类对象的功能,对额外的装饰器功能选择性添加。

总结

装饰器模式主要的作用是给原始类添加增强功能,解决通过继承方式增加功能时导致的类爆炸问题,通过组合来代替继承。此外,装饰器模式还需要有共同父类,方便对原始类嵌套的使用多个装饰器。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com