Должны ли абстракции снижать читабельность кода?

19

Хороший разработчик, с которым я работаю, недавно рассказал мне о некоторых трудностях, с которыми он столкнулся при реализации функции в некотором коде, который мы унаследовали; он сказал, что проблема была в том, что за кодом было трудно следовать. Исходя из этого, я посмотрел вглубь продукта и понял, как трудно было увидеть путь к коду.

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

Я знаю, что некоторые разработчики строго отказываются от контейнеров внедрения зависимостей по этой самой причине. Это настолько запутывает путь программного обеспечения, что сложность навигации по коду увеличивается в геометрической прогрессии.

Мой вопрос: когда фреймворк или шаблон вносит столько накладных расходов, как это, стоит ли это того? Это признак плохо реализованного паттерна?

Я думаю, разработчик должен взглянуть на более широкую картину того, что эти абстракции привносят в проект, чтобы помочь им преодолеть разочарование. Обычно, однако, трудно заставить их увидеть эту общую картину. Я знаю, что мне не удалось продать потребности МОК и ДИ с TDD. Для этих разработчиков использование этих инструментов слишком сильно ограничивает читабельность кода.

Мартин Блор
источник

Ответы:

17

Это действительно более длинный комментарий к ответу @kevin cline.

Даже если сами языки не обязательно вызывают или предотвращают это, я думаю, что в его представлении есть что-то, что оно в какой-то степени связано с языками (или, по крайней мере, языковыми сообществами). В частности, даже если вы можете столкнуться с одной и той же проблемой на разных языках, она часто принимает разные формы на разных языках.

Например, когда вы сталкиваетесь с этим в C ++, есть вероятность, что это скорее не результат слишком большой абстракции, а скорее результат слишком большой хитрости. Например, программист скрыл важнейшее преобразование, которое происходит (которого вы не можете найти) в специальном итераторе, поэтому то, что выглядит как копирование данных из одного места в другое, действительно имеет ряд побочных эффектов, которые не имеют ничего общего с делать с этим копированием данных. Просто чтобы сделать вещи интересными, это чередуется с выводом, который создается как побочный эффект создания временного объекта в процессе приведения одного типа объекта к другому.

Напротив, когда вы сталкиваетесь с этим в Java, вы с большей вероятностью увидите какой-нибудь вариант хорошо известного «корпоративного привет-мира», где вместо одного простого тривиального класса, который делает что-то простое, вы получаете абстрактный базовый класс и конкретный производный класс, который реализует интерфейс X и создается фабричным классом в среде DI и т. д. 10 строк кода, которые выполняют реальную работу, скрыты под 5000 строками инфраструктуры.

Отчасти это зависит от среды, по крайней мере, в той же степени, что и язык - работа непосредственно с оконными средами, такими как X11 и MS Windows, печально известна тем, что превращает тривиальную программу «hello world» в 300+ строк почти неразборчивого мусора. Со временем мы также разработали различные наборы инструментов, чтобы изолировать нас от этого - но 1) эти наборы инструментов довольно нетривиальны сами по себе, и 2) конечный результат все еще не только больше и сложнее, но также обычно менее гибок чем эквивалент текстового режима (например, хотя он просто распечатывает некоторый текст, перенаправление его в файл редко возможно / поддерживается).

Чтобы ответить (по крайней мере, на часть) первоначального вопроса: по крайней мере, когда я его видел, дело было не столько в плохой реализации шаблона, сколько в простом применении шаблона, который не подходил для стоящей перед нами задачи - большинство часто пытаясь применить какой-то шаблон, который вполне может быть полезен в программе, которая неизбежно огромна и сложна, но когда она применяется к более мелкой проблеме, в конечном итоге она также становится огромной и сложной, хотя в этом случае размер и сложность действительно можно было избежать ,

Джерри Гроб
источник
7

Я обнаружил, что это часто вызвано тем, что я не придерживался подхода ЯГНИ . Все, что происходит через интерфейсы, даже если есть только одна конкретная реализация и в настоящее время нет планов по внедрению других, является ярким примером добавления сложности, которая вам не понадобится. Вероятно, это ересь, но я чувствую то же самое в отношении широкого использования инъекций зависимостей.

Carson63000
источник
+1 за упоминание ЯГНИ и абстракций с единичными ориентирами. Основная роль создания абстракции заключается в том, чтобы выявлять общую точку зрения нескольких вещей. Если на абстракцию ссылаются только из одной точки, мы не можем говорить о выделении общих вещей, а такая абстракция только усугубляет проблему йойо. Я бы расширил это, потому что это верно для всех видов абстракций: функций, обобщений, макросов, чего угодно ...
Calmarius
3

Ну, недостаточно абстракции, и ваш код трудно понять, потому что вы не можете изолировать, какие части что делают.

Слишком много абстракции, и вы видите абстракцию, но не сам код, а затем затрудняете выполнение реального потока выполнения.

Чтобы добиться хорошей абстракции, нужно поцеловаться: посмотрите мой ответ на эти вопросы, чтобы знать, что нужно делать, чтобы избежать подобных проблем .

Я думаю, что избегание глубокой иерархии и именования являются наиболее важным моментом, который нужно рассмотреть для описываемого вами случая. Если бы абстракции были хорошо названы, вам не пришлось бы заходить слишком глубоко, только на уровень абстракции, где вы должны понимать, что происходит. Именования позволяют вам определить, где находится этот уровень абстракции.

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

Klaim
источник
3
Ну, недостаточно абстракции, и ваш код трудно понять, потому что вы не можете изолировать, какие части что делают. Это инкапсуляция, а не абстракция. Вы можете изолировать части в конкретных классах без особых абстракций.
Заявление
Классы - это не единственные абстракции, которые мы используем: функции, модули / библиотеки, сервисы и т. Д. В ваших классах вы обычно абстрагируете каждую функциональность за функцией / методом, которая может вызывать другие методы, которые абстрагируют друг друга от всех функций.
Klaim
1
@Statement: инкапсуляция данных, конечно, абстракция.
Эд С.
Иерархии пространств имен действительно хороши.
JAB
2

Для меня это проблема сцепления и связана с гранулярностью дизайна. Даже самая слабая форма связи вводит зависимости от одного к другому. Если это делается для сотен или тысяч объектов, даже если все они относительно просты, придерживаются SRP, и даже если все зависимости направлены к стабильным абстракциям, это дает кодовую базу, которую очень трудно рассуждать как взаимосвязанное целое.

Есть практические вещи, которые помогают вам оценить сложность кодовой базы, не часто обсуждаемые в теоретической SE, например, насколько глубоко в стек вызовов вы можете попасть, прежде чем достигнуть конца, и насколько глубоко вам нужно пройти, прежде чем вы сможете, с большая уверенность, понять все возможные побочные эффекты, которые могут возникнуть на этом уровне стека вызовов, в том числе в случае исключения.

И я на собственном опыте обнаружил, что более плоские системы с более мелкими стеками вызовов гораздо легче рассуждать. В качестве крайнего примера можно привести систему объект-компонент, где компоненты - это просто необработанные данные. Функциональность есть только у систем, и в процессе реализации и использования ECS я обнаружил, что это самая простая из когда-либо существовавших систем, когда можно рассуждать о том, когда сложные кодовые базы, охватывающие сотни тысяч строк кода, в основном сводятся к нескольким десяткам систем, которые содержать все функции.

Слишком много вещей обеспечивают функциональность

Альтернативой ранее, когда я работал в предыдущих кодовых базах, была система с сотнями или тысячами в основном крошечных объектов, в отличие от нескольких десятков громоздких систем, в которых некоторые объекты использовались просто для передачи сообщений от одного объекта к другому (Message объект, например, который имел собственный публичный интерфейс). Это в основном то, что вы получаете аналогично, когда вы возвращаете ECS обратно к точке, где компоненты имеют функциональные возможности, и каждая уникальная комбинация компонентов в сущности дает свой собственный тип объекта. И это, как правило, приводит к меньшим, более простым функциям, унаследованным и обеспеченным бесконечными комбинациями объектов, которые моделируют крошечные идеи ( Particleобъект противPhysics Systemнапример). Тем не менее, он также имеет тенденцию создавать сложный график взаимозависимостей, который затрудняет рассуждение о том, что происходит на широком уровне, просто потому, что в базе кода есть так много вещей, которые действительно могут что-то сделать и, следовательно, могут сделать что-то не так - - типы, которые не являются типами «данных», но являются типами «объектов» со связанной функциональностью. Типы, которые служат чистыми данными без какой-либо связанной функциональности, не могут ошибаться, так как они ничего не могут сделать самостоятельно.

Чистые интерфейсы не очень помогают в этой проблеме понятности, потому что даже если это делает «зависимости времени компиляции» менее сложными и предоставляет больше возможностей для изменений и расширений, это не делает «зависимости времени выполнения» и взаимодействия менее сложными. Клиентский объект все еще заканчивается вызовом функций для конкретного объекта учетной записи, даже если они вызываются IAccount. Полиморфизм и абстрактные интерфейсы имеют свое применение, но они не разделяют вещи таким образом, который действительно помогает вам размышлять обо всех побочных эффектах, которые могут произойти в любой данный момент. Чтобы добиться такого эффективного разделения, вам нужна кодовая база, в которой гораздо меньше функций, содержащих функциональность.

Больше данных, меньше функциональности

Таким образом, я нашел, что подход ECS, даже если вы не применяете его полностью, будет чрезвычайно полезным, поскольку он превращает то, что было бы сотнями объектов, в просто необработанные данные с помощью громоздких систем, более грубо спроектированных, которые обеспечивают все функциональность. Это максимизирует количество типов «данных» и сводит к минимуму количество типов «объектов», и, следовательно, абсолютно минимизирует количество мест в вашей системе, которые могут действительно пойти не так. Конечным результатом является очень «плоская» система без сложного графа зависимостей, только системы с компонентами, никогда не наоборот, и никогда не компоненты с другими компонентами. В основном это гораздо больше необработанных данных и намного меньше абстракций, что приводит к централизации и выравниванию функциональности базы кода для ключевых областей, ключевых абстракций.

30 более простых вещей не обязательно проще рассуждать о чем-то более сложном, если эти 30 более простых вещей взаимосвязаны, а сложная вещь стоит сама по себе. Таким образом, мое предложение состоит в том, чтобы на самом деле перенести сложность от взаимодействий между объектами и больше к более объемным объектам, которым не нужно взаимодействовать с чем-либо еще для достижения массовой развязки, к целым «системам» (не монолитам и объектам бога, учтите, и не классы с 200 методами, но что-то значительно более высокого уровня, чем a Messageили a, Particleнесмотря на минималистский интерфейс). И предпочитаю более простые старые типы данных. Чем больше вы зависите от них, тем меньше связи вы получите. Даже если это противоречит некоторым идеям SE, я обнаружил, что это очень помогает.


источник
0

У меня такой вопрос: когда фреймворк или шаблон вносит столько накладных расходов, как это, стоит ли это того? Это признак плохо реализованного паттерна?

Может быть, это симптом выбора неправильного языка программирования.

Кевин Клайн
источник
1
Я не понимаю, как это связано с выбранным языком. Абстракции - это независимая от языка концепция высокого уровня.
Ed S.
@Ed: Некоторые абстракции легче реализуются в некоторых языках, чем в других.
Кевин Клайн
Да, но это не значит, что вы не можете написать на тех языках совершенно понятную и понятную абстракцию. Моя точка зрения заключалась в том, что ваш ответ не отвечает на вопрос и никак не помогает ОП.
Эд С.
0

Плохое понимание шаблонов проектирования, как правило, является основной причиной этой проблемы. Одним из худших, что я видел, когда вы переходили от интерфейса к интерфейсу без большого количества конкретных данных, было расширение для Oracle Grid Control.
Честно говоря, похоже, что у кого-то был мой абстрактный метод фабрики и шаблонный оргазм во всем моем Java-коде. И это заставило меня чувствовать себя таким же пустым и одиноким.

Джефф Лангемайер
источник
-1

Я бы также предостерег от использования функций IDE, которые упрощают абстрагирование.

Кристофер Махан
источник