设计模式:享元模式

享元模式(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

相关文章
相关标签/搜索