Skip to main content

Swift Example

/// Bridge Design Pattern
///
/// Intent: Lets you split a large class or a set of closely related classes
/// into two separate hierarchies—abstraction and implementation—which can be
/// developed independently of each other.
///
///   A
///   / \ A N
///   Aa Ab ===> / \ / \
///   / \ / \ Aa(N) Ab(N) 1 2
/// Aa1 Aa2 Ab1 Ab2

import XCTest

/// The Abstraction defines the interface for the "control" part of the two
/// class hierarchies. It maintains a reference to an object of 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))
}
}
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