Pull to refresh

Паттерн — Вылить воду из чайника

Reading time2 min
Views4.5K
как программист кипятит воду в чайнике?
1. Набирает воду в чайник
2. Ставит чайник на огонь
3. Ждет пока тот не вскипит

как программист кипятит воду в чайнике если в нем уже есть вода?
1. Выливает воду из чайника что сводит задачу к уже решенной
(старый анекдот)


Для каждой задачи есть куча разных решений. Умных, глупых, быстрых, хитрых. В реальной жизни решения делятся на два типа — те которые работают и все остальные.

Представьте себе что у вас есть сложный код ( свой или чужой ), и нужно серьезно повлиять на логику его работы? Конечно можно залезть в глубину, исследовать зависимости, подумать над возможными последствиями и проблемами. Даже для своего кода это не просто. Для чужого же — сложность просто дикая.

Когда перед вами ставится подобная задача — самое время вспомнить про паттерн «вылить воду из чайника» (звучит почти как китайская стратегемма =) )
И вместо проделывая сложной задачи — свести ее к решению уже существующей. Таким образом вы не модифицируете сложный, и потенциально опасный код, а пишете свой, небольшой кусочек кода, в правильности работы которого легко убедиться.

примеры
Когда-то давно наша компания создавала БД для Lotus Notes. Но увы клиентам было непривчно нажимать Tab для перехода на другую ячейку таблицы — поскольку они привыкли к Экселю, где для перехода нужно было нажать Enter. Мы не нашли ни одного стандартного средства для замены клавиши Таб на Энтер, и поэтому прибегли к следующему решению. Повесили клавиатурный хук который замещал нажатия Enter на Tab если нажатие имело место быть в нужных нам таблицах нашего приложения.

другой пример — джаббер транспорты
джаббер клиенту не нужно знать о существовании других протоколов. Он работает только по xmpp. Переводом занимается джаббер сервер.

ну и пример из кода
class Int
{
public:
    Int(const int iv)
    :i(iv)
    {}

    Int(const Int& copy)
    {
        *this = copy;
    }

    // констуркторы копировая и присваивания тут объеденены.
    // Так если у вас добавится новая переменная в класс -
    // нужно будет добавить ее только в одно место в коде, а не в несколько
    Int& operator=(const Int& copy)
    {
        i = copy.i;
        return *this;
    }

    Int& operator++()
    {
        i++;
        return *this;
    }
    
    // реализуем постфиксный ++ через префиксный
    const Int operator++(int)
    {
        return ++(*this);
    }

    // а += через конструктор
    Int operator+=(int v)
    {
        return Int(i+v);
    }

private:
    int i;
};

// оператор+ использующий уже существующий +=
Int operator+(const Int& i, const int iv)
{
    Int intVal(i);
    intVal+=iv;
    return intVal;
}

ключевой момент — все участки которые можно было вынести в одно место — вынесены в него. Так мы гарантируем унифицированное поведение для всех однородных методов ( += и + в нашем случае) а также однородное поведение конструкторов класса. То есть не будет поведения где один конструктор работает правильно, а другой не инициализирует половину переменных.

Каждый раз когда перед вами становится большая задача по переделке логики — подумайте — может быть существует уже решение делающее все почти так как вам нужно. Решение которое можно слегка адаптировать под свои нужды.

Было бы интересно послушать ваши простые решения сложных проблем
Tags:
Hubs:
+7
Comments16

Articles

Change theme settings