
Bridge を Swift で
Bridge は、 構造に関するデザインパターンの一つで、 ビジネス・ロジックや巨大なクラスを独立して開発可能なクラス階層に分割します。
階層の一つ (抽象化と呼ばれる) は、 二つ目の階層 (実装) のオブジェクトへの参照を持ちます。 抽象化階層は、 その呼び出しのいくつか (場合によっては大多数) を実装階層のオブジェクトに委任します。 すべての実装は、 共通のインターフェースを持っているので、 抽象化の中で入れ替え可能です。
複雑度:
人気度:
使用例: Bridge パターンは、 クロス・プラットフォーム・アプリを扱う時、 複数の種類のデータベース・サーバーをサポートする時、 あるいはある種の API プロバイダー (クラウド・プラットフォーム、 ソーシャル・ネットワークなど) を複数利用したい場合に特に便利です。
見つけ方: Bridge は、 制御するものと、 それが依存するいくつかの異なるプラットフォームとが明らかに分かれていることから識別できます。
以下の例は Swift Playgroundsで利用できます。
Playgroundバージョンを作成してくれた Alejandro Mohamadに感謝します。
概念的な例
この例は、 Bridge デザインパターンの構造を説明するためのものです。 以下の質問に答えることを目的としています:
- どういうクラスからできているか?
- それぞれのクラスの役割は?
- パターンの要素同士はどう関係しているのか?
ここでパターンの構造を学んだ後だと、 これに続く、 現実世界の Swift でのユースケースが理解しやすくなります。
Example.swift: 概念的な例
import XCTest /// The Abstraction defines the interface for the "control" part of the two /// class hierarchies. It holds a reference to an object from the Implementation /// hierarchy and delegates all of the real work to this object. class Abstraction { fileprivate var implementation: Implementation init(_ implementation: Implementation) { self.implementation = implementation } func operation() -> String { let operation = implementation.operationImplementation() return "Abstraction: Base operation with:\n" + operation } } /// You can extend the Abstraction without changing the Implementation classes. class ExtendedAbstraction: Abstraction { override func operation() -> String { let operation = implementation.operationImplementation() return "ExtendedAbstraction: Extended operation with:\n" + operation } } /// The Implementation defines the interface for all implementation classes. It /// doesn't have to match the Abstraction's interface. In fact, the two /// interfaces can be entirely different. Typically the Implementation interface /// provides only primitive operations, while the Abstraction defines higher- /// level operations based on those primitives. protocol Implementation { func operationImplementation() -> String } /// Each Concrete Implementation corresponds to a specific platform and /// implements the Implementation interface using that platform's API. class ConcreteImplementationA: Implementation { func operationImplementation() -> String { return "ConcreteImplementationA: Here's the result on the platform A.\n" } } class ConcreteImplementationB: Implementation { func operationImplementation() -> String { return "ConcreteImplementationB: Here's the result on the platform B\n" } } /// Except for the initialization phase, where an Abstraction object gets linked /// with a specific Implementation object, the client code should only depend on /// the Abstraction class. This way the client code can support any abstraction- /// implementation combination. class Client { // ... static func someClientCode(abstraction: Abstraction) { print(abstraction.operation()) } // ... } /// Let's see how it all works together. class BridgeConceptual: XCTestCase { func testBridgeConceptual() { // The client code should be able to work with any pre-configured // abstraction-implementation combination. let implementation = ConcreteImplementationA() Client.someClientCode(abstraction: Abstraction(implementation)) let concreteImplementation = ConcreteImplementationB() Client.someClientCode(abstraction: ExtendedAbstraction(concreteImplementation)) } }
Output.txt: 実行結果
Abstraction: Base operation with: ConcreteImplementationA: Here's the result on the platform A ExtendedAbstraction: Extended operation with: ConcreteImplementationB: Here's the result on the platform B
現実的な例
Example.swift: 現実的な例
import XCTest private class BridgeRealWorld: XCTestCase { func testBridgeRealWorld() { print("Client: Pushing Photo View Controller...") push(PhotoViewController()) print() print("Client: Pushing Feed View Controller...") push(FeedViewController()) } func push(_ container: SharingSupportable) { let instagram = InstagramSharingService() let facebook = FaceBookSharingService() container.accept(service: instagram) container.update(content: foodModel) container.accept(service: facebook) container.update(content: foodModel) } var foodModel: Content { return FoodDomainModel(title: "This food is so various and delicious!", images: [UIImage(), UIImage()], calories: 47) } } private protocol SharingSupportable { /// Abstraction func accept(service: SharingService) func update(content: Content) } class BaseViewController: UIViewController, SharingSupportable { fileprivate var shareService: SharingService? func update(content: Content) { /// ...updating UI and showing a content... /// ... /// ... then, a user will choose a content and trigger an event print("\(description): User selected a \(content) to share") /// ... shareService?.share(content: content) } func accept(service: SharingService) { shareService = service } } class PhotoViewController: BaseViewController { /// Custom UI and features override var description: String { return "PhotoViewController" } } class FeedViewController: BaseViewController { /// Custom UI and features override var description: String { return "FeedViewController" } } protocol SharingService { /// Implementation func share(content: Content) } class FaceBookSharingService: SharingService { func share(content: Content) { /// Use FaceBook API to share a content print("Service: \(content) was posted to the Facebook") } } class InstagramSharingService: SharingService { func share(content: Content) { /// Use Instagram API to share a content print("Service: \(content) was posted to the Instagram", terminator: "\n\n") } } protocol Content: CustomStringConvertible { var title: String { get } var images: [UIImage] { get } } struct FoodDomainModel: Content { var title: String var images: [UIImage] var calories: Int var description: String { return "Food Model" } }
Output.txt: 実行結果
Client: Pushing Photo View Controller... PhotoViewController: User selected a Food Model to share Service: Food Model was posted to the Instagram PhotoViewController: User selected a Food Model to share Service: Food Model was posted to the Facebook Client: Pushing Feed View Controller... FeedViewController: User selected a Food Model to share Service: Food Model was posted to the Instagram FeedViewController: User selected a Food Model to share Service: Food Model was posted to the Facebook