内容简介:最近一直不在状态,月初就被博客质量的事给弄的情绪低落,之后群里又走了两个朋友,心情是一直在低谷徘徊,博客也是不想写,状态一天不如一天,总之就是一句话,不想工作。所以…… 有没有小(fu)姐(luo)姐(li)私聊我啊!设计模式刚入门的小伙伴可以先看看这篇《设计模式入门》,在文章末尾也将列出“设计模式系列”文章。欢迎大家关注留言投币丢香蕉。天星技术团QQ:557247785。为了方便理解,我们先举一些例子。
最近一直不在状态,月初就被博客质量的事给弄的情绪低落,之后群里又走了两个朋友,心情是一直在低谷徘徊,博客也是不想写,状态一天不如一天,总之就是一句话,不想工作。所以…… 有没有小(fu)姐(luo)姐(li)私聊我啊!
设计模式刚入门的小伙伴可以先看看这篇《设计模式入门》,在文章末尾也将列出“设计模式系列”文章。欢迎大家关注留言投币丢香蕉。天星技术团QQ:557247785。
什么是装饰者模式
为了方便理解,我们先举一些例子。
人是一个类,要上街的话,人就得穿衣服,裤子,鞋子,这是最简单的装扮,有些人还会打领带、背包、戴帽子等等。在这个例子中, “人”是一个被装饰者 , 衣服裤子鞋子是装饰者 。在整个打扮过程中, 被装饰者类是不会被更改的,产生变化的是装饰者类的数量。
在吃火锅之前,我们会调料碗。那么空碗就是被装饰者,油、香菜、葱花就是装饰者;
点菜这一步骤,也是装饰者模式,鸳鸯锅就是被装饰者,麻辣牛肉、毛肚、鸭肠、菌肝、千层肚、鸭血、红糖糍粑就是装饰者;
装饰模式就是在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。
聊到这里,有没有一点饿?
现在呢?
走进装饰者模式
首先看一下装饰者模式的UML图
可以看出装饰模式中,有四个参与者:
- Component(抽象组件): 定义对象的接口\抽象类,以规范准备接收附加责任的对象。
- ConcreteComponent(具体组件): 具体的对象,抽象装饰者能给他新增职责
- Decorator(抽象装饰者): 持有一个抽象组件对象的实例,并定义一个与抽象组件一致的接口。
- ConcreteDecorator(具体装饰者): 具体的装饰对象。给内部持有的具体组件增加具体的职责;
我是按括号里的文字来记忆的,会比较容易记住。刚刚我们举例写到的“人”就是抽象组件;“男人\女人”就是具体组件;“装饰品”就是抽象组件;“帽子”就是具体组件;
装饰模式的特点
- 装饰者和被装饰者有相同的超类型
- 可以用一个或者多个装饰者包装一个对象
- 任何需要被装饰者对象的场合,可以用装饰过的对象代替它。(其实就是因为特点一)
- 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为。(很重要)
- 对象可以在任何时候被装饰,包括运行时。
装饰模式的使用场合
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象增加/撤销职责。
- 当不能采用生成子类的方法进行扩充时。
活生生的例子
我们先来分析一下上面提到的人化妆的例子。首先来创建四个参与者。 1.抽象组件Human ,给他一个自我介绍的描述,再添加一个方法,说出自己穿了什么。
abstract class Human { open var description = "I'm a human." abstract fun getDress() : String } 复制代码
- 继承抽象组件的具体组件Male\Female,改变下自我描述。
class Male : Human() { override var description: String get() = "我是男性" set(value) {} override fun getDress(): String { return "我穿了内裤" } } 复制代码
- 继承抽象组件的抽象装饰者Decoration
abstract class Decoration : Human() { abstract override var description: String } 复制代码
- 继承抽象装饰者的具体装饰者,这里我写一个帽子类,其他的随意添加。
class Hats : Decoration() { override var description: String get() = " 我有帽子" set(value) {} override fun getDress(): String { return "帽子" } } 复制代码
现在有了四个参与者,结构也按照UML写好了。那接下来就是包装了。 穿衣服时,我们会一件一件的穿(没有谁会同时穿吧?),所以,我们穿了裤子后,再穿衣服时,被装饰者是“男\女人+裤子”。看图!
装饰者是一个一个去包裹被装饰者,这里要注意,衣服和裤子跟男人是同一个超类,我们在包裹的时候,需要把被包装的对象(被包装的对象可能是具体组件,也可能是已经被装饰者装饰之后的具体组件),传到装饰者中,所以我们需要在具体装饰者类中添加一个超类参数。这样才能得到被包装对象的所有参数。所以刚刚的具体装饰者类还没写完,补充完整应该是:
class Hats(var human: Human) : Decoration() { override var description: String get() = "帽子" set(value) {} override fun getDress(): String { return human.getDress() + " 帽子" } } 复制代码
再创造几个具体装饰者类,此时项目结构就是这样的。
现在我们创造一个超人,给她穿上帽子,斗篷,并在界面中显示一下自己穿了些什么。
class MainActivity : AppCompatActivity() { var superMan : Human? = Female() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) superMan = Cloak(Hats(this.superMan!!)) tv_textview.text = superMan?.description + superMan?.getDress() } } 复制代码
结果如下:
总结一下
通过例子应该对装饰模式有个初步的理解了。再写demo的时候也只需要记住一下几步:
- 按照装饰模式UML图,写出四个参与者类。
- 在具体装饰者类的主构造函数中添加超类参数
- 父类引用指向子类对象创造具体组件
- 用具体装饰者装饰对象(需要什么装饰什么)
装饰模式优点:装饰模式比普通的继承更加灵活,能够在运行时更改组件的功能。而继承的类在运行前就决定了所有功能。
缺点:会创造很多的小类。别人看代码的时候会很脑壳痛。
注意:装饰模式中,装饰的顺序很重要。先穿“裤子”再穿“衣服”跟先穿“衣服”后穿“裤子”是不一样的
源码中的装饰模式Java I/O
我当初决定要学习 设计模式 的初衷,是为了看源码。现在我们就来一起来看看源码, 我也会顺便把我在这当中学到的一些看源码的方式方法说出来。大佬们请忽略这句话。
查看源码技巧一:查看它的父类子类并画图。
在AS右上角有个hierarchy按钮,点它可以查看当前类的直接父类和全部子类。
java I/O 是比较庞大的一个库,如果直接看其代码,很难知道每个类都在干啥,也不知道它究竟怎么运作的。实话说,我以前就看晕了。 通过看它的类结构。我们能画出这样一张图:
在这个设计中,InputStream就是抽象组件,FilterInputStream就是抽象装饰者, StringBufferInputStream、ByteArrayInputStream等是具体组件,LineNumberInputStream、DataInputStream、BufferedInputStream等是具体装饰者。
几个具体组件提供了不同类型的基本字节读取功能。 具体装饰类提供了额外的功能。例如:BufferedInputStream提供readline()方法。
源码扩展
接下来我们试试自编写一个新的具体装饰者类。
//将所有大写字符转为小写 class LowerCaseInputSteam(inputStream: InputStream) : FilterInputStream(inputStream){ override fun read(): Int { val result = super.read() if(result==-1) return result else return Character.toLowerCase(result) } override fun read(b: ByteArray, off: Int, len: Int): Int { val result = super.read(b, off, len) for (i in off until off+result){ b[i] = Character.toLowerCase(b[i].toInt()).toByte() } return result } } 复制代码
此处需要实现两个方法,一个针对字节,一个针对字节组。
try { var inputStream : InputStream = LowerCaseInputSteam(BufferedInputStream(FileInputStream("手机文件路径"))) c = inputStream.read() while (c!! >0) { stringBuffer?.append(c!!) c = inputStream.read() } tv_textview.text = stringBuffer.toString() }catch (e : IOException){ e.printStackTrace() } 复制代码
第一次写关于源码的东西,写的不好的地方,多多提意见。
下一次我将写代理模式,也会讲到装饰模式和代理模式的区别,和本章未提到的装饰模式的透明性。
以下是我“设计模式系列”文章,欢迎大家关注留言投币丢香蕉。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring 源码学习(七)扩展功能 下篇
- Spring源码解析:高级容器的扩展内幕
- Spring 源码(四):扩展点之 BeanFactoryPostProcessor
- Spring 源码(八):扩展点之 mybatis 集成
- Spring 源码学习(六)扩展功能 上篇-BeanFactoryPostProcessor
- Dubbo源码分析(一)Dubbo的扩展点机制
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。