前言
装饰模式(Decorator Pattern) 也称为包装模式,结构型设计模式之一。 其使用一种对客户端透明的方式来动态地扩展对象功能,同时它也是继承关系的一种替代方案。
定义
装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
扩展
扩展是一种十分强大的机制,可以让你在不用继承的情况下,给已存在的类、结构体或者枚举类添加一些新的功能。最重要的一点是,你可以在你没有访问权限的情况下扩展已有类。这意味着你甚至可以扩展 Cocoa 的类,比如 UIView 或者 UIImage 。
举个例子,在编译时新加的方法可以像扩展类的正常方法一样执行。这和装饰器模式有点不同,因为扩展不会持有扩展类的对象。
使用场景
-
需要扩展一个类的功能,或给一个类添加附加职责。
-
需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
-
需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使用继承关系实现不现实。
-
当不能采用生成子类的方法进行扩充时。比如类定义被隐藏或类定义无法被继承。
UML
-
Component:抽象组件,给出一个抽象接口,以规范准备接收附加能力的对象。
-
ConcreteComponent: 具体组件,定义一个将要接收附加能力的类,即被装饰的具体对象。
-
Decorator: 抽象装饰者,其职责就是装饰我们的组件,其内部会有一个指向组件对象的引用
-
ConcreteDecoratorA, ConcreteDecoratorB: 装饰者的具体实现类,用于附加上不同的能力
如何使用扩展
swift
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="kotlin" cid="n47" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">extension Double{
var km : Double { return self * 1000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm;
print("One inch is \(oneInch)meters")</pre>
objective-c
例子1
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="objectivec" cid="n51" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//MyClass.h
@interface MyClass : NSObject
- (float)value;
@end
//MyClass.m
@interface MyClass () { //注意此处:扩展
float value;
}
- (void)setValue:(float)newValue;
@end
@implementation MyClass
- (float)value {
return value;
}
- (void)setValue:(float)newValue {
value = newValue;
}
@end </pre>
例子2
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="objectivec" cid="n54" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//Person_extension.h
#import "Person.h"
@interface Person () {
NSString *_someStr;
}
@property NSObject *extraProperty;
@property (readwrite) NSString *uniqueIdentifier;
@end</pre>
委托
装饰者模式的另一种实现方案是委托。在这种机制下,一个对象可以和另一个对象相关联。比如你在用UITableView
,你必须实现tableView(_:numberOfRowsInSection:)
这个委托方法。 你不应该指望UITableView
知道你有多少数据,这是个应用层该解决的问题。所以,数据相关的计算应该通过UITableView
的委托来解决。这样可以让UITableView
和数据层分别独立。视图层就负责显示数据,你递过来什么我就显示什么。
下面这张图很好的解释了UITableView
的工作过程:
UITableView
的工作仅仅是展示数据,但是最终它需要知道自己要展示那些数据,这时就可以向它的委托询问。在 objc 的委托模式里,一个类可以通过协议来声明可选或者必须的方法。
看起来似乎继承然后重写必须的方法来的更简单一点。但是考虑一下这个问题:继承的结果必定是一个独立的类,如果你想让某个对象成为多个对象的委托,那么子类这招就行不通了。
注意:委托模式十分重要,苹果在 UIKit 中大量使用了该模式,基本上随处可见。
如何使用委托模式
swift
例子1
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="swift" cid="n68" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName 为 "John Appleseed"</pre>
例子2
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="swift" cid="n71" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//定义一个协议
protocol LogManagerDelegate {
func writeLog()
}
//用户登录类
class UserController {
var delegate : LogManagerDelegate?
func login() {
//查看是否有委托,然后调用它
delegate?.writeLog()
}
}
//日志管理类
class SqliteLogManager : LogManagerDelegate {
func writeLog() {
print("将日志记录到sqlite数据库中")
}
}
//使用
let userController = UserController()
userController.login() //不做任何事
let sqliteLogManager = SqliteLogManager()
userController.delegate = sqliteLogManager
userController.login() //输出“将日志记录到sqlite数据库中”</pre>
Objective - C
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="objectivec" cid="n74" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//Mydelegate.h
//可以单独放在一个.h文件,也可以直接放入A.h中
@protocol Mydelegate//协议的声明 中
-(void)Fun;
@end
//A.h
#import "Mydelegate.h"
@interface A:NSObject
{
id<Mydelegate> delegate;//声明一个委托对象,我们要把事情委托给实现Mydelegate协议的类
}
@property(assign,nonatomic) id<Mydelegate> delegate;
@end
//A.m
#import "A.h"
@implementation
-(void)FunToOther
{
[delegate Fun];//将消息发送给委托去处理
}
@end
//B.h
#import"A.h"
@interface B:NSObject<MyDelegate>
@property(strong,nonatomic) A *a;
@end
//B.m
#import "B.h"
@implementation
-(void)init
{
_a = [[A alloc] init];
_a.delegate = self;
}
#prage mark - MyDelegate
-(void)fun
{
//实现Mydelegate中制定的方法
}
@end</pre>
有需要文中完整代码的同学: 《现在点击此链接 即可免费获取》
现在点击链接还可以获取《更多 Android 源码解析+学习大纲+面试真题》
总结
优点
装饰者模式的性能非常好, 在项目中,你会有非常多的因素考虑不到,特别是业务的变更,不时地冒出一个需求,装饰模式可以给我们很好的帮助,通过装饰模式重新封装一 个类,而不是通过继承来完成, 对原有程序没有变更。
缺点
可忽略,还是那句话,类增加了,麻烦。可以在实际需要时再抽象组件,避免过度设计
最后我想说:
对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
网友评论