設計模式:享元模式

享元模式(Flyweight Pattern)主要用於減小建立對象的數量,以減小內存佔用和提升性能。這種類型的設計模式屬於結構型模式,它提供了減小對象數量從而改善應用所需的對象結構的方式。html

簡介

重複字母

如上圖所示,「hello world」中「L」出現了3次,爲了節省開始,咱們顯然不須要重複建立三次「L」,享元模式正是爲了應對此類狀況的一種設計模式。swift

享元模式主要解決的問題:在有大量對象時,有可能會形成內存溢出,咱們把其中共同的部分抽象出來,若是有相同的業務請求,直接返回在內存中已有的對象,避免從新建立。設計模式

應用flyweight設計模式以前,咱們須要考慮如下因素:bash

1.應用程序要建立的對象數量很大;函數

2.對象建立在內存上很重要,也可能很耗時;性能

3.對象屬性能夠分爲內在屬性和外在屬性,對象的外在屬性應該由客戶端程序定義。ui

結構

首先須要將Object的屬性劃分爲內部屬性和外部屬性:spa

內部屬性:可共享狀態,享元對象分組依據;設計

外部屬性:不共享的狀態,每個具體享元對象不一樣。code

享元模式中主要包含如下三個構成:

1.抽象享元(FlyWeight):給出抽象接口,規定全部具體享元角色須要實現的方法;

2.具體享元(ConcreteFlyWeight):實現抽象享元所規定出的接口;存儲內部狀態;

3.不共享具體工廠(UnsharedConcreteFlyweight):Flyweight接口使共享成爲可能,但它並不強制共享。在Flyweight對象結構的某些層次,UnsharedConcreteFlyweight對象一般將ConcreteFlyweight對象做爲子節點;

4.享元工廠(FlyWeightFactory):建立和管理享元角色,保證享元對象可被共享;主要經過HashMap來實現。

其結構以下圖所示:

FlyWeight結構

示例

以軍隊士兵爲例,下面是一個享元模式的示例: 軍隊中的士兵能夠分爲不少種類,例如步兵,偵察兵等等; 在這種狀況下,能夠按照種類對士兵進行分組,同種類士兵共享相同的內部狀態。在建立士兵時,與士兵種類相關的內部狀態不會被重複的建立,而每一個士兵的外部狀態(在本示例中是士兵的位置)則各不相同。

protocol Soldier {
  func render(from location: CGPoint, to newLocation: CGPoint)
}
複製代碼

Soldier爲抽象享元,規定出士兵所須要實現的方法。

class Infantry: Soldier {
  
  private let modelData: Data
  
  init(modelData: Data) {
    self.modelData = modelData
  }
  
  func render(from location: CGPoint, to newLocation: CGPoint) {
    // Remove rendering from original location
    // Graphically render at new location
  }
}
複製代碼

Infantry爲具體享元,其中modelData即爲可被共享的內部狀態,被種類爲Infantry的士兵共享。

class SoldierClient {
  
  // 1
  var currentLocation: CGPoint
  
  // 2
  let soldier: Soldier
  
  init(currentLocation: CGPoint, soldier: Soldier) {
    self.currentLocation = currentLocation
    self.soldier = soldier
  }
  
  // 3
  func moveSoldier(to nextLocation: CGPoint) {
    soldier.render(from: currentLocation, to: nextLocation)
    currentLocation = nextLocation
  }
}
複製代碼

SoldierClient爲具體生成的每個士兵對象,其中保存了外部狀態,也就是位置:currentLocation;同時也保存了享元的inference,這樣,每次生成士兵對象時,可被共享的享元部分不會被重複生成。

class SoldierFactory {
  
  // 1
  enum SoldierType {
    case infantry
  }
  
  // 2
  private var availableSoldiers =  [SoldierType: Soldier]()
  
  // 3
  static let sharedInstance = SoldierFactory()
  
  private init(){}
  
  private func createSoldier(of type: SoldierType) -> Soldier {
    switch type {
    case .infantry:
      let infantry = Infantry(modelData: Data())
      availableSoldiers[type] = infantry
      return infantry
    }
  }
  
  // 4
  func getSoldier(type: SoldierType) -> Soldier {
    if let soldier = availableSoldiers[type] {
      return soldier
    } else {
      let soldier = createSoldier(of: type)
      return soldier
    }
  }
}
複製代碼

SoldierFactory爲享元工廠。享元工廠負責生成和管理享元,採用hashmap以保證同類的享元不會被重複生成。每次生成享元時,若是次類享元已存在,直接返回存在的享元,若是不存在,則生成新的。

let soldierFactory = SoldierFactory.sharedInstance
let infantry = soldierFactory.getSoldier(type: .infantry)
let firstSoldier = SoldierClient(currentLocation: CGPoint.zero, soldier: infantry)
let secondSoldier = SoldierClient(currentLocation: CGPoint(x: 5, y: 10), soldier: infantry)

firstSoldier.moveSoldier(to: CGPoint(x: 1, y: 5))
複製代碼

在主函數中,首先經過SoldierFactory生成種類爲infantry的享元,接下來生成士兵。 firstSoldier,secondSoldier種類都爲infantry,此時享元infantry不會被重複的生成,被士兵共享;而士兵能夠具備不一樣的外部狀態(位置)。

經過上面的例子能夠看出,在須要大量重複生成對象時,經過享元模式,能夠實現分組後可被共享的狀態不被重複生成,從而下降內存消耗,提升效率。

總結

優勢:大大減小對象的建立,下降系統的內存,使效率提升

缺點:須要分離出外部狀態和內部狀態,提升了系統的複雜度

參考:

Article by Guo Dong

相關文章
相關標籤/搜索