Принцип разделения интерфейса гласит:
Ни один клиент не должен зависеть от методов, которые он не использует. Интернет-провайдер разделяет очень большие интерфейсы на более мелкие и более специфичные, чтобы клиенты могли знать только о методах, которые им интересны.
Здесь есть несколько вопросов без ответа. Один:
Как маленький?
Ты говоришь:
В настоящее время я занимаюсь этим, разделяя пространство имен модуля в зависимости от требований его клиентов.
Я называю это ручным вводом утки . Вы создаете интерфейсы, которые предоставляют только то, что нужно клиенту. Принцип разделения интерфейса - это не просто ручная печать.
Но Интернет-провайдер - это не просто призыв к «согласованным» интерфейсам ролей, которые можно использовать повторно. Никакой «согласованный» дизайн интерфейса ролей не может полностью защитить от добавления нового клиента с его собственными потребностями ролей.
ISP - это способ изолировать клиентов от влияния изменений в сервисе. Он был предназначен для ускорения сборки при внесении изменений. Конечно, у него есть и другие преимущества, например, не ломать клиентов, но это было главным. Если я изменяю count()
сигнатуру сервисных функций, было бы хорошо, если бы неиспользуемые клиенты count()
не нуждались в редактировании и перекомпиляции.
Вот почему я забочусь о принципе разделения интерфейсов. Это не то, что я считаю верой, как важно. Это решает реальную проблему.
Таким образом, способ его применения должен решить проблему для вас. Не существует интеллектуального способа применить провайдера, который нельзя победить только правильным примером необходимого изменения. Предполагается, что вы посмотрите на то, как меняется система, и сделаете выбор, который позволит успокоиться. Давайте рассмотрим варианты.
Сначала спросите себя: сложно ли сейчас вносить изменения в интерфейс сервиса? Если нет, выходите на улицу и играйте, пока не успокоитесь. Это не интеллектуальное упражнение. Пожалуйста, убедитесь, что лекарство не хуже, чем болезнь.
Если многие клиенты используют одно и то же подмножество функций, это говорит о «связных» повторно используемых интерфейсах. Подмножество, вероятно, сосредоточено вокруг одной идеи, которую мы можем рассматривать как роль, которую служба предоставляет клиенту. Приятно, когда это работает. Это не всегда работает.
Если многие клиенты используют разные подмножества функций, возможно, что клиент фактически использует службу через несколько ролей. Это нормально, но это делает роли трудно различимыми. Найди их и постарайся дразнить. Это может вернуть нас в случае 1. Клиент просто использует сервис через несколько интерфейсов. Пожалуйста, не начинайте пользоваться услугой. Во всяком случае, это означало бы передачу услуги клиенту более одного раза. Это работает, но это заставляет меня задаться вопросом, не является ли сервис большим клубком грязи, который нужно разбить.
Если многие клиенты используют разные подмножества, но вы не видите роли, даже если клиенты могут использовать более одного, у вас нет ничего лучше, чем печатать на клавиатуре, чтобы разрабатывать свои интерфейсы. Такой способ проектирования интерфейсов гарантирует, что клиент не будет подвержен даже одной функции, которую он не использует, но он почти гарантирует, что добавление нового клиента всегда будет включать добавление нового интерфейса, который в то время как реализация службы не должна знать об этом интерфейс, который агрегирует роль интерфейсов. Мы просто обменяли одну боль на другую.
Если многие клиенты используют разные подмножества, накладываются друг на друга, ожидается добавление новых клиентов, которым потребуются непредсказуемые подмножества, и вы не захотите сломать службу, а затем подумайте о более функциональном решении. Поскольку первые две опции не сработали, и вы действительно находитесь в плохом месте, где ничто не следует шаблону, и грядут дополнительные изменения, тогда рассмотрите возможность предоставления каждой функции свой собственный интерфейс. Завершение здесь не означает, что провайдер провалился. Если что-то не получалось, это была объектно-ориентированная парадигма. Интерфейсы с одним методом следуют за ISP в крайнем случае. Это довольно сложный ввод с клавиатуры, но вы можете обнаружить, что это внезапно делает интерфейсы многоразовыми. Опять же, убедитесь, что нет
Получается, что они могут стать очень маленькими.
Я воспринял этот вопрос как вызов для применения ISP в самых крайних случаях. Но имейте в виду, что крайностей лучше избегать. В хорошо продуманном дизайне, который применяет другие принципы SOLID, эти проблемы обычно не возникают или имеют значение, почти столько же.
Еще один вопрос без ответа:
Кому принадлежат эти интерфейсы?
Снова и снова я вижу интерфейсы, разработанные на основе того, что я называю «библиотечным» менталитетом. Мы все были виновны в кодировании monkey-see-monkey-do, когда вы просто что-то делаете, потому что именно так вы и видели. Мы виноваты в одном и том же с интерфейсами.
Когда я смотрел на интерфейс, разработанный для класса в библиотеке, я думал: о, эти ребята профессионалы. Это должен быть правильный способ сделать интерфейс. Что я не смог понять, так это то, что у границ библиотеки есть свои потребности и проблемы. С одной стороны, библиотека совершенно не знает о дизайне своих клиентов. Не все границы одинаковы. И иногда даже одна и та же граница имеет разные пути ее пересечения.
Вот два простых взгляда на дизайн интерфейса:
Сервисный интерфейс. Некоторые люди разрабатывают каждый интерфейс, чтобы показать все, что может сделать сервис. Вы даже можете найти опции рефакторинга в IDE, которые напишут интерфейс для вас, используя любой класс, которым вы его кормите.
Клиентский интерфейс. Интернет-провайдер, кажется, утверждает, что это правильно, а принадлежащий сервис - неправильно. Вы должны разбить каждый интерфейс с учетом потребностей клиентов. Поскольку клиент владеет интерфейсом, он должен его определить.
Так кто же прав?
Рассмотрим плагины:
Кому здесь принадлежат интерфейсы? Клиенты? Услуги?
Оказывается оба.
Цвета здесь слои. Красный слой (справа) не должен ничего знать о зеленом слое (слева). Зеленый слой можно изменить или заменить, не касаясь красного слоя. Таким образом, любой зеленый слой может быть подключен к красному слою.
Мне нравится знать, что должно знать о чем, а что не должно знать. Для меня «что знает о чем?», Это самый важный архитектурный вопрос.
Давайте проясним некоторые слова:
[Client] --> [Interface] <|-- [Service]
----- Flow ----- of ----- control ---->
Клиент - это то, что использует.
Служба - это то, что используется.
Interactor
бывает оба.
Интернет-провайдер говорит, что нужно разбивать интерфейсы для клиентов. Хорошо, давайте применим это здесь:
Presenter
(сервис) не должен диктовать Output Port <I>
интерфейс. Интерфейс должен быть сужен к тому, что Interactor
(здесь действует как клиент) необходимо. Это означает, что интерфейс ЗНАЕТ о, Interactor
и, чтобы следовать за ISP, должен измениться с этим. И это нормально.
Interactor
(здесь действует как сервис) не должен диктовать Input Port <I>
интерфейс. Интерфейс должен быть сужен до Controller
потребностей клиента. Это означает, что интерфейс ЗНАЕТ о, Controller
и, чтобы следовать за ISP, должен измениться с этим. И это не хорошо.
Второй не подходит, потому что красный слой не должен знать о зеленом слое. Так что провайдер не прав? Ну вроде. Ни один принцип не является абсолютным. Это тот случай, когда глупцы, которым нравится интерфейс, показывающий все, что может сделать сервис, оказываются правы.
По крайней мере, они правы, если Interactor
не делают ничего, кроме потребностей этого варианта использования. Если Interactor
они работают для других вариантов использования, то нет причин Input Port <I>
, о которых нужно знать. Не уверен, почему Interactor
нельзя просто сосредоточиться на одном прецеденте, так что это не проблема, но вещи случаются.
Но input port <I>
интерфейс просто не может подчинить себя Controller
клиенту, и это будет настоящий плагин. Это граница «библиотеки». Совсем другой магазин программирования мог бы писать зеленый слой спустя годы после того, как красный слой был опубликован.
Если вы пересекаете границу «библиотеки» и чувствуете необходимость применения интернет-провайдера, даже если у вас нет интерфейса на другой стороне, вам нужно будет найти способ сузить интерфейс, не меняя его.
Один из способов сделать это - адаптер. Поместите это между клиентами, как Controler
и Input Port <I>
интерфейс. Адаптер принимает Interactor
как Input Port <I>
и делегирует свою работу ему. Однако он раскрывает только то, что Controller
нужно клиентам, через ролевый интерфейс или интерфейсы, принадлежащие зеленому слою. Адаптер не следует самому ISP, но позволяет более сложному классу, например, Controller
наслаждаться ISP. Это полезно, если адаптеров меньше, чем используют клиенты Controller
, использующие их, и когда вы находитесь в необычной ситуации, когда вы пересекаете границу библиотеки и, несмотря на публикацию, библиотека не перестанет меняться. Глядя на тебя Firefox. Теперь эти изменения только сломают ваши адаптеры.
Так что это значит? Честно говоря, это означает, что вы не предоставили мне достаточно информации, чтобы сказать вам, что вам следует делать. Я не знаю, если не следование ISP вызывает у вас проблемы. Я не знаю, приведет ли это к тому, что у вас возникнут дополнительные проблемы.
Я знаю, что вы ищете простой руководящий принцип. ISP пытается быть таким. Но это оставляет много недосказанного. Я верю в это. Да, пожалуйста, не заставляйте клиентов полагаться на методы, которые они не используют, без веской причины!
Если у вас есть веская причина, например, вы разрабатываете что-то, чтобы принимать плагины, то помните о проблемах, не связанных с причинами провайдера (трудно изменить, не нарушая клиентов), и о способах их устранения (сохраняйте Interactor
или хотя бы Input Port <I>
сосредотачивайтесь на одном стабильном). вариант использования).