前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面向对象设计的设计模式(六):原型模式

面向对象设计的设计模式(六):原型模式

作者头像
用户2932962
发布2019-03-18 10:30:12
6040
发布2019-03-18 10:30:12
举报
文章被收录于专栏:程序员维他命

定义

原型模式(Prototype Pattern): 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

适用场景

  • 对象层级嵌套比较多,从零到一创建对象的过程比较繁琐时,可以直接通过复制的方式创建新的对象
  • 当一个类的实例只能有几个不同状态组合中的一种时,我们可以利用已有的对象进行复制来获得

成员与类图

成员

原型模式主要包含如下两个角色:

  1. 抽象原型类(Prototype):抽象原型类声明克隆自身的接口。
  2. 具体原型类(ConcretePrototype):具体原型类实现克隆的具体操作(克隆数据,状态等)。

下面通过类图来看一下各个成员之间的关系:

模式类图

原型模式类图

需要注意的是,这里面的clone()方法返回的是被复制出来的实例对象。

代码示例

场景概述

模拟一份校招的简历,简历里面有人名,性别,年龄以及学历相关的信息。这里面学历相关的信息又包含学校名称,专业,开始和截止年限的信息。

场景分析

这里的学历相关信息可以使用单独一个对象来做,因此整体的简历对象的结构可以是:

简历对象:

  • 人名
  • 性别
  • 年龄
  • 学历对象
  • 学校名称
  • 专业
  • 开始年份
  • 结束年份

而且因为对于同一学校同一届的同一专业的毕业生来说,学历对象中的信息是相同的,这时候如果需要大量生成这些毕业生的简历的话比较适合使用原型模式。

代码实现

首先定义学历对象:

代码语言:javascript
复制
//================== UniversityInfo.h ==================
@interface UniversityInfo : NSObject<NSCopying>

@property (nonatomic, copy) NSString *universityName;
@property (nonatomic, copy) NSString *startYear;
@property (nonatomic, copy) NSString *endYear;
@property (nonatomic, copy) NSString *major;

- (id)copyWithZone:(NSZone *)zone;

@end



//================== UniversityInfo.m ==================
@implementation UniversityInfo

- (id)copyWithZone:(NSZone *)zone
{
    UniversityInfo *infoCopy = [[[self class] allocWithZone:zone] init];

    [infoCopy setUniversityName:[_universityName mutableCopy]];
    [infoCopy setStartYear:[_startYear mutableCopy]];
    [infoCopy setEndYear:[_endYear mutableCopy]];
    [infoCopy setMajor:[_major mutableCopy]];

    return infoCopy;
}

@end

因为学历对象是支持复制的,因此需要遵从<NSCopying>协议并实现copyWithZone:方法。而且支持的是深复制,所以在复制NSString的过程中需要使用mutableCopy来实现。

接着我们看一下简历对象:

代码语言:javascript
复制
//================== Resume.h ==================
#import "UniversityInfo.h"

@interface Resume : NSObject<NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *gender;
@property (nonatomic, copy) NSString *age;

@property (nonatomic, strong) UniversityInfo *universityInfo;

@end



//================== Resume.m ==================
@implementation Resume

- (id)copyWithZone:(NSZone *)zone
{
    Resume *resumeCopy = [[[self class] allocWithZone:zone] init];

    [resumeCopy setName:[_name mutableCopy]];
    [resumeCopy setGender:[_gender mutableCopy]];
    [resumeCopy setAge:[_age mutableCopy]];
    [resumeCopy setUniversityInfo:[_universityInfo copy]];

    return resumeCopy;
}

@end

同样地,简历对象也需要遵从<NSCopying>协议并实现copyWithZone:方法。

最后我们看一下复制的效果有没有达到我们的预期(被复制对象和复制对象的地址和它们所有的属性对象的地址都不相同)

代码语言:javascript
复制
//================== Using by client ==================


//resume for LiLei
Resume *resume = [[Resume alloc] init];
resume.name = @"LiLei";
resume.gender = @"male";
resume.age = @"24";

UniversityInfo *info = [[UniversityInfo alloc] init];
info.universityName = @"X";
info.startYear = @"2014";
info.endYear = @"2018";
info.major = @"CS";

resume.universityInfo = info;


//resume_copy for HanMeiMei
Resume *resume_copy = [resume copy];

NSLog(@"\n\n\n======== original resume ======== %@\n\n\n======== copy resume ======== %@",resume,resume_copy);

resume_copy.name = @"HanMeiMei";
resume_copy.gender = @"female";
resume_copy.universityInfo.major = @"TeleCommunication";

NSLog(@"\n\n\n======== original resume ======== %@\n\n\n======== revised copy resume ======== %@",resume,resume_copy);

上面的代码模拟了这样一个场景:李雷同学写了一份自己的简历,然后韩梅梅复制了一份并修改了姓名,性别和专业这三个和李雷不同的信息。

这里我们重写了Resumedescription方法来看一下所有属性的值及其内存地址。最后来看一下resume对象和resume_copy对象打印的结果:

代码语言:javascript
复制
//================== Output log ==================

======== original resume ======== 
resume object address:0x604000247d10
name:LiLei | 0x10bc0c0b0
gender:male | 0x10bc0c0d0
age:24 | 0x10bc0c0f0
university name:X| 0x10bc0c110
university start year:2014 | 0x10bc0c130
university end year:2018 | 0x10bc0c150
university major:CS | 0x10bc0c170


======== copy resume ======== 
resume object address:0x604000247da0
name:LiLei | 0xa000069654c694c5
gender:male | 0xa000000656c616d4
age:24 | 0xa000000000034322
university name:X| 0xa000000000000581
university start year:2014 | 0xa000000343130324
university end year:2018 | 0xa000000383130324
university major:CS | 0xa000000000053432





======== original resume ======== 
resume object address:0x604000247d10
name:LiLei | 0x10bc0c0b0
gender:male | 0x10bc0c0d0
age:24 | 0x10bc0c0f0
university name:X| 0x10bc0c110
university start year:2014 | 0x10bc0c130
university end year:2018 | 0x10bc0c150
university major:CS | 0x10bc0c170


======== revised copy resume ======== 
resume object address:0x604000247da0
name:HanMeiMei | 0x10bc0c1b0
gender:female | 0x10bc0c1d0
age:24 | 0xa000000000034322
university name:X| 0xa000000000000581
university start year:2014 | 0xa000000343130324
university end year:2018 | 0xa000000383130324
university major:TeleCommunication | 0x10bc0c1f0

  • 上面两个是原resume和刚被复制后的 copy resume的信息,可以看出来无论是这两个对象的地址还是它们的值对应的地址都是不同的,说明成功地实现了深复制。
  • 下面两个是原resume和被修改后的 copy_resume的信息,可以看出来新的copy_resume的值发生了变化,而且值所对应的地址还是和原resume的不同。

注:还可以用序列化和反序列化的办法来实现深复制,因为与代码设计上不是很复杂,很多语言直接提供了接口,故这里不做介绍。

下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:

代码对应的类图

原型模式代码示例类图

在这里需要注意的是:

  • copy方法是NSObject类提供的复制本对象的接口。NSObject类似于Java中的Object类,在Objective-C中几乎所有的对象都继承与它。而且这个copy方法也类似于Object类的clone()方法。
  • copyWithZone(NSZone zone)方法是接口NSCopying提供的接口。而因为这个接口存在于实现文件而不是头文件,所以它不是对外公开的;即是说外部无法直接调用copyWithZone(NSZone zone)方法。copyWithZone(NSZone zone)方法是在上面所说的copy方法调用后再调用的,作用是将对象的所有数据都进行复制。因此使用者需要在copyWithZone(NSZone zone)方法里做工作,而不是copy方法,这一点和Java的clone方法不同。

优点

  • 可以利用原型模式简化对象的创建过程,尤其是对一些创建过程繁琐,包含对象层级比较多的对象来说,使用原型模式可以节约系统资源,提高对象生成的效率。
  • 可以很方便得通过改变值来生成新的对象:有些对象之间的差别可能只在于某些值的不同;用原型模式可以快速复制出新的对象并手动修改值即可。

缺点

  • 对象包含的所有对象都需要配备一个克隆的方法,这就使得在对象层级比较多的情况下,代码量会很大,也更加复杂。

iOS SDK 和 JDK 中的应用

  • Objective-C中可以使用 协议,配合- (id)copyWithZone:(NSZone *)zone方法; 或者协议,配合 copyWithZone:/mutableCopyWithZone:方法
  • Java中可以让一个类实现Cloneable接口并实现clone()方法来复制该类的实例。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员维他命 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • 适用场景
  • 成员与类图
    • 成员
      • 模式类图
      • 代码示例
        • 场景概述
          • 场景分析
            • 代码实现
              • 代码对应的类图
              • 优点
              • 缺点
              • iOS SDK 和 JDK 中的应用
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档