→ Пошук по сайту       Увійти / Зареєструватися
Знання Патерни Структурні патерни — Structural patterns

12. Проксі — Proxy

12.	Проксі — Proxy

Спробуйте себе уявити у процесі знешкодження бомби? Невже не лячно? Навіть якщо вам важко таке уявити або ви вважаєте, що й так ніколи нічого подібного не будете робити, комусь все ж таки приходиться знешкоджувавати бомби час від часу. Можу поручитися, що сапери, за винятком тих які хильнули чвертку, також відчувають страх перед вибухівкою. На наше щастя зараз є багато різних технологій, які дозволяються обійтися без присутності людити під час знешкодження вибухового матеріалу. Більше того, робот може проробити більш складні операції аніж людина, оскільки він точніший і потужніший (проте не настільки повороткий). Принаймні в робота не трясуться руки на відміну від сапера напідпитку.

Комунікація із таким роботом звісно безпровідна, керування здійснюється за допомогою кінекту , або якогось костюму, або, принаймні, супер джойстиків. Робот також має на собі панель управління, яка дозволяє керувати ним безпосередньо на місці, на випадок, якщо якийсь внучок Бен Ладена буде блокувати сигнал до робота.

Проксі підміняє реальний об'єкт та надсилає запити до нього тоді, коли це потрібно. Проксі також може ініціалізувати реальний об'єкт, якщо він до того не існував.

Говорячи про приклад вище, реальним об’єктом є робот (RobotBombDefuser). Це купа важкого залізаччя, яким ми можемо керувати за допомогою віддалених контролів (RobotBombDefuserProxy). Так як чудо-кінект установка передає сигнали роботу, вона, звісно, знає як приєднатися до справжнього робота і передавати йому сигнали від ваших рухів (щось подібне відбувається коли ви приєднуєтеся до віддалених сервісів і викликаєте методи на них).

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

Давайте глянемо на наш приклад. Ось сам робот.

Уривок коду 12.1. Робот-знешкоджувач бомб

 class RobotBombDefuser
{
    private Random _random = new Random();
    private int _robotConfiguredWavelength = 41;
    private bool _isConnected = false;

    public void ConnectWireless(int communicationWaveLength)
    {
        if(communicationWaveLength == _robotConfiguredWavelength)
        {
            _isConnected = IsConnectedImmitatingConnectivitiyIssues();
        }
    }
    public bool IsConnected()
    {
        _isConnected = IsConnectedImmitatingConnectivitiyIssues();
        return _isConnected;
    }
    private bool IsConnectedImmitatingConnectivitiyIssues()
    {
        // Імітуємо погане з’єднання (працює в 4 із 10 спробах)
        return _random.Next(0, 10) < 4;
    }
    public virtual void WalkStraightForward(int steps)
    {
        Console.WriteLine("Did {0} steps forward...", steps);
    }
    public virtual void TurnRight()
    {
        Console.WriteLine("Turned right...");
    }
    public virtual void TurnLeft()
    {
        Console.WriteLine("Turned left...");
    }
    public virtual void DefuseBomb()
    {
        Console.WriteLine("Cut red or green or blue wire...");
    }
}

Основними методами, що роблять усю роботу є WalkStraightForward, TurnRight, TurnLeft, DefuseBomb. Є також методи, які здійснюють безпровідне з’єднання та виконують перевірку на його наявність (IsConnected). Вони потрібні для більшої реалістичності цього прикладу. Можливо, вони несуть додатковий «шум», якщо так, то можна їх упустити при читанні коду. 

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

Уривок коду 12.2. Проксі до робота – можливість ним керувати із сторони

 class RobotBombDefuserProxy : RobotBombDefuser
{
    private RobotBombDefuser _robotBombDefuser;
    private int _communicationWaveLength;
    private int _connectionAttempts = 3;

    public RobotBombDefuserProxy(int communicationWaveLength)
    {
        _robotBombDefuser = new RobotBombDefuser();
        _communicationWaveLength = communicationWaveLength;
    }
    public virtual void WalkStraightForward(int steps)
    {
        EnsureConnectedWithRobot();
        _robotBombDefuser.WalkStraightForward(steps);
    }
    public virtual void TurnRight()
    {
        EnsureConnectedWithRobot();
        _robotBombDefuser.TurnRight();
    }
    public virtual void TurnLeft()
    {
        EnsureConnectedWithRobot();
        _robotBombDefuser.TurnLeft();
    }
    public virtual void DefuseBomb()
    {
        EnsureConnectedWithRobot();
        _robotBombDefuser.DefuseBomb();
    }
    private void EnsureConnectedWithRobot()
    {
        if (_robotBombDefuser == null)
        {
            _robotBombDefuser = new RobotBombDefuser();
            _robotBombDefuser.ConnectWireless(_communicationWaveLength);
        }
        for (int i = 0; i < _connectionAttempts; i++)
        {
            if (_robotBombDefuser.IsConnected() != true)
            {
                _robotBombDefuser.ConnectWireless(_communicationWaveLength);
            }
            else
            {
                break;
            }
        }
        if(_robotBombDefuser.IsConnected() != true)
        {
            throw new BadConnectionException("No connection with remote bomb diffuser robot could be made after few attempts.");
        }
    }
}

Таким чином, наш проксі просто передає запити до справжнього об’єкту і завжди перед цим перевіряє чи з’єднання не пропало, і якщо пропало, то пробує тричі його відновити. Якщо і після цього не виходить, то генерується помилка.
Ось використання проксі:

Уривок коду 12.3. Операція знешкодження (у випадку невдачі – план «B»)

 public static void Run()
{
    int opNum = 0;
    try
    {
        var proxy = new RobotBombDefuserProxy(41);
        proxy.WalkStraightForward(100);
        opNum++;
        proxy.TurnRight();
        opNum++;
        proxy.WalkStraightForward(5);
        opNum++;
        proxy.DefuseBomb();
        opNum++;

        Console.WriteLine();
    }
    catch (BadConnectionException e)
    {
        Console.WriteLine("Exception has been caught with message: ({0}).
                Decided to have human operate robot there.", e.Message);
        PlanB(opNum);
    }
}

private static void PlanB(int nextOperationNum)
{
    RobotBombDefuser humanOperatingRobotDirectly = new RobotBombDefuser();

    if(nextOperationNum == 0)
    {
        humanOperatingRobotDirectly.WalkStraightForward(100);
        nextOperationNum++;
    }
    if (nextOperationNum == 1)
    {
        humanOperatingRobotDirectly.TurnRight();
        nextOperationNum++;
    }
    if (nextOperationNum == 2)
    {
        humanOperatingRobotDirectly.WalkStraightForward(5);
        nextOperationNum++;
    }
    if (nextOperationNum == 3)
    {
        humanOperatingRobotDirectly.DefuseBomb();
    }
}

В коді вище наведено використання проксі для робота, а також "план Б", якщо неможливо приєднатися до робота. В такому випадку створється пряме посилання на робота і методи виконуться уже безпосередньо на ньому. Код може виглядати трохи складо через opNum та nextOperationNum, які були додані, щоб імітувати довиконання операції після втрати зв’язку.

Вивід:

 Did 100 steps forward... 
 Turned right... 
 Exception has been caught with message: (No connection with remote bomb diffuser robot could be made after few attempts.). Decided to have human operate robot there. 
 Did 5 steps forward... 
 Cut red or green or blue wire...

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

А ось UML-діаграма чудасії, яку ми закодили:

UML-діаграма 8. Проксі

По матеріалам книги Андрія Будая "Дизайн патерни – просто, як двері". Матеріал розміщується за домовленістю з автором.
Робота представлена за умовами ліцензії Creative Commons Attribution-NonCommercial 3.0 Unported License.

загрузка...
Сторінки, близькі за змістом