Delegate Pattern

The delegate pattern refers to the basic design patterns.
Suppose we are developing a barbershop application. The application has a calendar for choosing the day for recording, tap on the date should open a list of barbers with the option of choice.
We implement naive binding of system components, combine the calendar and the screen using pointers to each other, to implement the list output:



// pseudocode

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date) {
        screen.showBarbersList(date: date)
    }
}

After a few days, the requirements change, before the list is displayed, you need to show offers with a choice of services (beard haircuts, etc.) but not always, on all days except Saturday.
We add the check Saturday today or not to the calendar, depending on it we call the method of the list of barbers or the list of services, for clarity I will demonstrate:



// pseudocode

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date)  {
        if date.day != .saturday {
             screen.showOffersList()
        }
        else {
             screen.showBarbersList(date: date)
        }
    }
}

After a week, we are asked to add a calendar to the feedback screen, and at this moment the first architectural oh happens!
What to do? After all, the calendar is tightly connected to the haircut recording screen.
wow! phew! Oh oh
If you continue to work with such a crazy application architecture, you should make a copy of the entire calendar class and associate this copy with the feedback screen.
Ok, it seems good, then we added a few more screens and several copies of the calendar, and then the X moment came. We were asked to change the design of the calendar, that is, now we need to find all copies of the calendar and add the same changes to all. This “approach” greatly affects the speed of development, increases the chance to make a mistake. As a result, such projects find themselves in a trough, when even the author of the original architecture does not understand how copies of his classes work, other hacks added along the way fall apart on the fly.
What had to be done, but it is better that it’s not too late to start doing? Use the delegation pattern!
Delegation is a way to pass class events through a common interface. The following is an example delegate for a calendar:


protocol CalendarDelegate {
   func calendar(_ calendar: Calendar, didSelect date: Date)
}

Now add the work code with the delegate to the sample code:



// pseudocode

class BarbershopScreen: CalendarDelegate {
   let calendar: Calendar

   init() {
       calendar.delegate = self
   }

   func calendar(_ calendar: Calendar, didSelect date: Date) {
        if date.day != .saturday {
            showOffersList()
        }
        else {
             showBarbersList(date: date)
        }
   }

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    weak var delegate: CalendarDelegate

    func handleTap(on date: Date)  {
        delegate?.calendar(self, didSelect: date)
    }
}

As a result, we untied the calendar from the screen at all, when selecting a date from the calendar, it sends a date selection event – * delegates * the processing of the event to the subscriber; the subscriber is the screen.
What benefits do we get in this approach? Now we can change the calendar and the logic of the screens independently of each other, without duplicating classes, simplifying further support; Thus, the “sole responsibility principle” of the implementation of the system components is implemented, the DRY principle is respected.
When using delegation, you can add, change the logic for displaying windows, the order of anything on the screen, and this will absolutely not affect the calendar and other classes, which objectively and should not participate in processes not directly related to them.
Alternatively, programmers who are not particularly bothered use sending messages via a common bus, without writing a separate protocol / interface for the delegate, where it would be better to use delegation. I wrote about the shortcomings of this approach in the last post – “Pattern Observer”.

References

https://refactoring.guru/replace-inheritance-with-delegation

1+