Bridge Pattern

The Bridge pattern refers to the structural design patterns. It allows you to abstract the implementation of class logic using the transfer of logic into a separate abstract class. Sounds easy, right?

Suppose we implement a spam bot that should be able to send messages to different types of instant messengers.
We implement using a common protocol:


protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class SpamBot {
    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            iSeekUMessennger.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

Now let’s imagine the situation of the release of a new, faster message sending protocol for the iSekU messenger. To add a new protocol, it will be necessary to duplicate the implementation of the iSekU bot, changing only a small part of it. It is not clear why to do this if only a small part of the class logic has changed. With this approach, the principle of DRY is violated; with the further development of the product, the lack of flexibility will make itself known by mistakes and delays in the implementation of new opportunities.
We’ll take out the logic of the protocol in an abstract class, thus implementing the Bridge pattern:


protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

protocol MessagesSender {
    func send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekUFastMessengerSender: MessagesSender {
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?
    var messagesSender: MessengerMessagesSender?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        messagesSender?.send(message: message, to: user)
    }
}

class SpamBot {

    var messagesSender: MessagesSender?

    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            messagesSender.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

One of the advantages of this approach is undoubtedly the ability to extend the functionality of the application, by writing plug-ins / libraries that implement the abstracted logic, without changing the code of the main application.
What is the difference with the Strategy pattern? Both patterns are very similar, but the Strategy describes the switching * of algorithms *, while the Bridge allows switching large parts * of any complex logic *.

References

https://refactoring.guru/design-patterns/bridge

Source Code

https://gitlab.com/demensdeum/patterns/