十谒拴、原型和外觀模式

1. 相關概念

模版的用處:我們要制作一系列的東西尝江,他們都有相同之處,我們只需要修改其中一個模板的某些元素英上,就能供給其他地方使用炭序,這就是模板。

原型模型是先借用已有系統(tǒng)作為原型模型苍日,通過“樣品”不斷改進惭聂,使得最后的產(chǎn)品就是用戶所需要的。

原型模式屬于對象的創(chuàng)建模式相恃。通過給出一個原型對象來指明所要創(chuàng)建的對象的類型辜纲,然后用復制這個原型對象的創(chuàng)建辦法創(chuàng)建出更多同類型的對象。
不通過new來創(chuàng)建對象而是通過拷貝來創(chuàng)建對象的模式就叫做原型模式拦耐。

抽象的部分一致耕腾,細節(jié)不同。

2. 原型模式的原理

原型模式的原理

何時使用原型模式

某些對象或控件特別復雜揩魂,我們直接重寫的話比較麻煩幽邓,而直接從之前的模板中拷貝出來,只需修改其中的幾個元素就能使用火脉。

代碼示例:

(1)定義一個協(xié)議

#import <Foundation/Foundation.h>

@protocol ProtoypeCopyProtocol <NSObject>

@required

/**
 *  復制自己
 *
 *  @return 返回一個拷貝樣本
 */
- (id)clone;

@end

(2)遵守協(xié)議牵舵,并實現(xiàn)拷貝方法

#import <Foundation/Foundation.h>
#import "ProtoypeCopyProtocol.h"

@interface StudentModel : NSObject <ProtoypeCopyProtocol>

@property (nonatomic, strong) NSString  *name;
@property (nonatomic, strong) NSNumber  *age;
@property (nonatomic, strong) NSString  *address;
@property (nonatomic, strong) NSNumber  *totalScore;

- (id)clone; // 實現(xiàn)復制方法

@end
#import "Student.h"

@implementation Student

/**
 *  完成復制的所有操作
 *
 *  @return 學生對象
 */
- (id)clone{

    Student *student = [[[self class] alloc] init];
    student.name = self.name;
    student.age = self.age;
    student.address = self.address;
    student.totalScore = self.totalScore;

    return student;
}

@end

在主控制器中調(diào)用復制的方法

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    Student *student = [[Student alloc] init];
    student.name = @"樂樂樂";
    student.age = 24;
    student.address = @"北京宇宙無限公司";
    student.totalScore = 10000;

    Student *student2 = [student clone];
    NSLog(@"%@", student2.name);
}

結(jié)論:以上只是通過一個小的模型來展示了原型模式的實現(xiàn)原理,如果一個對象很簡單倦挂,屬性很少畸颅,我們會覺得并沒有什么卵用,但是如果很復雜的一個控件有很多相似之處方援,使用原型模式設計是不是讓我們開發(fā)事半功倍没炒?

3. NSCopying協(xié)議的使用細節(jié)

(1)創(chuàng)建基類,遵守NSCopying協(xié)議犯戏,并實現(xiàn)協(xié)議的代理方法

#import <Foundation/Foundation.h>

@interface BaseCopyObject : NSObject<NSCopying>

/**
 *  == 子類不要重載 ==
 *
 *  @return 復制的對象
 */
- (id)copyWithZone:(NSZone *)zone;

/**
 *  == 由子類重載實現(xiàn)送火,只是為了通過這個方法讓外界拿到拷貝過的對象用來進行進一步的賦值 =
 *
 * 復制(賦值實現(xiàn))
 *  @param object 已經(jīng)復制的對象
 */
- (void)copyOperationObject:(id)object;

@end
#import "BaseCopyObject.h"

@implementation BaseCopyObject

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

    BaseCopyObject *copyObject = [[self class] allocWithZone:zone];

    // 賦值操作作業(yè)
    [self copyOperationObject:copyObject];

    return copyObject;
}

- (void)copyOperationObject:(id)object{
    // 空實現(xiàn)、什么操作都不做
}

@end

(2)Model類繼承自實現(xiàn)了NSCopying協(xié)議的類

#import "BaseCopyObject.h"

@interface StudentModel : BaseCopyObject

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

@end

實現(xiàn)協(xié)議中的方法

#import "StudentModel.h"

@implementation StudentModel

- (void)copyOperationObject:(StudentModel *)object{
    object.name = self.name;
    object.age = self.age;
}

@end

使用場景:

 StudentModel *stu1 = [[StudentModel alloc] init];
 stu1.name          = @"小明";
    
 StudentModel *stu2 = stu1.copy;

深拷貝與淺拷貝
(1)寫一個Class對象先匪,同樣繼承實現(xiàn)了NSCopying協(xié)議的基類

#import "BaseCopyObject.h"

@interface ClassModel : BaseCopyObject

@property(nonatomic, copy) NSString * className;
@property (nonatomic, strong) NSArray *students;

@end


#import "ClassModel.h"

@implementation ClassModel

- (void)copyOperationObject:(ClassModel *)object{

    object.className = self.className;
    object.students = self.students;
}

@end

使用場景:

ClassModel *class1 = [[ClassModel alloc] init];
class1.className   = @"班級1";
class1.students    = @[stu1, stu2];
    
ClassModel *class2 = class1.copy;
NSLog(@"%@ %@", class1, class2);
    
NSLog(@"%@", class1.students);
NSLog(@"%@", class2.students);

打印結(jié)果:

淺拷貝

如何實現(xiàn)深拷貝种吸?我們在賦值的時候使用深拷貝的方法即可:

- (void)copyOperationObject:(ClassModel *)object{

    object.className = self.className;

    // 深拷貝(完整的復制了集合里面的對象)
    object.students = [[NSArray alloc] initWithArray:self.students copyItems:YES];
}
深拷貝

所以,在我們使用NSCopying在對對象進行拷貝的時候呀非,如果對象中包含數(shù)組坚俗、集合镜盯、字典的時候一定要使用initWithArray: copyItem方法進行深拷貝。要注意的是數(shù)組猖败、集合速缆、字典里面的對象也要實現(xiàn)NSCopying協(xié)議。

4. 外觀模式的原理

外觀模式的原理
  • 外觀模式的基本原理
    不關心內(nèi)容實現(xiàn)恩闻,只用來使用

何時使用外觀模式

  • 為什么使用艺糜?
    (1)解耦合(只提供幾個簡單的接口)
    (2)簡化了操作,將繁瑣的實現(xiàn)過程封裝判呕,使用者不需要關心實現(xiàn)倦踢。

5. 如何繪制復雜的圖形

繪制一個圓送滞,繪制一個矩形侠草,兩者結(jié)合起來的方法

  • Shape類
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@interface Shape : NSObject

- (void)draw;

@end
  • 繪制圓的類
#import "Shape.h"

@interface Circle : Shape

@property (nonatomic) CGFloat radius;

- (void)draw;

@end
  • 繪制矩形類
#import "Shape.h"

@interface Rectangle : Shape

@property (nonatomic) CGFloat width;
@property (nonatomic) CGFloat height;

- (void)draw;

@end
  • 圖形創(chuàng)造器類
/*
 圖形創(chuàng)造器
 */
#import <Foundation/Foundation.h>
#import "Circle.h"
#import "Rectangle.h"

@interface ShapeMaker : NSObject

+ (void)drawCircleWithParas:(NSDictionary *)paras;

+ (void)drawCircleAndRectangle:(NSDictionary *)paras;

@end
  • 主控制器調(diào)用
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // 繪制一個圓
    [ShapeMaker drawCircleWithParas:@{@"radius": @"10"}];

    // 繪制圓 + 矩形
    [ShapeMaker drawCircleAndRectangle:@{@"a": @"2"}];


    // 繪制一個圓
    Circle *circle = [Circle new];
    circle.radius = 10.0f;
    [circle draw];

    // 繪制一個矩形
    Rectangle *restangle = [Rectangle new];
    restangle.width = 10.0f;
    restangle.height = 20.0f;
    [restangle draw];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

6. 使用Java實現(xiàn)原型模式

6.1 簡單形式的原型模式

3個角色:

(1)客戶角色:客戶類提出創(chuàng)建對象的請求。
(2)抽象原型:這是一個抽象角色犁嗅,通常使用接口或抽象類實現(xiàn)边涕。此角色給出所有具體原型類所需的接口。
(3) 具體原型:被復制的對象褂微。此角色需要實現(xiàn)抽象角色所提供的方法功蜓。

抽象原型代碼:

package com.zsw.prototype;
public interface Prototype extends Cloneable {
    public Prototype clone();
 
    public void setName(String name);
    public String getName();
}

具體原型代碼:


package com.zsw.prototype;
 
public class ConcretePrototype implements Prototype {
    private String name;
    public String getName() {
       return name;
    }
    public void setName(String name) {
       this.name = name;
    }
    public Prototype clone(){
       try {
           return (Prototype) super.clone();
       } catch (CloneNotSupportedException e) {
           e.printStackTrace();
           return null;
       }
    }
}

客戶端原型代碼:

package com.zsw.prototype;
public class Client {
    public static void main(String[] args) {
       Prototype prototype = new ConcretePrototype();
       prototype.setName("shangwu");
       Prototype p = prototype.clone();
       System.out.println(prototype == p);
       System.out.println(prototype.getName());
       System.out.println(p.getName());
    }
}

6.2 登記形式的原始模型模式

它有如下角色:
(1)客戶端角色:客戶端向管理員提出創(chuàng)建對象的請求。
(2)抽象原型角色:這是一個抽象角色宠蚂,通常由一個接口或抽象類實現(xiàn)式撼。此角色給出所有具體原型類所需的接口。
(3)具體原型角色:被復制的對象求厕,需要實現(xiàn)抽象原型角色定義的方法著隆。
(4)原型管理器角色:創(chuàng)建具體原型類的對象,并記錄每一個被創(chuàng)建的對象呀癣。

6.3 復制簡歷來實現(xiàn)淺復制

具體原型類型:

package com.zsw.prototype.resume;
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private WorkExperience work;
   
    public Resume(String name){
       this.name = name;
       work = new WorkExperience();
    }
   
    //設置個人信息
    public void setPersonalInfo(String sex,int age){
       this.sex = sex;
       this.age = age;
    }
   
    //設置工作經(jīng)歷
    public void setWorkExperience(String workDate,String company){
       work.setWorkDate(workDate);
       work.setCompany(company);
    }
   
    public void display(){
       System.out.println("姓名:"+name+"性別:"+sex+"年齡:" + age);
       System.out.println("工作日期:"+work.getWorkDate()+"工作公司:"+work.getCompany());
    }
   
    public Object clone(){
       try {
           return super.clone();
       } catch (CloneNotSupportedException e) {
           e.printStackTrace();
           return null;
       }
    }
}

工作經(jīng)歷代碼:

package com.zsw.prototype.resume;
 
public class WorkExperience {
    private String workDate;
 
    private String company;
    public String getCompany() {
       return company;
    }
 
    public void setCompany(String company) {
       this.company = company;
    }
 
    public String getWorkDate() {
       return workDate;
    }
 
    public void setWorkDate(String workDate) {
       this.workDate = workDate;
    }
}

客戶端:

package com.zsw.prototype.resume;
 
public class Client {
 
    public static void main(String[] args) {
       Resume a = new Resume("大鳥");
       a.setPersonalInfo("男",29);
       a.setWorkExperience("1998-2000", "XXXX-公司");
      
       Resume b = (Resume)a.clone();
       b.setWorkExperience("1998 - 2006", "YY企業(yè)");
      
       Resume c = (Resume)a.clone();
       c.setWorkExperience("1998-2003", "ZZ企業(yè)");
      
       a.display();
       b.display();
       c.display();
    }
}

輸出結(jié)果:

姓名:大鳥性別:男年齡:29
工作日期:1998-2003工作公司:ZZ企業(yè)
姓名:大鳥性別:男年齡:29
工作日期:1998-2003工作公司:ZZ企業(yè)
姓名:大鳥性別:男年齡:29
工作日期:1998-2003工作公司:ZZ企業(yè)

此處的工作日期美浦,和工作公司都是一樣,a项栏、b浦辨、c三個對象都引用工作經(jīng)歷對象,但同時都引用了最后一個對象沼沈,所以三次輸出的數(shù)據(jù)都相同流酬。這就是淺復制的現(xiàn)象,只復制所考慮的對象列另,而不復制它的引用對象芽腾。使用深復制可以解決此問題。

6.4 對簡歷進行改造访递,使用深復制來實現(xiàn)

工作經(jīng)歷類:

package com.zsw.prototype.resume;
 
public class WorkExperience implements Cloneable {
    private String workDate;
 
    private String company;
    public String getCompany() {
       return company;
    }
 
    public void setCompany(String company) {
       this.company = company;
    }
 
    public String getWorkDate() {
       return workDate;
    }
 
    public void setWorkDate(String workDate) {
       this.workDate = workDate;
    }
   
    public Object clone(){
       try {
           return super.clone();
       } catch (CloneNotSupportedException e) {        
           e.printStackTrace();
           return null;
       }
    }
}

簡歷源碼類:

package com.zsw.prototype.resume;
 
public class Resume implements Cloneable {
 
    private String name;
    private String sex;
    private int age;
    private WorkExperience work;
   
    public Resume(String name){
       this.name = name;
       work = new WorkExperience();
    }
   
    //設置個人信息
    public void setPersonalInfo(String sex,int age){
       this.sex = sex;
       this.age = age;
    }
   
    //設置工作經(jīng)歷
    public void setWorkExperience(String workDate,String company){
       work.setWorkDate(workDate);
       work.setCompany(company);
    }
   
    public void display(){
       System.out.println("姓名:"+name+"性別:"+sex+"年齡:" + age);
       System.out.println("工作日期:"+work.getWorkDate()+"工作公司:"+work.getCompany());
    }
   
    public Object clone(){
       try {
           Resume resume = (Resume) super.clone();
           resume.work = (WorkExperience) this.work.clone();
           return resume;
       } catch (CloneNotSupportedException e) {
           e.printStackTrace();
           return null;
       }
    }
}

運行結(jié)果如下:

姓名:大鳥性別:男年齡:29
工作日期:1998-2000工作公司:XXXX-公司
姓名:大鳥性別:男年齡:29
工作日期:1998 - 2006工作公司:YY企業(yè)
姓名:大鳥性別:男年齡:29
工作日期:1998-2003工作公司:ZZ企業(yè)

7. 優(yōu)缺點

優(yōu)點:
(1)原型模式的有點和缺點
(2) 原型模式允許動態(tài)的添加或減少產(chǎn)品類晦嵌。
(3) 原始模型模式提供簡單的創(chuàng)建結(jié)構(gòu)。
(4)原始模型模式具有給一個應用軟件動態(tài)加載新的功能能力。
(5) 產(chǎn)品類不需要非得有任何事先確定的等級結(jié)構(gòu)惭载。

缺點:原始模型模式的最主要的缺點是每一個類都必須配備一個克隆的方法旱函。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市描滔,隨后出現(xiàn)的幾起案子棒妨,更是在濱河造成了極大的恐慌,老刑警劉巖含长,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件券腔,死亡現(xiàn)場離奇詭異,居然都是意外死亡拘泞,警方通過查閱死者的電腦和手機纷纫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陪腌,“玉大人辱魁,你說我怎么就攤上這事∈迹” “怎么了染簇?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長强岸。 經(jīng)常有香客問我锻弓,道長,這世上最難降的妖魔是什么蝌箍? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任青灼,我火速辦了婚禮,結(jié)果婚禮上十绑,老公的妹妹穿的比我還像新娘聚至。我一直安慰自己,他們只是感情好本橙,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布扳躬。 她就那樣靜靜地躺著,像睡著了一般甚亭。 火紅的嫁衣襯著肌膚如雪贷币。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天亏狰,我揣著相機與錄音役纹,去河邊找鬼。 笑死暇唾,一個胖子當著我的面吹牛促脉,可吹牛的內(nèi)容都是我干的辰斋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼瘸味,長吁一口氣:“原來是場噩夢啊……” “哼宫仗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起旁仿,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤藕夫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后枯冈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毅贮,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年尘奏,在試婚紗的時候發(fā)現(xiàn)自己被綠了滩褥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡罪既,死狀恐怖铸题,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琢感,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布探熔,位于F島的核電站驹针,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诀艰。R本人自食惡果不足惜柬甥,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望其垄。 院中可真熱鬧苛蒲,春花似錦、人聲如沸绿满。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喇颁。三九已至漏健,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間橘霎,已是汗流浹背蔫浆。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留姐叁,地道東北人瓦盛。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓洗显,卻偏偏與公主長得像,于是被迫代替她去往敵國和親原环。 傳聞我的和親對象是個殘疾皇子墙懂,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容