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