Я использую большой интерфейс с около 50 методов для доступа к базе данных. Интерфейс был написан моим коллегой. Мы обсуждали это:
Я: 50 методов это слишком много. Это кодовый запах.
Коллега: что мне с этим делать? Вы хотите доступ к БД - у вас есть.
Я: Да, но это неясно и вряд ли будет исправимо в будущем.
Коллега: хорошо, вы правы, это не приятно. Как должен выглядеть интерфейс?
Я: Как насчет 5 методов, которые возвращают объекты, которые имеют, например, 10 методов каждый?
Ммм, но разве это не будет так же? Это действительно приводит к большей ясности? Стоит ли усилий?
Время от времени я нахожусь в ситуации, когда мне нужен интерфейс, и первое, что приходит на ум, это один большой интерфейс. Есть ли общий шаблон дизайна для этого?
Обновление (в ответ на комментарий Сюаня):
«Методы»: это интерфейс для извлечения данных из базы данных. Все методы имеют вид (псевдокод)
List<Typename> createTablenameList()
Методы и таблицы не находятся точно в отношении 1-1, акцент делается больше на том факте, что вы всегда получаете какой-то список, который поступает из базы данных.
источник
UserDao
и aCustomerDao
и aProductDao
)List<WeatherDataRecord> createWeatherDataTable() {db.open(); return db.select("*", "tbl_weatherData");}
Ответы:
Да, 50 методов - это запах кода, но запах кода означает еще раз взглянуть на него, а не то, что он автоматически ошибается. Если каждому клиенту, использующему этот класс, потенциально нужны все 50 методов, может не быть случая для его разделения. Однако это маловероятно. Моя точка зрения заключается в том, что произвольное разделение интерфейса может быть хуже, чем полное его отсутствие.
Не существует единого шаблона для его исправления, но принцип, который описывает желаемое состояние, - это принцип разделения интерфейса («I» в SOLID), который гласит, что ни одного клиента не следует заставлять зависеть от методов, которые он не использует. ,
Описание провайдера дает вам подсказку, как это исправить: посмотрите на клиента . Часто, просто глядя на класс, кажется, что все принадлежит друг другу, но при взгляде на клиентов, использующих этот класс, возникают четкие различия . Всегда учитывайте клиентов в первую очередь при разработке интерфейса.
Другой способ определить, следует ли разделять интерфейс, - это сделать вторую реализацию. Часто случается, что вашей второй реализации не нужно много методов, поэтому их следует разделить на собственный интерфейс.
источник
Это не очень хорошие критерии (в этом утверждении вообще нет критериев). Вы можете сгруппировать их (для моих примеров, если ваше приложение является приложением для финансовых транзакций):
Если вы выбираете правильные критерии, безусловно. Если нет, то определенно нет :).
Несколько примеров:
посмотрите на объекты ADODB для упрощенного примера примитивов OO (ваш DB API, вероятно, уже предлагает это)
посмотрите на модель данных Django ( https://docs.djangoproject.com/en/dev/topics/db/models/ ) для идеи модели данных с высоким уровнем абстракции (в C ++ вам, вероятно, понадобится немного больше котла код, но это хорошая идея). Эта реализация разработана с учетом роли «модели» в схеме проектирования MVC.
посмотрите на API sqlite, чтобы получить представление об плоском API ( http://www.sqlite.org/c3ref/funclist.html ), состоящем только из функциональных примитивов (C API).
источник
Это анти-шаблон дизайна, называемый монолитным классом . Наличие 50 методов в классе или интерфейсе является вероятным нарушением SRP . Монолитный класс возникает потому, что он пытается быть всем для всех.
DCI адресует метод раздувания. По существу, многие обязанности класса могут быть распределены между ролями (перенесены в другие классы), которые имеют отношение только в определенных контекстах. Применение ролей может быть достигнуто несколькими способами, включая миксины или декораторы . Такой подход делает занятия сосредоточенными и стройными.
Это предполагает создание всех ролей, когда создается сам объект. Но зачем создавать роли, которые вам могут не понадобиться? Вместо этого создайте экземпляр роли в контексте, в котором она вам действительно нужна.
Если вы обнаружите, что рефакторинг в сторону DCI неочевиден, вы можете использовать более простой шаблон посетителей . Это обеспечивает аналогичное преимущество, не подчеркивая создание контекстов вариантов использования.
РЕДАКТИРОВАТЬ: мое мышление по этому вопросу изменилось. Я дал альтернативный ответ.
источник
Мне кажется, каждый ответ не имеет смысла. Дело в том, что в идеале интерфейс должен определять элементарный фрагмент поведения. Это Я в ТВЕРДОМ.
У класса должна быть одна ответственность, но она может включать в себя несколько вариантов поведения. Чтобы придерживаться типичного объекта клиента базы данных, это может предложить полную функциональность CRUD. Это было бы четыре поведения: создавать, читать, обновлять и удалять. В чистом мире SOLID клиент базы данных будет реализовывать не IDatabaseClient, а неустановившиеся ICreator, IReader, IUpdater и IDeleter.
Это будет иметь ряд преимуществ. Во-первых, просто прочитав объявление класса, можно мгновенно узнать много нового о классе, и интерфейсы, которые он реализует, рассказывают всю историю. Во-вторых, если клиентский объект должен быть передан в качестве аргумента, у него теперь есть разные полезные опции. Его можно передать как IReader, и можно быть уверенным, что вызываемый будет только в состоянии читать. Различное поведение может быть проверено отдельно.
Однако, когда дело доходит до тестирования, обычной практикой является просто шлепнуть интерфейс по классу, который является копией 1-к-1 полного интерфейса класса. Если тестирование - это все, что вас волнует, это может быть правильным подходом. Это позволяет делать манекены довольно быстро. Но это вряд ли когда-либо SOLID и действительно злоупотребление интерфейсами для определенной цели.
Так что да, 50 методов - это запах, но это зависит от намерения и цели, плохо это или нет. Это, конечно, не идеально.
источник
Уровни доступа к данным, как правило, имеют множество методов, привязанных к одному классу. Если вы когда-либо работали с Entity Framework или другими инструментами ORM, вы увидите, что они генерируют сотни методов. Я полагаю, что вы и ваш коллега выполняете это вручную. Нет необходимости пахнуть кодом, но на это не приятно смотреть. Не зная вашего домена, сложно сказать.
источник
Я использую протоколы (назовите их интерфейсами, если хотите) почти универсально для всех API с FP и OOP. (Помните Матрицу? Конкреций нет!) Конечно, существуют конкретные типы, но в рамках программы каждый тип рассматривается как нечто, играющее роль в некотором контексте.
Это означает, что объекты, передаваемые через программы, в функции и т. Д., Могут рассматриваться как абстрактные объекты с именованными наборами поведения. Объект может рассматриваться как играющий роль, представляющую собой некоторый набор протоколов. Человек (конкретный тип) может быть мужчиной, отцом, мужем, другом и служащим, но я не могу представить себе множество функций, которые бы рассматривали сущность как сумму более двух из них.
Я предполагаю, что вполне возможно, что сложный объект может выдерживать множество различных протоколов, но вам все равно будет сложно получить API с 50 методами. Большинство протоколов имеют 1 или 2 метода, возможно 3, но никогда не 50! Любая сущность, имеющая 50 методов, должна быть объединена в группу более мелких компонентов, каждый из которых имеет свои обязанности. Сущность в целом представила бы более простой интерфейс, который абстрагирует общую сумму apis в нем.
Вместо того, чтобы думать с точки зрения объектов и методов, начните думать с точки зрения абстракций и контрактов, а также роли, которую субъект играет в каком-то контексте.
источник