Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 原型模式(Prototype pattern)

原型模式(Prototype pattern)

編輯:關於Android編程

定義   原型模式是一種非常簡單的是模式,屬於創建型設計模式的一種。原型模式(Prototype)即應用於“復制”操作的模式,此模式最初定義在《設計模式》(Addison-Wesley,1994),定義為:“使用原型實例指定創建對象的種類,並通過復制這個原型創建新的對象”。簡單來理解就是根據這個原型創建新的對象,這種創建是指深復制,得到一份新的內存資源,而不是一個新的指針引用。我們都知道,創建型模式一般是用來創建一個新的對象,然後我們使用這個對象完成一些對象的操作,我們通過原型模式可以快速的創建一個對象而不需要提供專門的new()操作就可以快速完成對象的創建,這無疑對於快速的創建一個新的對象是一種非常有效的方式。   結構   原型模式結構如下頁上圖所示:   客戶(Client):使用原型對象的客戶程序,客服端知道抽象的Prototype類 抽象原型(Prototype):規定了具體原型對象必須實現的接口(如果要提供深拷貝,則必須具有實現clone的規定) 具體原型(ConcretePrototype):從抽象原型派生而來,是客戶程序使用的對象,即被復制的對象。此角色需要實現抽象原型角色所要求的接口。   Prototype類中包括一個Clone方法,Client知道Prototype,調用其復制方法clone即可得到多個實例,不需要手工去創建這些實例。 Prototype聲明了賦值自身的接口,作為Prototype的子類,ConcretePrototype實現了Concrete復制自身的clone操作,這裡的客戶端通過請求原型復制其自身,創建一個新的對象。   解決什麼問題?   1:解決了每次創建新的對象,都需要alloc init,這樣就造成了代碼要直接訪問具體的類,也就增加了代碼的耦合度,編碼了使用標准方式創建新對象的固有代價。 2:避免創建工廠類的子類(例如抽象工廠模式) 3:通過copy能夠保存對象當時的狀態   適用性   原型模式的主要思想是基於現有的對象克隆一個新的對象出來,一般是由對象的內部提供克隆的方法,通過該方法返回一個對象的副本,特別是以下的幾個場景的時候,可能使用原型模式更簡單也效率更高。   1)需要創建的對象應獨立於其類型與創建方式。 2)當要實例化的類是在運行時刻指定,例如,通過動態裝載 3)為了避免創建一個與產品類層次平行的工廠類層次時,不通過工廠方法或者抽象工廠來控制產品的創建過程,想要直接復制對象 4)當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。(也就是當我們在處理一些對象比較簡單,並且對象之間的區別很小,可能只是很固定的幾個屬性不同的時候,可能我們使用原型模式更合適) 5)類不容易創建,比如每個組件可把其他組件作為子節點的組合對象,復制已有的組合對象並對副本進行修改會更容易   實現原理   1:Swift/Objective C並不支持抽象基類或者抽象方法。但是可以使用協議類似定義一個抽象的“基類”,定義通用的屬性,方法,以及復制方法。 2:具體的類負責實現復制方法,以及公用的方法。 3:通過抽象基類的接口創建對象   深復制與淺復制   非指針型實例變量沒有淺復制與深復制之分,像布爾型、整型、浮點型。淺復制與深復制是針對指針型實例變量說的,淺復制就只是復制了指針到副本中,原始對象與副本共享內存數據;深復制就是把內存的資源也復制了,原始對象和副本分別對應自己的內存數據。   如果對象有個指針型成員指向內存的某個資源,那麼如何復制這個對象呢?你會只是復制指針的值傳給副本的新對象嗎?指針只是存儲內存中資源地址的占位符。在復制操作中,如果只是將指針復制給新對象(副本),那麼底層的資源實際上仍然由兩個實例在共享,如下圖: 復制ConcretePrototype的場景,只復制了資源指針,實際的資源並沒有復制   在ConcretePrototype的clone操作中,把資源指針的值復制到新的副本,盡管ConcretePrototype的實例生成了一個同類型的實例作為其副本,但兩個實例的指針仍然指向內存中的同一資源。因此只復制了指針值而不是實際資源,這稱為淺復制。   那麼什麼是深復制呢?深復制是指不僅是賦值指針值,還賦值指針所指向的資源,同一個clone操作的深復制如下圖:clone操作不只是簡單的復制資源指針,還要生成內存中實際的資源的真正副本。因此副本對象的指針指向了內存中不同位置的同一資源(內容)的副本。 與上圖類似,但是復制過程中對內存中的實際資源進行了賦值   使用Cocoa Touch框架的對象復制   Cocoa Touch框架為NSObject的派生類提供了實現深復制的協議,NSObject的子類需要實現NSCoping協議- (id)copyWithZone:(NSZone *)zone.NSObject有一個實例方法(id)copy,這個方法默認調用了[self copyWithZone:nil].對於采納了NSCoping協議的子類,需要實現這個方法,否則將引發異常。在iOS中,這個方法保持新的副本對象,然後將其返回。此方法的調用者要負責釋放返回的對象。   多數情況下,深復制的實現看起來並不分復雜,其思路是復制必需的成員變量與資源,傳給此類的新實例,然後返回這個新實例。在Objective-C中使用原型模式, 首先要遵循NSCoping協議(OC中一些內置類遵循該協議, 例如NSArray, NSMutableArray等)。剛才我們提到了深復制, 如下圖所示:   具體Demo,直接看代碼
struct Part{
    var name: String
    var price: Double
    var brand: String
    
    init(name: String,price: Double, brand: String) {
        self.name = name
        self.price = price
        self.brand = brand
    }
}

struct Service {
    var name: String
    var laborDurationInMinutes: Int
    
    init(name: String, laborDurationInMinutes: Int) {
        self.name = name
        self.laborDurationInMinutes = laborDurationInMinutes
    }
}

struct Mechanic {
    static var id: Int = 0
    var id: Int
    var name: String
    
    init(name: String) {
        self.name = name
        Mechanic.id += 1
        self.id = Mechanic.id 
    }
}
由上可知,Part是一個簡單的結構體,擁有name,price,brand等屬性,並在初始化期間設置值,Service也是相同的模式,在初始化期間設置name和laborDurationInMinutes兩個基本屬性,Mechanic也是一樣,擁有基本屬性並初始化。

 

 

class CorporateQuote{
    var services: [Service]
    var price: Double
    var parts: [Part]
    var numberOfCars: Int
    var startTime: NSDate?
    var mechanics: [Mechanic]
    var client: String?
    var address: String?
    
    init(services: [Service],
         price: Double,
         parts: [Part],
         numberOfCars: Int,
         mechanics: [Mechanic]) {
        
        self.services = services
        self.price = price
        self.parts = parts
        self.numberOfCars = numberOfCars
        self.mechanics = mechanics
        
    }
    
    func clone() -> CorporateQuote {
        return CorporateQuote(services: self.services,
                              price: self.price,
                              parts: self.parts,
                              numberOfCars: self.numberOfCars,
                              mechanics: self.mechanics)
    }
}

由上面代碼可知,我們的CorporateQuote類中有services、price、parts、numberOf、startTime、Carsmechanics、client、address等屬性,並且在初始化方法中,我們設置了services、price、parts、numberOf、Carsmechanics等屬性。並且擁有一個clone方法來獲取CorporateQuote類的實例對象,並且clone方法所使用的參數跟之前實例使用參數一樣,這裡就是實現了復制當前的CorporateQuote。但是這對我們有什麼用處呢?下面一起看看測試代碼:

func testCorporateQuote(){
        var steve = Mechanic (name: "Steve Brimington")
        var mike = Mechanic(name: "Mike Fulton")
        var ali = Mechanic (name: "Ali Belevue")
        
        var corporateMechanics = [steve, mike, ali]
        
        var brakePadReplacement = Service(name: "Brake Pad Replacement", laborDurationInMinutes: 100)
        var oilChange = Service(name: "Oil Change", laborDurationInMinutes: 65)
        var rotateTires = Service(name: "Roate Tires", laborDurationInMinutes: 45)
        
        var corporateServices = [brakePadReplacement, oilChange, rotateTires]
        
        var corporateParts = [Part(name: "Brake Pads Front", price: 25.65, brand: "ACME Pads"),
                     Part(name: "Filter", price: 8.99, brand: "ACME Pads"),
                     Part(name: "Synthetic Oil", price: 15.19, brand: "ACME Pads"),
                     Part(name: "Brake Pads Rear", price: 32.65, brand: "ACME Pads"),
                     Part(name: "Air Freshners", price: 3.65, brand: "ACME Pads")]
}
首先在testCorporateQuote函數中添加了如上的代碼,代碼簡單,初始化了相關結構體並使用數組存儲同類變量。如果我們需要更多這樣的數據,我們可以模仿當前代碼例子很輕松的編寫更多的代碼部分。但是假設在上面代碼中,我們共有了mechanics、services、parts等屬性,而且創建corporateMechanics和corporateServices以及corporateParts的代價非常昂貴並且耗時,你是願意只做一次創建操作還是操作多次呢?肯定是一次創建咯,下面我們來生成一個prototype corporateQuote,在測試函數corporateParts下添加如下代碼:
 var prototypedCorporeateQuote = CorporateQuote(services: corporateServices,
                                                       price: 1488.99,
                                                       parts: corporateParts,
                                                       numberOfCars: 20,
                                                       mechanics: corporateMechanics)
  
 var googleQuote = prototypedCorporeateQuote.clone()
     googleQuote.client = "Google"
     googleQuote.startTime = NSDate(timeIntervalSinceNow: 0)
     googleQuote.address = "1600 Amphitheatre Pkwy, Mountain View"
           
 var facebookQuote = prototypedCorporeateQuote.clone()
     facebookQuote.client = "Facebook"
     facebookQuote.startTime =  NSDate(timeIntervalSinceNow: 1)
     facebookQuote.address = "1 Hacker Way, Menlo Park"
        
 var microsoftQuote = prototypedCorporeateQuote.clone()
     microsoftQuote.client = "Microsoft"
     microsoftQuote.startTime = NSDate(timeIntervalSinceNow: 2)
     microsoftQuote.address = "1085 La Avenida St, Mountain View"
現在我們有了prototypedCorporeateQuote變量,我們可以使用它的clone方法創建更多的其他corporate quotes並且擁有著相同的配置。他們將擁有相同的services、price、parts、numberOf、Carsmechanics。所有的操作都不涉及再次創建任意的這些值。而且我們僅僅是調用了clone()方法,就獲得了復雜和昂貴的預配置CorporateQuote類的實例。這看起來非常的棒,這時我們能夠修改每一個corporate quote,指定具體的客戶(client)、日期、地址等信息了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved