poniedziałek, 9 listopada 2015

[Wzorce projektowe] Composite

Analogia z życia:

Oddając samochód do mechanika powierzamy mu kluczyki i oczekujemy naprawy zepsutych części. Mechanik po odebraniu kluczyków wjeżdża samochodem na warsztat i zleca swoim pracownikom konkretne zadania: wymianę zderzaka, prostowanie blachy, lakierowanie. Z naszego punktu widzenia załatwiamy wszystkie te sprawy z jedną osobą (kierownikiem warsztatu), nie musimy martwić się o to, kto będzie wykonywał poszczególne zadania.



Zastosowanie:

Wszędzie tam, gdzie chcemy, aby kod klienta wiedział jak najmniej o procesie, który woła. Klient powinien być niezależny od tego procesu i przechowywać jedynie referencję do obiektu kompozytu, który dba o to, by wywołać odpowiednie konkretne składowe procesu.

Zasada działania:

Klient zawiera referencję na pewien interfejs. Interfejs ten jest implementowany przez konkretne typy, jak również przez specjalny typ - kompozyt, który zawiera także kolekcję elementów implementujących interfejs.


public interface ICarMechanic
{
    void Repair(Car instance);
}

public class Tinsmith : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.CrackedBody = false;
    }
}

public class WheelChanger : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.WorkingWheels = 4;
    }
}

public class BumperChanger : ICarMechanic
{
    public void Repair(Car instance)
    {
        instance.HasNewBumper = true;
    }
}

//composite
public class CarService : ICarMechanic
{
    private List<ICarMechanic> _mechanics;

    public CarService(IEnumerable<ICarMechanic> mechanics)
    {
        _mechanics = new List<ICarMechanic>(mechanics);
    }

    public void Repair(Car instance)
    {
        if (instance.BrokenWheel)
        {
            RepairIfAny<WheelChanger>(instance);
        }

        if (instance.BrokenBody)
        {
            RepairIfAny<WheelChanger>(instance);                
        }

        if (instance.BrokenWheel)
        {
            RepairIfAny<BumperChanger>(instance);
        }
    }

    private void RepairIfAny<T>(Car instance) where T : ICarMechanic
    {
        var mechanic = _mechanics.OfType<T>().FirstOrDefault();
        if (mechanic != null)
            mechanic.Repair(instance);
    }
}

Wyekstraktowanie logiki napraw do klasy - kompozytu znacznie ułatwia kod po stronie klienta wołającego usługę naprawy.

public class CarOwner
{
    private Car _car;
    private ICarMechanic _mechanicContact;

    public CarOwner(Car car, ICarMechanic contact)
    {
        _car = car;
        _mechanicContact = contact;
    }

    public void RenewCar()
    {
        _mechanicContact.Repair(_car);
        Console.WriteLine("Car has been repaired");
    }
}

Cały proces startuje przez stworzenie odpowiednich zależności dla kompozytu:

static void Main(string[] args)
{
    var mechanicsSvc = new CarService(
        new ICarMechanic[] { new BumperChanger(), new WheelChanger(), new Tinsmith() });

    var car = new Car() { BrokenBody = true, BrokenWheel = true, HasNewBumper = false };

    var owner = new CarOwner(car, mechanicsSvc);
    owner.RenewCar();

    Console.ReadKey();
}

Brak komentarzy:

Prześlij komentarz