Являются ли так называемые «сквозные проблемы» действительным поводом для нарушения SOLID / DI / IoC?


43

Мои коллеги любят говорить «ведение журнала / кеширование и т. Д. - сквозная проблема», а затем везде использовать соответствующий синглтон. Все же они любят IoC и DI.

Действительно ли это оправданное нарушение принципа SOLI D ?


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

1
Я обнаружил, что термин «сквозная забота» является довольно конкретным термином (связанным с аспектами) и не обязательно является синонимом синглтона. Ведение журнала может быть сквозной проблемой в том смысле, что вам иногда требуется регистрировать общее сообщение в нескольких местах одним и тем же способом (например, регистрировать каждый вызов метода службы вместе с тем, кто сделал вызов, или какой-либо другой тип регистрации аудита). Тем не менее, я бы сказал, что ведение журнала (в общем смысле) не является сквозной проблемой только потому, что оно используется везде. Я думаю, что это скорее «повсеместная проблема», хотя это не совсем стандартный термин.
Пейс

4
@svidgen Принципы SOLID также полезны, когда те, кто их не знает, просматривают ваш код и спрашивают вас, почему вы так поступили. Приятно иметь возможность указать на принцип и сказать: «Вот почему»
candied_orange

1
У вас есть примеры, почему вы думаете, что регистрация - это особый случай? Это довольно упрощенный канал для перенаправления некоторых данных, как правило, в часть того X-фреймворка, который вы используете.
ksiimson

Ответы:


42

Нет.

SOLID существует в качестве руководства для учета неизбежных изменений. Вы действительно никогда не собираетесь менять свою библиотеку журналов, или цель, или фильтрацию, или форматирование, или ...? Вы действительно не собираетесь менять свою кеширующую библиотеку, или цель, или стратегию, или область видимости, или ...?

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

И вы тогда получите аргумент о том , что регистратор сам будет обрабатывать изменения. «О, если цель / фильтрация / форматирование / стратегия изменятся, тогда мы просто изменим конфигурацию!» Это мусор. Мало того, что теперь у вас есть объект God, который обрабатывает все эти вещи, вы пишете свой код в XML (или аналогичный), где вы не получаете статический анализ, вы не получаете ошибки времени компиляции, и вы не ' Т действительно получить эффективные юнит-тесты.

Есть ли случаи, когда нарушаются правила SOLID? Абсолютно. Иногда вещи не меняются (без необходимости переписывать в любом случае). Иногда небольшое нарушение LSP является самым чистым решением. Иногда создание изолированного интерфейса не имеет значения.

Но регистрация и кэширование (и другие повсеместные проблемы) не являются такими случаями. Они, как правило, являются отличными примерами проблем с соединением и дизайном, которые возникают при игнорировании рекомендаций.


22
Какая у вас альтернатива? Внедрение ILogger в каждый класс, который вы пишете? Написание декоратора для каждого класса, которому нужен регистратор?
Роберт Харви

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


20
Реальная проблема, которую я имею с догматическим IoC, состоит в том, что почти весь современный код зависит от чего-то, что не вводится и не абстрагируется. Если вы пишете на C #, например, вы не часто происходит абстрагироваться Stringили Int32даже Listиз вашего модуля. Есть только степень, в которой разумно и разумно планировать изменения. И, помимо наиболее очевидных «основных» типов, определение того, что вы, вероятно, измените , действительно является вопросом опыта и суждений.
svidgen

6
@svidgen - Надеюсь, вы не восприняли мой ответ как пропаганду догматического IoC. И только то, что мы не абстрагируем эти основные типы (и другие вещи, где разумно), не означает, что нормально иметь глобальный List / String / Int / и т.д.
Теластин

38

да

В этом весь смысл термина «сквозная забота» - это означает то, что не вписывается в принцип SOLID.

Здесь идеализм встречается с реальностью.

Люди, полу новички в SOLID и сквозные люди, часто сталкиваются с этой мысленной проблемой. Все в порядке, не волнуйтесь. Стремитесь представить все в терминах SOLID, но есть несколько мест, таких как логирование и кэширование, где SOLID просто не имеет смысла. Сквозной является братом SOLID, они идут рука об руку.


9
Обоснованный, практичный, не догматический ответ.
user1936

11
Не могли бы вы рассказать нам, почему все пять принципов SOLID нарушаются сквозными проблемами? У меня есть проблемы, чтобы понять это.
Док Браун

4
Да. Я мог бы потенциально найти вескую причину, чтобы «нарушать» каждый принцип в отдельности; но ни единой причины сломать все 5!
svidgen

1
Для меня было бы идеально, чтобы мне не приходилось биться головой об стену каждый раз, когда мне нужно издеваться над одиночным логгером / кешем для юнит-теста. Представьте, что было бы, если бы у вас не было модульного тестирования веб-приложения HttpContextBase(которое было введено именно по этой причине). Я точно знаю, что моя реальность была бы очень кислой без этого класса.
devnull

2
@devnull Может быть, в этом и заключается проблема, а не сквозные проблемы самих себя ...
Борис Паук

25

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

Одна из вышеперечисленных проблем заключается в том, что кто-то скажет: «Но как я могу проверить логирование? , Я думаю, что обычно я не тестирую логи, кроме того, что я могу на самом деле читать файлы логов и понимать их. Когда я видел проверенные журналы, это обычно потому, что кто-то должен утверждать, что класс действительно что-то сделал, и они используют сообщения журнала, чтобы получить эту обратную связь. Я бы предпочел зарегистрировать слушателя / наблюдателя в этом классе и утверждать в моих тестах, что он вызывается. Затем вы можете поместить журнал событий в этом наблюдателе.

Однако я думаю, что кэширование - это совершенно другой сценарий.


3
Немного покопавшись в FP, я начал проявлять большой интерес к использованию (возможно, монадической) композиции функций для встраивания таких вещей, как ведение журнала, обработка ошибок и т. Д. В ваши сценарии использования, не помещая ничего из этого в основную бизнес-логику ( см fsharpforfunandprofit.com/rop )
Sara

5
Ведение журнала не должно быть повсеместным. Если это так, то вы слишком много регистрируетесь и создаете шум для себя, когда вам действительно нужно посмотреть эти журналы. Внедряя зависимость от логгера, вы вынуждены думать о том, действительно ли вам нужно что-то регистрировать.
RubberDuck

12
@RubberDuck - Скажите мне, что «регистрация не должна быть повсеместной», когда я получаю сообщение об ошибке с поля, и единственная возможность отладки, которую я должен выяснить, - это тот файл журнала, который я не хотел делать повсеместным. Извлеченный урок, регистрация должна быть «распространяющейся», очень распространяющейся.
Данк

15
@RubberDuck: с серверным программным обеспечением регистрация - единственный способ выжить. Это единственный способ выяснить ошибку, которая произошла 12 часов назад, а не прямо сейчас на вашем ноутбуке. Не беспокойся о шуме. Используйте программное обеспечение для управления журналами, чтобы вы могли запрашивать ваш журнал (в идеале вы должны также настроить оповещения, которые будут отправлять вам сообщения об ошибках в журналах)
slebetman

6
Наши сотрудники службы поддержки звонят мне и говорят, что «клиент нажимал на некоторые страницы, и появлялась ошибка». Я спрашиваю их, что сказала ошибка, они не знают. Я спрашиваю, что конкретно клиент нажал, они не знают. Я спрашиваю, могут ли они воспроизвести, они не могут. Я согласен, что ведение журнала в большинстве случаев может быть достигнуто с помощью хорошо развернутых регистраторов в нескольких ключевых точках, но маленькие странные сообщения здесь и там также имеют неоценимое значение. Страх регистрации ведет к снижению качества программного обеспечения. Если у вас слишком много данных, удалите их обратно. Не оптимизируйте преждевременно.
Пейс

12

Мои 2 цента ...

Да и нет.

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

Принципы SOLID, в частности, помимо большого количества нюансов, в конечном счете, подчиняются цели «предоставления работоспособного, поддерживаемого программного обеспечения». Таким образом, соблюдение какого-либо конкретного принципа SOLID является самоубийственным и противоречивым, когда это противоречит целям SOLID. И здесь я часто отмечаю, что доставляет козыри ремонтопригодность .

Итак, что насчет D в SOLID ? Что ж, это способствует повышению удобства обслуживания, делая ваш повторно используемый модуль относительно независимым от контекста. И мы можем определить «модуль многократного использования» как «набор кода, который вы планируете использовать в другом отдельном контексте». И это относится к отдельным функциям, классам, наборам классов и программ.

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

Итак, позвольте мне предложить два моих больших замечания :

Во-первых: рисование линий вокруг блоков кода, которые составляют «модуль многократного использования», является вопросом профессионального суждения. И ваше суждение обязательно ограничено вашим опытом.

Если вы в настоящее время не планируете использовать модуль в другом контексте, вероятно, он может беспомощно зависеть от него. Предостережение к предостережению: Ваши планы, вероятно, ошибочны, но это тоже нормально. Чем дольше вы будете писать модуль за модулем, тем более интуитивным и точным будет ваше понимание того, «когда-нибудь мне это понадобится снова». Но вы, вероятно, никогда не сможете ретроспективно сказать: «Я модульный и отделил все в максимально возможной степени, но без излишеств ».

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

Во-вторых: Инвертирующий контроль не равен инъекционным зависимостям .

Это особенно верно, когда вы начинаете вводить зависимости до тошноты . Внедрение зависимостей - полезная тактика для всеобъемлющей стратегии IoC. Но я бы сказал, что DI обладает меньшей эффективностью, чем некоторые другие тактики - например, использование интерфейсов и адаптеров - отдельные точки воздействия контекста внутри модуля.

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

И если вы пишете код для Адаптера, то, Loggerвведен ли он в этот Адаптер или обнаружен Адаптером, вообще чертовски незначительно для общей цели обслуживания. И что более важно, если у вас есть адаптер уровня модуля, возможно, просто абсурдно внедрять его во что-либо. Это написано для модуля.

tl; dr - Хватит суетиться о принципах, не задумываясь о том, почему вы используете эти принципы. И, более практично, просто построить Adapterдля каждого модуля. Используйте свое суждение, решая, где вы проводите границы «модуля». Внутри каждого модуля, идти вперед и обратиться непосредственно к Adapter. И конечно, впрысните настоящий регистратор в Adapter- но не в каждую мелочь, которая может понадобиться.


4
Чувак ... Я бы дал более короткий ответ, но у меня не было времени.
Свидген

2
+1 за использование адаптера. Вы должны изолировать зависимости от сторонних компонентов, чтобы позволить их заменять везде, где это возможно. Ведение журнала является легкой целью для этого - существует множество реализаций ведения журналов, и в большинстве своем они имеют похожие API, поэтому простая реализация адаптера может позволить вам очень легко изменить их. И людям, которые говорят, что вы никогда не поменяете своего провайдера журналов: мне пришлось это сделать, и это вызвало настоящую боль, потому что я не использовал адаптер.
Жюль

«Принципы SOLID, в частности, в дополнение к требованию большого количества нюансов, в конечном счете, подчиняются цели« предоставления работоспособного, поддерживаемого программного обеспечения ».» - это одно из лучших утверждений, которые я видел. Как программист с легким ОКР, я столкнулся с борьбой идеализма и производительности.
ДВК

Каждый раз, когда я вижу +1 или комментарий к старому ответу, я снова читаю свой ответ и снова обнаруживаю, что я ужасно неорганизованный и неясный автор ... Я ценю этот комментарий, @DVK.
svidgen

Адаптеры спасают жизнь, они не стоят дорого и делают вашу жизнь намного проще, особенно когда вы хотите применить SOLID. Тестирование с ними очень просто.
Алан

8

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

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

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


2
Каково ваше предлагаемое решение, тогда? Внедряет ли ILogger в каждый класс, который вы пишете? Как насчет использования шаблона Decorator?
Роберт Харви

4
Не совсем отвечаю на мой вопрос сейчас, не так ли? Я назвал шаблоны просто в качестве примеров ... Это не обязательно должно быть так, если вы думаете о чем-то лучшем.
Роберт Харви

8
Я не очень понимаю этот ответ, с точки зрения каких-либо конкретных предложений
Брайан Агнью

3
@RobbieDee - То есть вы говорите, что ведение журнала должно быть реализовано самым запутанным и неудобным способом, так как «случайно» мы можем захотеть войти в несколько мест? Даже если произошло подобное изменение, действительно ли вы думаете, что добавление этой функциональности к существующему экземпляру Logger будет более трудоемким, чем все усилия по передаче этого Logger между классами и изменением интерфейсов, когда вы решите, хотите ли вы Logger или нет десятки проектов, где никогда не будет происходить многократное ведение журнала?
Данк

2
Re: «вы можете захотеть войти в несколько мест в зависимости от характера вывода»: Конечно, но лучший способ справиться с этим - в каркасе ведения журнала , а не пытаться внедрить несколько отдельных зависимостей ведения журнала. (
Обычные

4

Ведение журнала действительно является особым случаем.

@Telastyn пишет:

Вы действительно никогда не собираетесь менять свою библиотеку журналов, или цель, или фильтрацию, или форматирование, или ...?

Если вы ожидаете, что вам может понадобиться изменить библиотеку журналов, то вам следует использовать фасад; т.е. SLF4J, если вы находитесь в мире Java.

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

Короче говоря, это решенные проблемы ... для ведения журнала ... и, следовательно, нет необходимости использовать Dependency Injection для их решения.

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

@Telastyn пишет:

И тогда вы получите аргумент, что сам регистратор будет обрабатывать изменения. «О, если цель / фильтрация / форматирование / стратегия изменятся, тогда мы просто изменим конфигурацию!» Это мусор. Мало того, что теперь у вас есть объект God, который обрабатывает все эти вещи, вы пишете свой код в XML (или аналогичный), где вы не получаете статический анализ, вы не получаете ошибки времени компиляции, и вы не ' действительно получить эффективные юнит-тесты.

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

Конечно, если вы заполняете конфигурацию регистрации, вы можете получить проблемы, но они будут проявляться либо как сбой приложения во время запуска, либо слишком много / слишком мало регистрации. 1) Эти проблемы легко исправить, исправив ошибку в файле конфигурации. 2) Альтернативой является полный цикл сборки / анализа / тестирования / развертывания каждый раз, когда вы вносите изменения в уровни ведения журнала. Это не приемлемо.


3

Да и нет !

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

Тем не мение,

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


Это одна из причин, по которой многие думают о модульности уровней: методы объединяются в классы, а классы объединяются в подсистемы и / или семантические уровни. Эти большие связки являются ценными инструментами абстракции; мы должны дать другие соображения в рамках модульных границ, чем при их пересечении.


3

Сначала он начинается с сильного одноэлементного кэша, следующие вещи, которые вы видите, это сильные синглеты для уровня базы данных, представляющие глобальное состояние, неописательные API-интерфейсы classes и непроверяемый код.

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

Использование синглтона в классе превращает класс, имеющий определенное количество зависимостей, в класс, имеющий теоретически бесконечное их количество, потому что вы никогда не знаете, что действительно скрыто за статическим методом.

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


Да, регистрация редко изменяется и обычно не должна быть сменным модулем. Однако однажды я попытался выполнить юнит-тестирование вспомогательного класса ведения журнала, который имел статическую зависимость от системы ведения журнала. Я решил, что самый простой механизм сделать его тестируемым - это запустить тестируемый класс в отдельном процессе, настроить его регистратор на запись в STDOUT и проанализировать этот вывод в моем тестовом примере case_ಠ У меня был похожий опыт с часами, где вы ' Очевидно, я никогда не хотел ничего, кроме реального времени, верно? За исключением случаев, когда тестируются случаи с часовым поясом / DST, конечно…
am

@amon: часы похожи на вход в систему, потому что уже есть другой механизм, который служит для той же цели, что и DI, а именно Joda-Time и его многочисленные порты. (Нет ничего плохого в том, чтобы использовать DI вместо этого; но проще использовать Joda-Time напрямую, чем пытаться написать нестандартный инъекционный адаптер, и я никогда не видел, чтобы кто-то сожалел об этом.)
ruakh,

@amon «У меня был подобный опыт работы с часами, когда вы, очевидно, никогда не захотите ничего, кроме реального времени, верно? За исключением случаев, когда вы тестируете случаи с часовым поясом / DST, конечно…» - или когда вы понимаете, что ошибка повреждена ваша база данных, и единственная надежда на ее возвращение состоит в том, чтобы проанализировать ваш журнал событий и воспроизвести его, начиная с последней резервной копии ... но внезапно вам нужен весь ваш код для работы, основанный на отметке времени текущей записи журнала, а не текущей время.
Жюль

3

Да и нет, но в основном нет

Я предполагаю, что большая часть разговора основана на статическом и внедренном экземпляре. Никто не предлагает, чтобы регистрация сломала SRP, который я предполагаю? В основном мы говорим о «принципе инверсии зависимостей». Я в основном согласен с тем, что Теластин не ответил.

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

Возьмите: Convert.ToInt32("1")

Я предпочитаю это:

private readonly IConverter _converter;

public MyClass(IConverter converter)
{
   Guard.NotNull(converter)
   _converter = conveter
}

.... 
var foo = _converter.ToInt32("1");

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

Глядя на другой конец спектра, если бы IConverterбыл SqlConnection, я был бы довольно испуган, чтобы видеть это как статический вызов. Причины этого очевидны. Я бы SQLConnectionотметил, что a может быть довольно «сквозным» в приложении, поэтому я бы не использовал эти точные слова.

Ведение журнала больше похоже на SQLConnectionили Convert.ToInt32? Я бы сказал больше как «SQLConnection».

Вы должны быть насмешливым . Это говорит с внешним миром. Когда я пишу метод с использованием Convert.ToIn32, я использую его как инструмент для вычисления некоторых других отдельно проверяемых выходных данных класса. Мне не нужно, чтобы проверка Convertбыла вызвана правильно при проверке, что "1" + "2" == "3". Ведение журнала отличается, это совершенно независимый вывод класса. Я предполагаю, что это результат, который имеет значение для вас, команды поддержки и бизнеса. Ваш класс не работает, если регистрация не правильная, поэтому модульные тесты не должны пройти. Вы должны тестировать то, что записывает ваш класс. Я думаю, что это убийственный аргумент, я мог бы просто остановиться здесь.

Я также думаю, что это что-то, что вполне может измениться. Хорошая регистрация не просто печатает строки, это взгляд на то, что делает ваше приложение (я большой поклонник регистрации событий). Я видел, как базовые логи превращаются в довольно сложные пользовательские интерфейсы отчетности. Очевидно, что намного легче идти в этом направлении, если ваши записи выглядят как, _logger.Log(new ApplicationStartingEvent())а не как Logger.Log("Application has started"). Кто-то может утверждать, что это создает инвентарь для будущего, которое может никогда не произойти, это суждение, и я думаю, что оно того стоит.

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

Так что я бы согласился с Теластином на случай регистрации.


Вот так я случайно захожу . Я хотел бы дать ссылку на статью или что-то, но я не могу найти одну. Если вы находитесь в мире .NET, и посмотрите журнал событий, вы найдете Semantic Logging Application Block. Не используйте его, как и большая часть кода, созданного командой MS «Шаблоны и практика», по иронии судьбы, это антипаттерн.
Натан Купер

3

Первые сквозные проблемы не являются основными строительными блоками и не должны рассматриваться как зависимости в системе. Система должна работать, если, например, Logger не инициализирован или кеш не работает. Как вы сделаете систему менее связанной и связной? Вот где SOLID входит в картину в дизайне ОО системы.

Хранение объекта как синглтона не имеет ничего общего с SOLID. Это жизненный цикл вашего объекта, как долго вы хотите, чтобы объект жил в памяти.

Класс, для инициализации которого требуется зависимость, не должен знать, является ли предоставленный экземпляр класса одноэлементным или временным. Но тлдр; если вы пишете Logger.Instance.Log () в каждом методе или классе, то это проблемный код (запах кода / жесткая связь), он действительно очень грязный. Это момент, когда люди начинают злоупотреблять SOLID. И коллеги-разработчики, такие как OP, начинают задавать такие вопросы.


2

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


Интересный. Я никогда не делал какой-либо значительной работы с использованием языка, который поддерживает черты, но я рассмотрел возможность использования таких языков для некоторых будущих проектов, поэтому было бы полезно, если бы вы могли расширить это и показать, как черты решают проблему.
Жюль
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.