Ориентация на объект - где разместить эту декларацию интерфейса - PullRequest
1 голос
/ 07 августа 2009

У меня есть несколько вопросов для вас, мудрые люди, связанные с проектированием ОО с интерфейсами и абстрактными базовыми классами. Рассмотрим следующий сценарий:

У меня есть абстрактный басовый класс "DataObjectBase" и производный класс "UserDataObject". У меня также есть интерфейс "IDataObject". Конечно, интерфейс предоставляет все открытые методы и свойства, которые должны быть представлены моими объектами данных, и вы, вероятно, можете догадаться, что абстрактная база реализует методы и свойства, общие для всех объектов данных.

У меня вопрос: если абстрактный басовый класс DataObjectBase реализует все, что указано в интерфейсе IDataObject, должен ли интерфейс быть объявлен в базовом классе или в производных классах?

В интерфейсах C #, объявленных в базовом классе, применяется простота к производным классам, но лучше ли это? Мне кажется, что реализация интерфейса в базовом классе делает менее очевидным, что производный класс реализует интерфейс, но опять же требует указания интерфейса для каждого производного класса.

Кроме того, если базовый класс НЕ был абстрактным, изменилась бы рекомендация?

Второй подвопрос: Если базовый класс реализует все методы / свойства интерфейса IDataObject, нужен ли этот интерфейс? Типовое имя базового класса можно просто использовать вместо имени интерфейса, например:

private DataObjectBase _dataObject;
частный IDataObject _dataObject;

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

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 07 августа 2009

Мой способ думать о таких проблемах - это рассматривать разных людей, читающих код, «роли», если хотите. Также учитывайте общую ремонтопригодность системы.

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

Тогда у нас есть роль, которая разрабатывает реализацию. Они придумали один из возможных подходов и обнаружили некоторые варианты, поэтому они хотят объединить общий код. Абстрактный базовый класс - заполните здесь общий материал, пусть детализированные исполнители заполнят пробелы. Помогите им, предоставив абстрактные методы, говорящие «ваш код идет сюда». Обратите внимание, что эти методы должны быть не только в интерфейсе. Также обратите внимание, что этот абстрактный базовый класс может даже реализовывать более одного интерфейса! (Например, это CleverThingWorker, но также и IntermediateWorkPersister.)

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

Итог ... Я использую оба интерфейса и базовые классы. Вы помещаете Интерфейс в Базовый класс. Мы не добавляем ценность, добавляя ее в класс реализации.

1 голос
/ 07 августа 2009

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

Что касается интерфейса, который скрыт в базовом классе и, следовательно, не сразу виден в пользовательском классе, это нормально и может быть обработано компилятором. Это также тот случай, когда вступают в силу хорошие соглашения об именах - ваш UserDataObject имеет имя, совпадающее с IDataObject, как и DataObjectBase. Вы можете добавить комментарий к файлу класса, в котором говорится, что он наследуется от IDataObject, но будет видно, что он наследует от DataObjectBase, который, в свою очередь, выглядит так, как будто он наследует от IDataObject по своему имени.

0 голосов
/ 07 августа 2009

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

Скажем, например, что один из методов интерфейса должен генерировать исключение - например, «DatabaseConnectionLostException» - и вы хотите протестировать клиентский код, чтобы проверить, правильно ли он работает в такой ситуации.

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

Если бы вы использовали абстрактный базовый класс вместо интерфейса, эта операция была бы немного сложнее (хорошо, вы можете использовать Mocks, но интерфейсное решение намного чище)

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