Как стать автором
Обновить

Про абстракции и метод рефакторинга «Extract method»

Время на прочтение 3 мин
Количество просмотров 4K
Абстракции чрезвычайно важны в программировании и это все знают. Они помогают нам отделить существенные детали чего бы то ни было от несущественных. В идеале они должны выделять только самое главное, эссенцию, без всяких посторонних примесей, минимум характеристик объекта или процесса, но идеал встречается не так уж и часто.

Когда программист пишет код, он оперирует абстракциями.

«Клиент посылает запрос Серверу».

Каждое слово в предыдущем предложении есть высокоуровневая абстракция. Важно, что эти четыре абстракции находятся на одном уровне. Соответствующий код может выглядеть например так:

client.send(request, server);


У читателя не возникает вопросов по при виде этой строки.

Часто одной строки кода бывает недостаточно и программисты добавляют ещё строчек. Много времени и места занимает, например, валидация входных параметров и обработка возвращаемых функциями значений.


void sendRequest(Request request)
{
        if (server->error() != Server::errorOk)
        {
                // handle
        }

        if (request.userName.empty())
        {
                // handle
        }

        // ещё 100 if-ов

        client.send(request, server);
}


Я так часто вижу подобные if'ы, что воспринимаю их как шум (особенно Yoda-condition). Они безусловно нужны, но… я считаю, что их не должно быть здесь, в этой функции sendRequest, потому что client.send находится на более высоком уровне абстракции чем всякие Server::ErrorState. Эти if'ы — более низкоуровневые абстракции, поэтому их надо спрятать в отдельные методы isServerReady() и isRequestValid(). Тогда код будет такой:


void sendRequest(Request request)
{
        if (!isServerReady())
        {
                // handle
        }

        if (!isRequestValid(request))
        {
                // handle
        }

        // больше нет if'ов

        client.send(request, server);
}


Теперь sendRequest оперирует функциями, находящимися на одном уровне абстракции, поэтому код читается легко и приятно. Бонус — эти методы можно (нужно) повторно использовать вместо копирования if'ов. Я не хочу видеть эти уродливые server->error() != Server::errorOk в функции sendRequest, но я хотел бы их видеть, когда они мне понадобятся. Тогда я посмотрю содержимое isServerReady(). Чуете?

Или вот например. Большой-пребольшой класс. По всем методам разбросаны условия вида


if (niceNamedContainer.empty())
{
// do some logic
}


Когда я вижу код в первый раз, то про if (niceNamedContainer.empty()) я могу только сказать «Если в niceNamedContainer ничего нет». Сразу у меня возникает вопрос, а что это значит? Сам себе отвечаю: в него ничего не положили, где-то в другом месте, где должны были положить, но не здесь. После перелопачивания не менее чем ста процентов кода класса с целью разобраться, как он работает, я понимаю, что это условие на самом деле означает «Данные готовы». Ура. Но я думаю, что было бы совсем не трудно сделать выделение метода isDataReady() на этапе написания класса. Читателю было бы гораздо легче, и не пришлось бы применять телепатические способности, потому что читателю кода в первую очередь нужны высокоуровневые абстракции. Метод isDataReady() это лестница, соединяющая низкий уровень абстракции с высоким. По ней очень удобно спускаться вниз, если надо, а если не надо, то можно и не спускаться. Я думаю, что это сэкономило бы порядочно времени на поддержке чужого, старого, унаследованного кода. А делов-то, применить выделение метода да следить, что бы одна функция работала только с одним уровнем абстракции… ну еще SRP не забывать… ну и Лисков… ну и инкапсуляцию… МакКоннела тоже… чаще вспоминать.

Данные примеры хотя и примитивны, но не так далеки от реальности, как могут показаться. Упор я сделал на if'ы, поскольку условные конструкции формулируют, так сказать, логику работы кода и потому, что иногда в переплетении if'ов довольно трудно разобраться. Тем не менее это далеко не единственное место применения Extract Method.

Ссылки:
ru.wikipedia.org/wiki/Абстракция_данных

Литература:
Steve McConnell. Code Complete: A Practical Handbook of Software Construction.
Robert C. Martin. Clean Code: A Handbook of Agile Software Craftsmanship.
Martin Fowler. Refactoring: Improving the Design of Existing Code
Теги:
Хабы:
+67
Комментарии 35
Комментарии Комментарии 35

Публикации

Истории

Работа

QT разработчик
13 вакансий
Программист C++
121 вакансия

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн