Behavioral patterns
Last modified on Tue 28 Jun 2022

Observer pattern

This pattern allows us to make a change of state in a single object and cascade this change to other observer objects. The object which initializes the change is inherited from the subject class and objects which are "subscribed" to that object are inherited from observer class. This works by subject class notifying other observers when a change of state happens in class which inherited the subject class. Observer objects can subscribe or unsubscribe from the subject at any time.

The subject doesn't have to be aware of observer object's implementation, but it maintains a list of observers, which is then used to notify them, usually by calling one of their methods.

Here we have a simple example of the observer pattern. Subject class allows observers to subscribe (or unsubscribe) to the objects that are inherited from subject class. By changing something in the login object, we automatically update the lists in NewsletterSubscription and DiscountSubscription class. Every time a new Email is added, all objects which inherited the Observer class update themselves to the new Email.

abstract class Subject
{
    internal readonly List<IObserver> _observers;

    public void Subscribe(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void Unsubscribe(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void NotifyObservers(string email)
    {
        foreach(var observer in _observers)
        {
            _observers.Update(email);
        }
    }

    public void AddEmail(string email)
    {
        NotifyObservers(email);
    }
}

public class Login : Subject 
{
    private string _email;

    public string GetEmail()
    {
        return _email:
    }

    public void SetEmail(string newEmail)
    {
        _email=newEmail;
        NotifyObservers(_email);
    }
}

public interface IObserver
{
    void Update(string email);
}

public class DiscountSubscription : IObserver
{
    public List<string> Emails { get; set; }

    public void Update(string email)
    {
        Emails.Add(email);
    }
}

public class NewsletterSubscription : IObserver
{
     public List<string> Emails { get; set; }

     public void Update(string email)
     {
         Emails.Add(email);
     }
}

Chain of Responsability

With Chain of Responsability pattern we avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.

To implement this pattern we create a chain of objects to examine requests. Each object in turn examines a request and either handles it or passes it on to the next object in the chain. Client that sends request doesn’t have to know the chain’s structure nor keep direct references to its members.

abstract class CarService
{
    protected CarService _successor;

    public abstract void Service(Car car);

    public void SetSuccessor(CarService successor)
    {
        _successor = successor;
    }
}

class EngineService : CarService
{
    public override void Service(Car car)
    {
        if (EngineDiagnostics(car))
            EngineRepair(car);
        else if (_successor != null)
            _successor.Service(Car car);
    }

    private bool EngineDiagnostics(Car car){...}
    private void EngineRepair(Car car){...}
}

class TransmissionService: CarService
{
    public override void Service(Car car)
    {
        if (TransmissionDiagnostics(car))
            TransmissionRepair(car);
        else if (_successor != null)
            _successor.Service(request);
    }

    private bool TransmissionDiagnostics(Car car){...}
    private void TransmissionRepair(Car car){...}
}

Template Method

Template method pattern is all about encapsulating an algorithm. It defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

The template method defines sequence of steps, each represented by a method. Concrete methods already have some default implementation, but still can be overridden if needed, while abstract methods must be implemented by every subclass. There is also a hook, which is an concrete method with an empty body. A template method would work even if a hook isn’t overridden. Usually, hooks are placed before and after crucial steps of algorithms, providing subclasses with additional extension points for an algorithm.

abstract class Worker
{
    // template method
    public final void MorningRoutine()
    {
        WakeUp();
        EatBreakfast();
        GoToWork();
    }

    public void GoToWork() {...};
    public void EatBreakfast() {...};
    public abstract void GoToWork();

}

class FireFighter : Worker
{
    public override void GoToWork() {...}
}