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)惭载。
缺點:原始模型模式的最主要的缺點是每一個類都必須配備一個克隆的方法旱函。