• 原型模式与深浅拷贝


    原型模式的目的就是通过复制的手段得到一个新的对象。

    个人认为这种模式最大的好处是省掉了“工厂”这层概念,因为工厂通常给人的印象是某种初始化动作,而拷贝则是建立在运行时已经生成的对象的基础之上的。

    一个更大的好处是可以让拷贝操作作用在聚合根之上,可以一次性拷贝一套复杂的对象集合,对外则屏蔽了赋值过程的细节。

    拷贝分为深拷贝和浅拷贝两种,浅拷贝就是建立一个新指针指向同一个对象,即同一个对象多份引用,深拷贝则是完全复制目标对象,生成一个新的对象二进制数据,并用一个新指针指向新的对象。对于浅拷贝,我觉得没什么好说的。

    对于深拷贝,java/C#当中都提供了克隆接口(我估计很多人都没直接用过它们),OC则提供了NSCopying协议,OC的很多类(比如词典类)都已经实现了这个协议。

    深拷贝要解决的问题分以下几种:普通类的拷贝,子类的拷贝,聚合根的拷贝。

    对于一个普通类Book:

    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Book : NSObject <NSCopying>
    4 @property (nonatomic, copy) NSString *name;
    5 - (id)copyWithZone:(NSZone *)zone;
    6 @end
     1 #import "Book.h"
     2 
     3 @implementation Book
     4 
     5 - (id)copyWithZone:(NSZone *)zone{
     6     Book *copy = [[Book allocWithZone:zone]init];
     7     copy.name = self.name;
     8     return copy;
     9 }
    10 
    11 @end
     1 #import <Foundation/Foundation.h>
     2 #import "Book.h"
     3 
     4 int main(int argc, const char * argv[])
     5 {
     6     @autoreleasepool {
     7         Book *book = [[Book alloc]init];
     8         book.name = @"test";
     9 
    10         Book *book2 = [book copy];
    11 
    12         NSLog(@"%@", book2.name);   //test
    13     }
    14     return 0;
    15 }

    实现拷贝的过程只要实现NSCopying协议当中的方法,并在其中创建一个新的对象就可以了。

    当Book类存在子类时,我们希望子类也能够支持拷贝:

    1 #import "Book.h"
    2 
    3 @interface ProgramingBook : Book <NSCopying>
    4 @property (nonatomic, copy) NSString *language;
    5 - (id)copyWithZone:(NSZone *)zone;
    6 @end
     1 #import "ProgramingBook.h"
     2 
     3 @implementation ProgramingBook
     4 
     5 - (id)copyWithZone:(NSZone *)zone{
     6     ProgramingBook *copy = [super copyWithZone:zone];
     7     copy.language = self.language;
     8     return copy;
     9 }
    10 
    11 @end

    这里调用了父类的copyWithZone:方法,因为从对象的内存结构来看,父类的拷贝工作需要由父类自己完成,这点无可厚非。

    问题是当调用拷贝方法时:

     1 #import <Foundation/Foundation.h>
     2 #import "Book.h"
     3 #import "ProgramingBook.h"
     4 int main(int argc, const char * argv[])
     5 {
     6     @autoreleasepool {
     7         ProgramingBook *book = [[ProgramingBook alloc]init];
     8         book.name = @"test";
     9         book.language = @"java";
    10         
    11         ProgramingBook *book2 = [book copy];
    12 
    13         NSLog(@"%@", book2.name);
    14         NSLog(@"%@", book2.language);
    15     }
    16     return 0;
    17 }

    却会报错,出错的代码是子类拷贝方法中的

    1 copy.language = self.language;

    错误信息是熟悉的unrecognized selector sent to instance 0x100101020 找不到方法。

    仔细分析会发现是找不到self对language参数的getter方法,那么就要怀疑这个self到底是什么。

    由于子类的拷贝方法调用了父类的拷贝方法,而父类的拷贝方法当中有这样一句话:

    1 Book *copy = [[Book allocWithZone:zone]init];

    显然,这里分配了一个Book类型的对象,那么self就表示Book类型,它是不存在language的getter方法的。

    解决方法是把父类拷贝方法做改造:

     1 #import "Book.h"
     2 
     3 @implementation Book
     4 
     5 - (id)copyWithZone:(NSZone *)zone{
     6     Book *copy = [[[self class] allocWithZone:zone]init];
     7     copy.name = self.name;
     8     return copy;
     9 }
    10 
    11 @end

    这样就能正确地初始化子类对象的内存空间、以及生成正确的getter方法了。

    第三种情况就是对聚合根的拷贝,这里举一个最简单的例子,书当中聚合了一个叫做Pages的集合,其中含有一个页数属性,其他的属性这里略去:

    让这个类也实现NSCopying协议,因为在聚合根Book当中,也是要对Pages类进行深拷贝的,而它的深拷贝行为应该是自治的:

    1 #import <Foundation/Foundation.h>
    2 
    3 @interface Pages : NSObject <NSCopying>
    4 @property (nonatomic, assign) NSInteger number;
    5 - (id)copyWithZone:(NSZone *)zone;
    6 @end
     1 #import "Pages.h"
     2 
     3 @implementation Pages
     4 
     5 - (id)copyWithZone:(NSZone *)zone {
     6     Pages *copy = [[[self class] allocWithZone:zone]init];
     7     copy.number = self.number;
     8     return copy;
     9 }
    10 
    11 @end

    聚合根类Book定义改为:

    1 #import <Foundation/Foundation.h>
    2 
    3 @class Pages;
    4 
    5 @interface Book : NSObject <NSCopying>
    6 @property (nonatomic, copy) NSString *name;
    7 @property (nonatomic, strong) Pages *pages;
    8 - (id)copyWithZone:(NSZone *)zone;
    9 @end
     1 #import "Book.h"
     2 #import "Pages.h"
     3 
     4 @implementation Book
     5 
     6 - (id)copyWithZone:(NSZone *)zone{
     7     Book *copy = [[[self class] allocWithZone:zone]init];
     8     copy.name = self.name;
     9     copy.pages = [self.pages copy];
    10     return copy;
    11 }
    12 
    13 @end

    只要在其中对pages变量进行copy就可以了,这样就能保证聚合根的拷贝操作与被聚合类的拷贝操作不发生耦合。

  • 相关阅读:
    Swizzling的使用
    Kubelet
    Windows 下 LaTeX 手动安装宏包(package)以及生成帮助文档的整套流程
    Leetcode-Day Three
    吴裕雄--天生自然Linux操作系统:Linux 用户和用户组管理
    吴裕雄--天生自然Linux操作系统:Linux 文件与目录管理
    吴裕雄--天生自然Linux操作系统:Linux 文件基本属性
    吴裕雄--天生自然Linux操作系统:Linux 远程登录
    吴裕雄--天生自然Linux操作系统:Linux 忘记密码解决方法
    吴裕雄--天生自然Linux操作系统:Linux 系统目录结构
  • 原文地址:https://www.cnblogs.com/Steak/p/3538047.html
Copyright © 2020-2023  润新知