美文网首页Android技术知识Android开发Android开发经验谈
结构型设计模式之装饰模式:DecoratorPattern

结构型设计模式之装饰模式:DecoratorPattern

作者: 程序老秃子 | 来源:发表于2022-07-26 15:26 被阅读0次

前言

装饰模式(Decorator Pattern) 也称为包装模式,结构型设计模式之一。 其使用一种对客户端透明的方式来动态地扩展对象功能,同时它也是继承关系的一种替代方案。

定义

装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

扩展

扩展是一种十分强大的机制,可以让你在不用继承的情况下,给已存在的类、结构体或者枚举类添加一些新的功能。最重要的一点是,你可以在你没有访问权限的情况下扩展已有类。这意味着你甚至可以扩展 Cocoa 的类,比如 UIView 或者 UIImage 。

举个例子,在编译时新加的方法可以像扩展类的正常方法一样执行。这和装饰器模式有点不同,因为扩展不会持有扩展类的对象。

使用场景

  1. 需要扩展一个类的功能,或给一个类添加附加职责。

  2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。

  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使用继承关系实现不现实。

  4. 当不能采用生成子类的方法进行扩充时。比如类定义被隐藏或类定义无法被继承。

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 架构师之路还很漫长,与君共勉

相关文章

网友评论

    本文标题:结构型设计模式之装饰模式:DecoratorPattern

    本文链接:https://www.haomeiwen.com/subject/qepoirtx.html