У меня есть метод, который создает файл данных после разговора с цифровой платой:
CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)
Здесь boardFileAccess
и boardMeasurer
тот же экземпляр Board
объекта, который реализует оба IFileAccess
и IMeasurer
. IMeasurer
используется в этом случае для одного метода, который установит один вывод на плате активным, чтобы сделать простое измерение. Данные этого измерения затем сохраняются локально на плате с помощью IFileAccess
. Board
находится в отдельном проекте.
Я пришел к выводу, что CreateDataFile
делать одну вещь - сделать быстрое измерение, а затем сохранить данные, и выполнение обоих в одном и том же методе является более интуитивно понятным для кого-то, кто использует этот код, чем для того, чтобы выполнить измерение и записать в файл. как отдельные вызовы методов.
Мне кажется неудобным передавать один и тот же объект методу дважды. Я рассмотрел создание локального интерфейса, IDataFileCreator
который будет расширяться, IFileAccess
а IMeasurer
затем иметь реализацию, содержащую Board
экземпляр, который будет просто вызывать необходимые Board
методы. Учитывая, что один и тот же объект платы всегда будет использоваться для измерения и записи файла, является ли плохой практикой передавать один и тот же объект методу дважды? Если да, то является ли использование локального интерфейса и реализации подходящим решением?
источник
Ответы:
Нет, это прекрасно. Это просто означает, что API перегружен в отношении вашего текущего приложения .
Но это не доказывает, что никогда не будет варианта использования, в котором источник данных и измеритель различны. Задача API - предложить программисту возможности, не все из которых будут использоваться. Вы не должны искусственно ограничивать то, что могут делать пользователи API, если это не усложняет API, так что понятность сети снижается.
источник
Согласитесь с ответом @ KilianFoth, что это прекрасно.
Тем не менее, если вы хотите, вы можете создать метод, который принимает один объект, который реализует оба интерфейса:
Нет общей причины, по которой аргументы должны быть разными объектами, и если метод требует, чтобы аргументы были разными, это было бы особым требованием, которое должен четко указывать контракт.
источник
Я думаю, что это ваша проблема, на самом деле. Метод не делает одну вещь. Он выполняет две отдельные операции, связанные с вводом-выводом на разные устройства , которые выгружаются на другие объекты:
Это две разные операции ввода / вывода. Примечательно, что первый никоим образом не изменяет файловую систему.
На самом деле, мы должны отметить, что есть промежуточный шаг:
Ваш API должен предоставлять каждый из них отдельно в некоторой форме. Откуда вы знаете, что вызывающий абонент не захочет проводить измерения, не сохраняя их где-либо? Откуда вы знаете, что они не захотят получить измерение из другого источника? Откуда вы знаете, что они не захотят хранить его где-то кроме устройства? Есть веская причина отделить операции. На голой минимум, каждая отдельная часть должна быть доступна любому абоненту. Я не должен быть принужден записывать измерение в файл, если мой вариант использования не требует его.
Например, вы можете разделить операции следующим образом.
IMeasurer
есть способ получить измерение:Ваш тип измерения может быть просто чем-то простым, например,
string
илиdecimal
. Я не настаиваю на том, что вам нужен интерфейс или класс для него, но это делает пример здесь более общим.IFileAccess
есть метод для сохранения файлов:Тогда вам нужен способ сериализации измерения. Встраивайте это в класс или интерфейс, представляющий измерение, или используйте служебный метод:
Пока не ясно, выделена ли у вас эта операция сериализации.
Такое разделение улучшает ваш API. Это позволяет вызывающему абоненту решать, что ему нужно и когда, а не заставлять ваши предвзятые представления о том, что выполнять ввод / вывод. Вызывающие должны иметь контроль для выполнения любой действительной операции, независимо от того, считаете ли вы ее полезной или нет.
Если у вас есть отдельные реализации для каждой операции, ваш
CreateDataFile
метод становится просто сокращением дляПримечательно, что ваш метод добавляет очень мало пользы после того, как вы все это сделали. Вышеуказанная строка кода не является сложной для непосредственного использования вашими абонентами, и ваш метод предназначен исключительно для удобства. Это должно быть и является чем-то необязательным . И это правильный способ поведения API.
После того, как все соответствующие части будут учтены, и мы признаем, что метод просто удобен, нам нужно перефразировать ваш вопрос:
Что будет наиболее распространенным вариантом использования для ваших абонентов?
Если весь смысл состоит в том, чтобы сделать типичный вариант использования измерения и записи на одну и ту же доску немного более удобным, то имеет смысл просто сделать его доступным
Board
непосредственно в классе:Если это не улучшит удобство, то я не стал бы беспокоиться о методе вообще.
Этот удобный метод поднимает еще один вопрос.
Должен ли
IFileAccess
интерфейс знать о типе измерения и как его сериализовать? Если это так, вы можете добавить метод кIFileAccess
:Теперь звонящие просто делают это:
который так же короток и, вероятно, более понятен, чем ваш удобный метод, как задумано в этом вопросе.
источник
Клиент-потребитель не должен иметь дело с парой предметов, когда достаточно одного предмета. В вашем случае они почти нет, до вызова
CreateDataFile
.Потенциальное решение, которое вы предлагаете, заключается в создании комбинированного производного интерфейса. Тем не менее, этот подход требует одного объекта, который реализует оба интерфейса, что является довольно ограничивающим, возможно, утечка абстракции в том, что он в основном настроен для конкретной реализации. Подумайте, насколько сложно было бы, если бы кто-то захотел реализовать два интерфейса в отдельных объектах: ему пришлось бы проксировать все методы в одном из интерфейсов, чтобы перенаправить его на другой объект. (FWIW, другой вариант - просто объединить интерфейсы, а не требовать, чтобы один объект реализовал два интерфейса через производный интерфейс.)
Тем не менее, другой подход, который менее ограничивает / диктует реализацию, заключается в том, что
IFileAccess
он связан сIMeasurer
составом in, так что один из них связан с другим и ссылается на него. (Это несколько увеличивает абстракцию одного из них, поскольку теперь оно также представляет сопряжение.) ТогдаCreateDataFile
можно взять, скажемIFileAccess
, только одну из ссылок , и при этом получить другую при необходимости. Ваша текущая реализация как объект , который реализует оба интерфейса будет простоreturn this;
для справки композиции, здесь геттер дляIMeasurer
вIFileAccess
.Если в какой-то момент разработки спаривание оказывается ложным, то есть иногда используется другой измеритель с тем же доступом к файлу, вы можете выполнить такое же спаривание, но на более высоком уровне, то есть введенный дополнительный интерфейс будет не быть производным интерфейсом, а скорее интерфейсом с двумя получателями, соединяющими доступ к файлу и измеритель вместе с помощью композиции, а не деривации. В этом случае клиент-потребитель имеет один элемент, с которым ему нужно иметь дело до тех пор, пока сохраняется сопряжение, и отдельные объекты, с которыми нужно иметь дело (для создания новых пар), когда это необходимо.
С другой стороны, я мог бы спросить, кому принадлежит
CreateDataFile
, и вопрос идет к тому, кто эта третья сторона. У нас уже есть некоторый клиент-потребитель, который вызываетCreateDataFile
объект-классCreateDataFile
, владеющий им , иIFileAccess
иIMeasurer
. Иногда, когда мы более широко рассматриваем контекст, могут появиться альтернативные, иногда лучшие организации. Трудно сделать здесь, так как контекст неполный, так что просто пища для размышлений.источник
Некоторые воспитывают, что
CreateDataFile
делает слишком много. Я мог бы предположить, что вместо этогоBoard
делает слишком много, так как доступ к файлу кажется отдельной задачей от остальной части платы.Однако, если мы предположим, что это не ошибка, большая проблема заключается в том, что в этом случае клиент должен определить интерфейс
CreateDataFile
.Принцип разделения интерфейса гласит, что клиент не должен зависеть от большего количества интерфейса, чем ему нужно. Заимствуя фразу из этого другого ответа , это можно перефразировать как «интерфейс определяется тем, что нужно клиенту».
Теперь можно составить этот специфичный для клиента интерфейс с использованием
IFileAccess
и,IMeasurer
как предполагают другие ответы, но, в конечном счете, этот клиент должен иметь специально разработанный для него интерфейс.источник