Most jest strukturalnym wzorcem projektowym zakładającym podział logiki biznesowej lub dużej klasy na osobne hierarchie klas które następnie można rozwijać niezależnie od siebie.
Jedna z takich hierarchii (zwana często Abstrakcją) posiada referencję do obiektu drugiej hierarchii (zwanej Implementacją) i deleguje mu część (czasem większość) wywołań. Ponieważ wszystkie implementacje mają wspólny interfejs, z punktu widzenia abstrakcji są wymienialne.
Przykład koncepcyjny
Poniższy przykład ilustruje strukturę wzorca Most ze szczególnym naciskiem na następujące kwestie:
Z jakich składa się klas?
Jakie role pełnią te klasy?
W jaki sposób elementy wzorca są ze sobą powiązane?
Poznawszy strukturę wzorca będzie ci łatwiej zrozumieć następujący przykład, oparty na prawdziwym przypadku użycia Swift.
Example.swift: Przykład koncepcyjny
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: Wynik działania
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
Przykład z prawdziwego życia
Example.swift: Przykład z prawdziwego życia
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: Wynik działania
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