原型模式概念:
- 把某些物件變成”塑膠印章",讓其擁有“複製”自身並得到其複製品的能力。
- “複製”指:用同一個模具,生產一系列的產品。這些產品只是某些顏色,特徵不同而已,只需進行簡單修改。
- 原型模式“複製”的物件都是真實的副本例項;
原型模式定義:
- 應用“複製”操作的模式,稱為原型模式。
原型模式UML圖如下:
客戶端引用著抽象父類Prototype類,父類Prototype類定義了clone的方法,Prototype類的子類實現clone方法;
何時考慮使用原型模式呢?
- 不想要產品工廠
- 不同例項間的差異僅僅是屬性狀態的不同,因此複製比手工建立更加便捷。
- 手工建立不容易。像複雜的組合物件,建立起來沒有複製來的快。
Cocoa Touch框架中的應用
NSCopying協議
Cocoa Touch為NSObject的派生類提供了實現深複製的協議NSCopying;
NSObject的子類需要實現協議NSCopying的方法copyingWithZone:
NSObject根類
NSObject提供的例項方法copy底層呼叫也是copyingWithZone:
所以遵守了NSCopying協議的物件,需要實現方法copyingWithZone,不然會引起異常。
實際例子如下:
現在需要做個畫板功能,功能點兩個:1.實現畫線;2.實現畫點; 線的特性:顏色,寬度; 點的特徵:顏色,Size大小; 下面設計了三個類和一個協議來表示它們的邏輯關係。
第一個版本設計:不具備“複製”能力的簡單自定義物件
基本關係型別的UML圖
程式碼實現如下:
Mark協議:
@protocol Mark <NSObject> @property (nonatomic, assign) CGSize size; @property (nonatomic, strong) UIColor *color; @property (nonatomic, assign) CGPoint location; - (void)addMark:(id<Mark>)mark; - (void)removeMark:(id<Mark>)mark; @end
Vertex類:最簡單,它只需要表示一個位置
@implementation Vertex @synthesize location = _location; @dynamic color,size; @end
Dot類:繼承自Vertex, 由於其表示一個獨立的點,所以有size,color屬性值
@implementation Dot @synthesize color = _color, size = _size; @end
Stroke類:是有若干個Vertex連線而成的線段。
@implementation Stroke @dynamic location; @synthesize color = _color, size = _size; #pragma mark - Life Cycle #pragma mark - Getter, Setter - (CGPoint)location { return [self.marksArray.firstObject location]; }
第二版本:具備“複製”能力的增強型自定義物件(原型模式)
程式碼實現如下:
Mark協議:
@protocol Mark <NSObject> @property (nonatomic, assign) CGSize size; @property (nonatomic, strong) UIColor *color; @property (nonatomic, assign) CGPoint location; - (void)addMark:(id<Mark>)mark; - (void)removeMark:(id<Mark>)mark; - (id<Mark>)copy; @end
Vertex類:新增了一個通過自身location建立的建構函式,為子類建立做準備
@implementation Vertex @synthesize location = _location; @dynamic color,size; - (instancetype)initWithLocation:(CGPoint)location { self = [super init]; if (self) { _location = location; } return self; } - (id)copyWithZone:(NSZone *)zone { Vertex *ver = [[[self class] alloc] initWithLocation:self.location]; return ver; } @end
Dot類:先通過父類的建構函式建立新物件,再對新物件設定屬性
@implementation Dot @synthesize color = _color, size = _size; - (id)copyWithZone:(NSZone *)zone { Dot *dot = [[[self class] alloc] initWithLocation:self.location]; dot.size = self.size; dot.color = [UIColor colorWithCGColor:[self.color CGColor]]; return dot; } @end
Stroke類:先建立一個新物件,同時遍歷舊物件的marksArray陣列,對裡面的所有物件(id<Mark>)
逐個進行copy
@implementation Stroke @dynamic location; @synthesize color = _color, size = _size; #pragma mark - Life Cycle #pragma mark - Getter, Setter - (CGPoint)location { return [self.marksArray.firstObject location]; } #pragma mark - Private Method - (id)copyWithZone:(NSZone *)zone { Stroke *stroke = [[[self class] alloc] init]; stroke.color = [UIColor colorWithCGColor:[self.color CGColor]]; stroke.size = self.size; NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:[self.marksArray count]]; for (id<Mark> mark in self.marksArray) { [arrayM addObject:[mark copy]]; } stroke.marksArray = arrayM; return stroke; } @end
對例子的實際使用
在觸控回撥方法中記錄觸控點資訊
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
在全域性變數中記錄觸控點資訊並設定屬性監控,當有觸控點改變就觸發監控函式,在監控函式中進行影象繪製
[self.scribble addObserver:self forKeyPath:@"mark" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
專案效果圖如下:
原型模式在平時開發的複雜模型中使用頻率較大,合理使用對於提高app效能有很好的作用。
具體在(Objective-C_Design_Patterns)WorkSpace下的(Design_Patterns_Demoes)Project專案下。