装饰模式
动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式现必生成子类更加灵活。–GOF 设计模式
该设计模式是让子类更加灵活,给某个对象而不是类添加功能。
坏代码
例如当前有一个流操作(Stream),分别有文件流(FileStream)、内存流(NetStream),而我们分别要对这些流进行编码(Ascii7)和压缩(Compressing) 或者还要编码和压缩一起搞,如果我们按照正常思维来写的话,将会产生很多很多的类,UML如下:

如果按照这种思维,以后再给流继续添加功能,有着更多功能的组合,那么就会产生更多的类,并随着日后代码的扩展,子类会不断的膨胀。这些类除了部分功能不同外,基本代码都极其相似。
interface Stream {
fun read(path: String)
}
open class FileStream : Stream {
override fun read(path: String) {
println("FileStream.read $path")
}
}
open class MemoryStream : Stream {
override fun read(path: String) {
println("MemoryStream.read $path")
}
}
class Ascii7FileStream : FileStream() {
override fun read(path: String) {
println("编码")
super.read(path)
}
}
class Ascii7MemoryStream : MemoryStream() {
override fun read(path: String) {
println("编码")
super.read(path)
}
}
class CompressingFileStream : FileStream() {
override fun read(path: String) {
println("压缩")
super.read(path)
}
}
class CompressingMemoryStream : MemoryStream() {
override fun read(path: String) {
println("压缩")
super.read(path)
}
}
class CompressingAscii7FileStream : FileStream() {
override fun read(path: String) {
println("压缩")
println("编码")
super.read(path)
}
}
class CompressingAscii7MemoryStream : MemoryStream() {
override fun read(path: String) {
println("压缩")
println("编码")
super.read(path)
}
}划分职责
此时,可以看到类Ascii7FileStream 和 Ascii7MemoryStream 的 read 操作中编码操作是相同的,唯一的不同仅仅是流的读取,一个是从文件读取,一个是从内存读取。产生了代码的冗余,不断的重复。而针对不同点 20 行和 27 行也不是完全的不同。
稍微进行一下代码的修改,将继承改为组合。
class Ascii7FileStream {
private lateinit var stream: FileStream
override fun read(path: String) {
println("编码")
stream.read(path)
}
}
class Ascii7MemoryStream {
private lateinit var stream: MemoryStream
override fun read(path: String) {
println("编码")
stream.read(path)
}
}经过修改的代码,发现针对 read 方法的不同点,此刻也成为了相同点。我们并没有修改了代码的功能,仍旧是原来的功能。而此刻 FileStream 和 MemoryStream 都有公共的父类,那么就可以接着进行修改,将编译时依赖,修改为运行时依赖。让代码在运行时在确定具体的类型(多态)。
class Ascii7FileStream(private var stream: Stream) {
override fun read(path: String) {
println("编码")
stream.read(path)
}
}
class Ascii7MemoryStream(private var stream: Stream) {
override fun read(path: String) {
println("编码")
stream.read(path)
}
}此时,Ascii7FIleStream 和 Ascii7MemoryStream 已经变得完全一样,此刻我们也不再需要两个类,只需要一个类 Ascii7Stream。同理,针对压缩类也可以合并为一个类 CompressingStream。
class Ascii7Stream(private var stream: Stream) {
override fun read(path: String) {
println("编码")
stream.read(path)
}
}
class CompressingStream(private var stream: Stream) {
override fun read(path: String) {
println("压缩")
stream.read(path)
}
}组合加继承
此时,细心的朋友肯定发现了,你既没有继承父类或者抽象类,也实现接口,你怎么方法前面还是 override 啊!是啊,这个情况在 kotlin 上是语法错误的。所以就要把 override 去掉吗?当然也不可以,我们既然是实现 Stream 的功能,那就意味着要遵循 Stream 的规范。仍旧需要去实现 Stream 流的接口。
class Ascii7Stream(private var stream: Stream) : Stream {
override fun read(path: String) {
println("编码")
stream.read(path)
}
}组合是为了更好的完成功能,将编译时依赖成为运行时依赖;而继承则是为第一点是符合 kotlin 语法、第二则是为了满足 Stream 的规范。
运行一下
fun main() {
val fileStream = FileStream()
val ascii7Stream = Ascii7Stream(fileStream)
ascii7Stream.read("123")
val memoryStream = MemoryStream()
val compressingStream = CompressingStream(memoryStream)
compressingStream.read("456")
}
// 编码
// FileStream.read 123
// 压缩
// MemoryStream.read 456简单运行一下,可以发现编码和压缩都完成了,那么编码加密操作呢?其实我们也已经实现了。
val fileStream = FileStream()
val ascii7Stream = Ascii7Stream(fileStream)
val compressingStream = CompressingStream(ascii7Stream)
compressingStream.read("456")只需要将上一步完成的 Stream 在进行新的操作即可。这样意味着 CompressingAscii7...这一系列的类就不需要再进行实现了。
现在再看我们的代码
interface Stream {
fun read(path: String)
}
open class FileStream : Stream {
override fun read(path: String) {
println("FileStream.read $path")
}
}
open class MemoryStream : Stream {
override fun read(path: String) {
println("MemoryStream.read $path")
}
}
class Ascii7Stream(private var stream: Stream) : Stream {
override fun read(path: String) {
println("编码")
stream.read(path)
}
}
class CompressingStream(private var stream: Stream) : Stream {
override fun read(path: String) {
println("压缩")
stream.read(path)
}
}相比于第一个版本的代码 59 行,9个类或者接口,而现在我们仅仅需要31行,5个类或者接口,无论函数还是类都相较于之前减少近一半,而且和之前完成的功能一样。
让代码变得更美一些
仔细观察代码,发现 CompressingStream和 Ascii7Stream 都有公共的属性(stream) ,我们可以将他们抽取到一个公共父类。
abstract class StreamDecorator(protected var stream: Stream) : Stream {
override fun read(path: String) {
stream.read(path)
}
}新写一个 abstract 类,让 CompressingStream 和 Ascii7Stream 去继承新的 StreamDecorator 类。通过装饰类,将代码结构分割开来。以后扩展,只需要继承 StreaDecorator 即可。

相关内容
如果你觉得这篇文章对你有所帮助,欢迎赞赏~
赞赏