Consider a use case in our travel product where we have two types of reservations:
- Premium reservations: special benefits such as free cancellation, better cashback, and so on.
- Normal reservation: normal restrictions.
Also, recall from Chapter 2, Packaging Code, that we have two types of sellers:
- Institutional: those who can give us an API to pull data off and do our bookings.
- Small scale: those who will use a platform we build as a part of this product to onboard inventory.
The reservations are finally fulfilled by the sellers. The naive implementation will have a matrix of the reservation type for each type of seller. However, very soon, the code will become unmaintainable. As the code grows in complexity, we might find hard-to-engineer binding between interface and implementation.
The is a relationships between the interfaces start getting mixed up with the implementation details. The abstraction and implementation cannot both be independently extended.
The bridge pattern aims to solve this as follows:
- Decoupling the abstraction from the implementation so that both can vary independently
- Segregating the interface hierarchy and the implementation hierarchy into two separate trees
This pattern is described here:
The dummy implementation for the use case mentioned previously is given here:
type Reservation struct {
sellerRef Seller // this is the implementer reference
}
func (r Reservation) Cancel() {
r.sellerRef.CancelReservation(10) // charge $10 as cancellation feed
}
type PremiumReservation struct {
Reservation
}
func (r PremiumReservation) Cancel() {
r.sellerRef.CancelReservation(0) // no charges
}
// This is the interface for all Sellers
type Seller interface {
CancelReservation(charge float64)
}
type InstitutionSeller struct {}
func (s InstitutionSeller) CancelReservation(charge float64) {
fmt.Println("InstitutionSeller CancelReservation charge =", charge)
}
type SmallScaleSeller struct {}
func (s SmallScaleSeller) CancelReservation(charge float64) {
fmt.Println("SmallScaleSeller CancelReservation charge =", charge)
}
Its usage is as follows:
res:= Reservation{InstitutionSeller{}}
res.Cancel() // will print 10 as the cancellation charge
premiumRes:= PremiumReservation{Reservation{SmallScaleSeller{}}}
premiumRes.Cancel() // will print 0 as the cancellation charge