Skip to main content

Swift Example

/// Flyweight Design Pattern
///
/// Intent: Lets you fit more objects into the available amount of RAM by
/// sharing common parts of state between multiple objects, instead of keeping
/// all of the data in each object.

import XCTest

/// The Flyweight stores a common portion of the state (also called intrinsic
/// state) that belongs to multiple real business entities. The Flyweight
/// accepts the rest of the state (extrinsic state, unique for each entity) via
/// its method parameters.
class Flyweight {

private let sharedState: [String]

init(sharedState: [String]) {
self.sharedState = sharedState
}

func operation(uniqueState: [String]) {
print("Flyweight: Displaying shared (\(sharedState)) and unique (\(uniqueState) state.\n")
}
}

/// The Flyweight Factory creates and manages the Flyweight objects. It ensures
/// that flyweights are shared correctly. When the client requests a flyweight,
/// the factory either returns an existing instance or creates a new one, if it
/// doesn't exist yet.
class FlyweightFactory {

private var flyweights: [String: Flyweight]

init(states: [[String]]) {

var flyweights = [String: Flyweight]()

for state in states {
flyweights[state.key] = Flyweight(sharedState: state)
}

self.flyweights = flyweights
}

/// Returns an existing Flyweight with a given state or creates a new one.
func flyweight(for state: [String]) -> Flyweight {

let key = state.key

guard let foundFlyweight = flyweights[key] else {

print("FlyweightFactory: Can't find a flyweight, creating new one.\n")
let flyweight = Flyweight(sharedState: state)
flyweights.updateValue(flyweight, forKey: key)
return flyweight
}
print("FlyweightFactory: Reusing existing flyweight.\n")
return foundFlyweight
}

func printFlyweights() {
print("FlyweightFactory: I have \(flyweights.count) flyweights:\n")
for item in flyweights {
print(item.key)
}
}
}

extension Array where Element == String {

/// Returns a Flyweight's string hash for a given state.
var key: String {
return self.joined()
}
}


class FlyweightConceptual: XCTestCase {

func testFlyweight() {

/// The client code usually creates a bunch of pre-populated flyweights
/// in the initialization stage of the application.

let factory = FlyweightFactory(states:
[
["Chevrolet", "Camaro2018", "pink"],
["Mercedes Benz", "C300", "black"],
["Mercedes Benz", "C500", "red"],
["BMW", "M5", "red"],
["BMW", "X6", "white"]
])

factory.printFlyweights()

/// ...

addCarToPoliceDatabase(factory,
"CL234IR",
"James Doe",
"BMW",
"M5",
"red")

addCarToPoliceDatabase(factory,
"CL234IR",
"James Doe",
"BMW",
"X1",
"red")

factory.printFlyweights()
}

func addCarToPoliceDatabase(
_ factory: FlyweightFactory,
_ plates: String,
_ owner: String,
_ brand: String,
_ model: String,
_ color: String) {

print("Client: Adding a car to database.\n")

let flyweight = factory.flyweight(for: [brand, model, color])

/// The client code either stores or calculates extrinsic state and
/// passes it to the flyweight's methods.
flyweight.operation(uniqueState: [plates, owner])
}
}
FlyweightFactory: I have 5 flyweights:

Mercedes BenzC500red
ChevroletCamaro2018pink
Mercedes BenzC300black
BMWX6white
BMWM5red
Client: Adding a car to database.

FlyweightFactory: Reusing existing flyweight.

Flyweight: Displaying shared (["BMW", "M5", "red"]) and unique (["CL234IR", "James Doe"] state.

Client: Adding a car to database.

FlyweightFactory: Can't find a flyweight, creating new one.

Flyweight: Displaying shared (["BMW", "X1", "red"]) and unique (["CL234IR", "James Doe"] state.

FlyweightFactory: I have 6 flyweights:

Mercedes BenzC500red
BMWX1red
ChevroletCamaro2018pink
Mercedes BenzC300black
BMWX6white
BMWM5red