原型模式概念:
- 把某些对象变成”塑胶印章",让其拥有“复制”自身并得到其复制品的能力。
- “复制”指:用同一个模具,生产一系列的产品。这些产品只是某些颜色,特征不同而已,只需进行简单修改。
- 原型模式“复制”的对象都是真实的副本实例;
原型模式定义:
- 应用“复制”操作的模式,称为原型模式。
原型模式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项目下。