11 січня 2011 р.

"Патерн Вівторка" #22: Адаптер (Adapter)



 Я із деяких причин дуже довго думав над тим який же може бути хороший приклад для Адаптера. Можна приклад коли є операції, які так і називаються ОпераціяА та ОпА, або придумати складний приклад із кучою методів і пропертів в класі що адаптується, але весь цей час в голові у мене крутилася така штукенція, яку ми соваємо у розетку, коли разетка вузька ще СРСР-рівська а ми хочемо запхати шнур для жившення ноутбука із товстою вилкою. Майже така як на картинці збоку, але не настільки жорстока.

Отже у нас є вилка від зарядного пристрою, яка підходить в широкі роз’єми. В одній із квартир у нас усе сучасне, тому NewElectricitySystem має метод MatchWideSocket, яким ми просто можемо скористатися. В іншій квартирі у нас проблемки тому OldElectricitySytem має тільки метод MatchThinSocket. Нажаль ми не можемо собі позволити взяти дрель і роздовбати отвори в розетці – ми купляємо Адаптер, який надає можливість користуватися тою ж функціональністю споживання електричного струму, але із старої системи.

АДАПТЕР


Адаптер – це дизайн патерн, що надає можливість користуватися об’єктом, зміни у який ми не можемо робити, надаючи його функціональність через відомий нашій системі інтерфейс.



// Adaptee
    class OldElectricitySystem
    {
        public string MatchThinSocket()
        {
            return "220V";
        }
    }
    // широковикористовуваний інтерфейс
    // Target
    interface INewElectricitySystem
    {
        string MatchWideSocket();
    }
    class NewElectricitySystem : INewElectricitySystem
    {
        public string MatchWideSocket()
        {
            return "220V";
        }
    }
    // Adapter
    class Adapter : INewElectricitySystem
    {
        private readonly OldElectricitySystem _adaptee;

        public Adapter(OldElectricitySystem adaptee)
        {
            _adaptee = adaptee;
        }
        // А тут коїться вся магія 
        // наш адаптер перекладає із того,
        // що ми (код) не можемо використати наразі у те що ми можемо
        public string MatchWideSocket()
        {
            return _adaptee.MatchThinSocket();
        }
    }
    // Consumer
    class ElectricityConsumer
    {
        // Зарядний пристрій розуміє тільки нову систему
        public static void ChargeNotebook(INewElectricitySystem electricitySystem)
        {
            Console.WriteLine(electricitySystem.MatchWideSocket());
        }
    }

    public class AdapterDemo
    {
        public static void Run()
        {
            // 1) Ми можемо і надалі користувати нашою новою системою
            var newElectricitySystem = new NewElectricitySystem();
            ElectricityConsumer.ChargeNotebook(newElectricitySystem);

            // 2) Ми повинні адаптуватися до старої системи, використовуючи адаптер
            var oldElectricitySystem = new OldElectricitySystem();
            var adapter = new Adapter(oldElectricitySystem);
            ElectricityConsumer.ChargeNotebook(adapter);
        }
    }

Ще одним ім’ям цього дизайн патерну є Wrapper із-за того що він огортає функціональність якогось об’єкту представлючи її у вигляді іншого інтерфейсу.

Насправді в реалізації не є обов’язковим композиція adaptee, наш клас Adapter міг б реалізовувати два інтерфейси – нової і старої системи. Таким чином одного разу створивши його на базі нової або старої системи, зробивши два конструктори (а чому б і ні), ми б могли використовувати його у два боки.

4 коментарі:

  1. Гарний малюнок :) Ну і приклад також :)

    ВідповістиВидалити
  2. гарний приклад, все як завжди на висоті, тільки.. ось UML діаграму ще б - був б фул-хаус :)

    ВідповістиВидалити
  3. Та я знаю... просто я хочу малювати свої, а на них завжди часу не стає. Дякую. Ще лишилося 3 GoF патерни, думаю що для них таки намалюю, щоб був фул-хаус.

    ВідповістиВидалити