解說21種設計模式之第一篇_原型模式(物件建立型)

滴水微瀾發表於2019-04-05

原型模式概念:

  • 把某些物件變成”塑膠印章",讓其擁有“複製”自身並得到其複製品的能力。
  • “複製”指:用同一個模具,生產一系列的產品。這些產品只是某些顏色,特徵不同而已,只需進行簡單修改。
  • 原型模式“複製”的物件都是真實的副本例項;

原型模式定義:

  • 應用“複製”操作的模式,稱為原型模式。

原型模式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專案下。

 

相關文章