 
                Легковес на Swift
Легковес — это структурный паттерн, который экономит память, благодаря разделению общего состояния, вынесенного в один объект, между множеством объектов.
Легковес позволяет экономить память, кешируя одинаковые данные, используемые в разных объектах.
Сложность:
Популярность:
Применимость: Весь смысл использования Легковеса — в экономии памяти. Поэтому, если в приложении нет такой проблемы, то вы вряд ли найдёте там примеры Легковеса.
Признаки применения паттерна: Легковес можно определить по создающим методам класса, которые возвращают закешированные объекты, вместо создания новых.
Концептуальный пример
Этот пример показывает структуру паттерна Легковес, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
После ознакомления со структурой, вам будет легче воспринимать второй пример, который рассматривает реальный случай использования паттерна в мире Swift.
Example.swift: Пример структуры паттерна
import XCTest
/// Легковес хранит общую часть состояния (также называемую внутренним
/// состоянием), которая принадлежит нескольким реальным бизнес-объектам.
/// Легковес принимает  оставшуюся часть состояния (внешнее состояние,
/// уникальное для каждого объекта)  через его параметры метода.
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")
    }
}
/// Фабрика Легковесов создает объекты-Легковесы и управляет ими. Она
/// обеспечивает правильное разделение легковесов. Когда клиент запрашивает
/// легковес, фабрика либо возвращает существующий экземпляр, либо создает
/// новый, если он ещё не существует.
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
    }
    /// Возвращает существующий Легковес с заданным состоянием или создает
    /// новый.
    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 {
    /// Возвращает хеш строки Легковеса для данного состояния.
    var key: String {
        return self.joined()
    }
}
class FlyweightConceptual: XCTestCase {
    func testFlyweight() {
        /// Клиентский код обычно создает кучу предварительно заполненных
        /// легковесов на этапе инициализации приложения.
        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])
        /// Клиентский код либо сохраняет, либо вычисляет внешнее состояние и
        /// передает его методам легковеса.
        flyweight.operation(uniqueState: [plates, owner])
    }
}
Output.txt: Результат выполнения
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
Пример из реальной жизни
Example.swift: Пример из реальной жизни
import XCTest
import UIKit
class FlyweightRealWorld: XCTestCase {
    func testFlyweightRealWorld() {
        let maineCoon = Animal(name: "Maine Coon",
                               country: "USA",
                               type: .cat)
        let sphynx = Animal(name: "Sphynx",
                            country: "Egypt",
                            type: .cat)
        let bulldog = Animal(name: "Bulldog",
                             country: "England",
                             type: .dog)
        print("Client: I created a number of objects to display")
        /// Displaying objects for the 1-st time.
        print("Client: Let's show animals for the 1st time\n")
        display(animals: [maineCoon, sphynx, bulldog])
        /// Displaying objects for the 2-nd time.
        ///
        /// Note: The cached appearance object will be reused this time.
        print("\nClient: I have a new dog, let's show it the same way!\n")
        let germanShepherd = Animal(name: "German Shepherd",
                              country: "Germany",
                              type: .dog)
        display(animals: [germanShepherd])
    }
}
extension FlyweightRealWorld {
    func display(animals: [Animal]) {
        let cells = loadCells(count: animals.count)
        for index in 0..<animals.count {
            cells[index].update(with: animals[index])
        }
        /// Using cells...
    }
    func loadCells(count: Int) -> [Cell] {
        /// Emulates behavior of a table/collection view.
        return Array(repeating: Cell(), count: count)
    }
}
enum Type: String {
    case cat
    case dog
}
class Cell {
    private var animal: Animal?
    func update(with animal: Animal) {
        self.animal = animal
        let type = animal.type.rawValue
        let photos = "photos \(animal.appearance.photos.count)"
        print("Cell: Updating an appearance of a \(type)-cell: \(photos)\n")
    }
}
struct Animal: Equatable {
    /// This is an external context that contains specific values and an object
    /// with a common state.
    ///
    /// Note: The object of appearance will be lazily created when it is needed
    let name: String
    let country: String
    let type: Type
    var appearance: Appearance {
        return AppearanceFactory.appearance(for: type)
    }
}
struct Appearance: Equatable {
    /// This object contains the predefined appearance for each cell.
    let photos: [UIImage]
    let backgroundColor: UIColor
}
extension Animal: CustomStringConvertible {
    var description: String {
        return "\(name), \(country), \(type.rawValue) + \(appearance.description)"
    }
}
extension Appearance: CustomStringConvertible {
    var description: String {
        return "photos: \(photos.count), \(backgroundColor)"
    }
}
class AppearanceFactory {
    private static var cache = [Type: Appearance]()
    static func appearance(for key: Type) -> Appearance {
        guard cache[key] == nil else {
            print("AppearanceFactory: Reusing an existing \(key.rawValue)-appearance.")
            return cache[key]!
        }
        print("AppearanceFactory: Can't find a cached \(key.rawValue)-object, creating a new one.")
        switch key {
        case .cat:
            cache[key] = catInfo
        case .dog:
            cache[key] = dogInfo
        }
        return cache[key]!
    }
}
extension AppearanceFactory {
    private static var catInfo: Appearance {
        return Appearance(photos: [UIImage()], backgroundColor: .red)
    }
    private static var dogInfo: Appearance {
        return Appearance(photos: [UIImage(), UIImage()], backgroundColor: .blue)
    }
}
Output.txt: Результат выполнения
Client: I created a number of objects to display
Client: Let's show animals for the 1st time
AppearanceFactory: Can't find a cached cat-object, creating a new one.
Cell: Updating an appearance of a cat-cell: photos 1
AppearanceFactory: Reusing an existing cat-appearance.
Cell: Updating an appearance of a cat-cell: photos 1
AppearanceFactory: Can't find a cached dog-object, creating a new one.
Cell: Updating an appearance of a dog-cell: photos 2
Client: I have a new dog, let's show it the same way!
AppearanceFactory: Reusing an existing dog-appearance.
Cell: Updating an appearance of a dog-cell: photos 2