SOLID Принципы и структура кода

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

Вопрос интервью:

  

Если бы вы взглянули на проект .Net, я сказал вам строго следовать принципам SOLID, что бы вы ожидали увидеть с точки зрения структуры проекта и кода?

Я немного пошатнулся, на самом деле не ответил на этот вопрос, а затем разбомбил.

Как я мог бы лучше справиться с этим вопросом?

146 голосов | спросил S-Unit 24 J0000006Europe/Moscow 2013, 18:09:29

6 ответов


183

S = принцип единой ответственности

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

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

O = открытый /закрытый принцип

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

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

L = принцип замещения Лискова

Это связано с возможностью обработки подтипов в качестве родительского типа. Это происходит из коробки на C #, если вы реализуете правильную иерархию наследуемых объектов.

Я ожидаю увидеть код, рассматривающий общие объекты как их базовый тип и методы вызова на базовых /абстрактных классах, а не создание экземпляров и работу над самими подтипами.

I = Принцип разделения цепей

Это похоже на SRP. В принципе, вы определяете меньшие подмножества функциональности в качестве интерфейсов и работаете с ними, чтобы поддерживать развязанность системы (например, FileManager может иметь единственную ответственность за работу с файловыми вводами-выводами, но это может реализовать IFileReader и IFileWriter, который содержит определения определенных методов для чтения и записи файлов).

D = Принцип инверсии зависимостей.

Опять же, это относится к поддержанию развязки системы. Возможно, вы были бы в поиске библиотеки .NET Dependency Injection, используемой в решении, таком как Unity или Ninject или система ServiceLocator, такая как AutoFacServiceLocator.

ответил Eoin Campbell 24 J0000006Europe/Moscow 2013, 18:25:45
16

Множество небольших классов и интерфейсов с инъекцией зависимостей повсюду. Возможно, в большом проекте вы также будете использовать структуру IoC, чтобы помочь вам построить и управлять временем жизни всех этих небольших объектов. См. https://stackoverflow.com/questions/21288/which Сетчатая-зависимости нагнетательная-рамка-это-стоит выглядящей-в

Обратите внимание, что большой проект .NET, который СТРОГО следует принципам SOLID, не обязательно означает хорошую кодовую базу для работы со всеми. В зависимости от того, кем был интервьюер, он, возможно, хотел, чтобы вы показали, что вы понимаете, что означает SOLID, и /или проверяйте, насколько догматически вы следуете принципам дизайна.

Вы видите, что для SOLID вам нужно следовать:

S ingle принцип ответственности, поэтому у вас будет много маленьких классов, каждый из которых делает только одну вещь

O принцип закрывания пера, который в .NET обычно реализуется с помощью инъекции зависимостей, что также требует, чтобы I и D ниже ...

L . Принцип замены исков iskov, вероятно, невозможно объяснить в c # одним слоем. К счастью, к нему относятся другие вопросы, например. https://stackoverflow.com/вопросы /4428725 /может вы-объяснить-Лисков-заместительная-принцип-с-хорошим-диез-пример

I nterface Принцип сегрегации работает в тандеме с принципом Open-Closed. Если следовать буквально, это означало бы предпочтение большому числу очень маленьких интерфейсов, а не только несколько «больших» интерфейсов

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

ответил Paolo Falabella 24 J0000006Europe/Moscow 2013, 18:30:25
12

Некоторые основные вещи, которые я ожидал бы увидеть в кодовой базе магазина, которая поддерживала SOLID в своей повседневной работе:

  • Многие небольшие файлы кода - с одним классом на один файл в качестве лучшей практики в .NET и принципом единой ответственности, поощряющим небольшие модульные структуры классов, я ожидаю увидеть много файлов, каждый из которых содержит один небольшой сфокусированный класс.
  • Множество адаптивных и композитных шаблонов - я бы ожидал использования множества шаблонов адаптера (класс, реализующий один интерфейс путем «прохождения» на функциональность другого интерфейса), чтобы упростить подключение зависимостей, разработанных для одного в несколько разных местах, где также необходима его функциональность. Обновления, такие же простые, как замена консольного регистратора файловым регистратором, будут нарушать LSP /ISP /DIP, если интерфейс обновлен, чтобы открыть средство для указания имени файла для использования; вместо этого класс регистратора файлов будет выставлять дополнительные элементы, а затем адаптер сделает регистратор файлов похожим на консольный логгер, спрятав новый материал, так что только объект, привязывающий все это вместе, должен знать разницу.

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

  • Множество интерфейсов и ABCs - DIP обязательно требует наличия абстракций, и интернет-провайдер поощряет их к узкомасштабному использованию. Поэтому интерфейсы и абстрактные базовые классы являются правилом, и вам потребуется много их, чтобы охватить общие функции зависимостей вашей кодовой базы. В то время как для строгого SOLID необходимо вводить все , очевидно, что вам нужно что-то создать, и поэтому, если форма GUI создается только в качестве дочернего элемента из одной родительской формы, выполняя некоторые действия для указанного родителя, у меня есть не возникает проблем с созданием дочерней формы из кода непосредственно внутри родителя. Я просто обычно делаю этот код своим собственным методом, поэтому, если два действия той же формы когда-либо открывали окно, я просто вызываю метод.
  • Многие проекты. Цель всего этого - ограничить масштаб изменений. Изменения включают необходимость перекомпилировать (относительно тривиальное упражнение больше, но все еще важно во многих критических операциях с процессорами и пропускной способностью, таких как развертывание обновлений в мобильной среде). Если один файл в проекте должен быть перестроен, все файлы делают. Это означает, что если вы размещаете интерфейсы в тех же библиотеках, что и их реализации, вам не хватает точки; вам придется перекомпилировать все обычаи, если вы измените реализацию интерфейса, потому что вы также будете перекомпилировать определение интерфейса, требуя от использования указания указать новое место в результирующем двоичном файле. Таким образом, использование интерфейсов отдельно от применений и , а также их разделение по общей области использования является типичной наилучшей практикой.
  • Большое внимание уделяется терминологии «Банда четырех». Шаблоны дизайна, определенные в книге Design Patterns в 1994 году, подчеркивают дизайн модульного кода укуса, который SOLID стремится создать. Принцип инверсии зависимостей и принцип открытого /закрытого, например, лежат в основе большинства идентифицированных шаблонов в этой книге. Таким образом, я ожидаю, что магазин, который строго придерживался принципов SOLID, также включил терминологию в книгу «Банда четырех» и назвал классы в соответствии с их функцией в этих строках, такими как «AbcFactory», «XyzRepository», «DefToXyzAdapter» "," A1Command "и т. Д.
  • Общий репозиторий - в соответствии с ISP, DIP и SRP, как обычно понимается, репозиторий почти повсеместен в дизайне SOLID, поскольку он позволяет потреблять код, запрашивая классы данных абстрактным образом, не требуя конкретных знаний о поиске /сохранении механизм, и он помещает код, который делает это в одном месте, в отличие от шаблона DAO (в котором, если у вас был, например, класс данных счета-фактуры, у вас также был бы InvoiceDAO, который создавал гидратированные объекты этого типа, и так и для всех объектов данных /таблиц в кодовой базе /схеме).
  • Контейнер IoC - я смущаюсь добавить это, так как на самом деле я не использую рамки IoC, чтобы выполнить большую часть моей инъекции зависимостей. Он быстро превращается в анти-шаблон Бога-объекта, бросая все в контейнер, встряхивая его и выливая вертикально-гидратированную зависимость, которую вам нужно, с помощью внедренного заводского метода. Звучит здорово, пока вы не поймете, что структура становится монолитной, а проект с регистрационной информацией, если «свободно», теперь должен знать все обо всем в вашем решении. Это lot причин для изменения. Если это не бегло (поздние регистрации с использованием конфигурационных файлов), то ключевая часть вашей программы опирается на «магические строки»,целые разные могут червями.
ответил KeithS 26 J0000006Europe/Moscow 2013, 04:40:26
9

Отвлечь их Обсуждение Джона Скита о том, как «O» в SOLID «бесполезно и плохо понимается» и заставляют их говорить о «защищенной вариации» Алистера Кокберна и «дизайне для наследования» Джоша Блоха или запрещать его ».

Краткий обзор статьи Скита (хотя я бы не рекомендовал отказаться от его имени, не читая оригинальное сообщение в блоге!):

  • Большинство людей не знают, что означает «открыто» и «закрыто» в «открытом закрытом принципе», даже если они думают, что делают.
  • Общие интерпретации включают:
    • , что модули всегда должны быть расширены через наследование реализации или
    • , что исходный код исходного модуля никогда не может быть изменен.
  • Основная цель OCP и первоначальная формулировка Бертрана Майера - это хорошо:
    • , что модули должны иметь четко определенные интерфейсы (не обязательно в техническом смысле «интерфейса»), от которых зависят их клиенты, , но
    • должно быть возможно расширить то, что они могут сделать, не нарушая эти интерфейсы.
  • Но слова «открытые» и «закрытые» просто путают проблему, даже если они действительно делают хороший объявляемый акроним.

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

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

ответил David Moles 27 PM000000100000002931 2013, 22:07:29
6

Вероятно, существует несколько способов, на которые можно ответить с разным количеством времени. Тем не менее, я думаю, что это больше похоже на «Знаете ли вы, что означает SOLID?» Поэтому ответ на этот вопрос, вероятно, просто сводится к ударам по точкам и объяснению его с точки зрения проекта.

Итак, вы ожидаете увидеть следующее:

  • У классов есть одна ответственность (например, класс доступа к данным для клиентов будет получать данные клиента из базы данных клиента).
  • Классы легко расширяются без какого-либо эффекта. Мне не нужно изменять свойства или другие методы, чтобы добавить дополнительные функции.
  • Производные классы могут быть заменены базовыми классами и функциями, которые используют эти базовые классы, не должны разворачивать базовый класс на более конкретный тип, чтобы обрабатывать их.
  • Интерфейсы малы и понятны. Если класс использует интерфейс, ему не нужно зависеть от нескольких методов для выполнения задачи.
  • Код достаточно абстрагирован, так что реализация на высоком уровне не зависит конкретно от конкретной реализации на низком уровне. Я должен иметь возможность переключать низкоуровневую реализацию без использования высокоуровневого кода. Например, я могу переключить свой уровень доступа к данным SQL для основанного на веб-сервисе без остальной части моего приложения.
ответил villecoder 24 J0000006Europe/Moscow 2013, 18:29:10
4

Это отличный вопрос, хотя я думаю, что это сложный вопрос для интервью.

Принципы SOLID действительно управляют классами и интерфейсами и как они связаны друг с другом.

Этот вопрос действительно тот, который больше связан с файлами и не обязательно с классами.

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

Роберт Мартин обсуждает эту тему в области C ++ в Проектирование объектно-ориентированных приложений на C ++ с использованием метод Booch (см. разделы «Сплоченность, закрытие и повторное использование») и в Очистить код .

ответил J. Polfer 24 J0000006Europe/Moscow 2013, 19:23:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132