装饰者模式可以动态的给指定的类添加一些行为和职责,而不用对原代码进行任何修改。当你需要使用子类的时候,不妨考虑一下装饰者模式,可以在原始类上面封装一层。
在 Swift 里,有两种方式实现装饰者模式:扩展 (Extension) 和委托 (Delegation)。
在Objective-C 里,有两种方式实现装饰者模式:扩展 (Extension)、委托 (Delegation) 和 分类 (category)。
扩展
扩展是一种十分强大的机制,可以让你在不用继承的情况下,给已存在的类、结构体或者枚举类添加一些新的功能。最重要的一点是,你可以在你没有访问权限的情况下扩展已有类。这意味着你甚至可以扩展 Cocoa 的类,比如 UIView 或者 UIImage 。
举个例子,在编译时新加的方法可以像扩展类的正常方法一样执行。这和装饰器模式有点不同,因为扩展不会持有扩展类的对象。
如何使用扩展
swift
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")
objective-c
例子1
//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
例子2
//Person_extension.h
#import "Person.h"
@interface Person () {
NSString *_someStr;
}
@property NSObject *extraProperty;
@property (readwrite) NSString *uniqueIdentifier;
@end
委托
装饰者模式的另一种实现方案是委托。在这种机制下,一个对象可以和另一个对象相关联。比如你在用UITableView
,你必须实现tableView(_:numberOfRowsInSection:)
这个委托方法。
你不应该指望UITableView
知道你有多少数据,这是个应用层该解决的问题。所以,数据相关的计算应该通过UITableView
的委托来解决。这样可以让UITableView
和数据层分别独立。视图层就负责显示数据,你递过来什么我就显示什么。
下面这张图很好的解释了UITableView
的工作过程:
UITableView
的工作仅仅是展示数据,但是最终它需要知道自己要展示那些数据,这时就可以向它的委托询问。在 objc 的委托模式里,一个类可以通过协议来声明可选或者必须的方法。
看起来似乎继承然后重写必须的方法来的更简单一点。但是考虑一下这个问题:继承的结果必定是一个独立的类,如果你想让某个对象成为多个对象的委托,那么子类这招就行不通了。
注意:委托模式十分重要,苹果在 UIKit 中大量使用了该模式,基本上随处可见。
如何使用委托模式
swift
例子1
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName 为 "John Appleseed"
例子2
//定义一个协议
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数据库中”
Objective - C
//Mydelegate.h
//可以单独放在一个.h文件,也可以直接放入A.h中
@protocol Mydelegate//协议的声明 中
-(void)Fun;
@end
//A.h
#import "Mydelegate.h"
@interface A:NSObject
{
id delegate;//声明一个委托对象,我们要把事情委托给实现Mydelegate协议的类
}
@property(assign,nonatomic) id delegate;
@end
//A.m
#import "A.h"
@implementation
-(void)FunToOther
{
[delegate Fun];//将消息发送给委托去处理
}
@end
//B.h
#import"A.h"
@interface B:NSObject
@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
Objective-C 类别(Category)
使用Object-C中的分类,是一种编译时的手段,允许我们通过给一个类添加方法来扩充它(但是通过category不能添加新的实例变量),并且我们不需要访问类中的代码就可以做到,这点和javascript中使用原型来定义属性有点类似。
我们可以为一个类创建一个新的方法,而不需要在代码中编辑类定义。
//Person+categoryPerson.h
#import "Person.h"
@interface Person () {
NSString *_someStr;
}
@property NSObject *extraProperty;
@property (readwrite) NSString *uniqueIdentifier;
@end
//Person+categoryPerson.m
#import "Person+categoryPerson.h"
@implementation Person (categoryPerson)
- (NSString *)lastNameFirstNameString {
return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName];
}
- (NSString *)category_lastNameFirstNameString {
return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName];
}
@end
//直接使用
Person *person = [Person alloc] init];
NSString *allName = [person lastNameFirstNameString];
NSLog(@"allName:%@", allName);