Bridge es un patrón de diseño estructural que divide la lógica de negocio o una clase muy grande en jerarquías de clases separadas que se pueden desarrollar independientemente.
Una de estas jerarquías (a menudo denominada Abstracción) obtendrá una referencia a un objeto de la segunda jerarquía (Implementación). La abstracción podrá delegar algunas (en ocasiones, la mayoría) de sus llamadas al objeto de las implementaciones. Como todas las implementaciones tendrán una interfaz común, serán intercambiables dentro de la abstracción.
Ejemplo conceptual
Este ejemplo ilustra la estructura del patrón de diseño Bridge y se centra en las siguientes preguntas:
¿De qué clases se compone?
¿Qué papeles juegan esas clases?
¿De qué forma se relacionan los elementos del patrón?
Después de conocer la estructura del patrón, será más fácil comprender el siguiente ejemplo basado en un caso de uso real de Swift.
Example.swift: Ejemplo conceptual
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))
}
}
Output.txt: Resultado de la ejecución
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
Ejemplo del mundo real
Example.swift: Ejemplo del mundo real
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: Resultado de la ejecución
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