Интерфейс как возможность или Интерфейс как тип - PullRequest
11 голосов
/ 22 июня 2011

Предположим, у меня есть такое требование:
Все объекты в системе являются производными от базового класса с именем IObject, и у него могут быть объекты с цветом, объекты с преобразованиями и оба.

Сейчас существует 2 подхода к проектированию иерархии классов.
Первый из них:

просто пусть конкретный класс получен из IObject, а также выберите «возможность» интерфейсы в качестве базового класса для указывают на то, что поддерживают такое поведение, как интерфейс: IHasColor,
IHasTransformation

Второй:

Организовать базовые классы, и пусть конкретные классы, полученные из одного из их: IObject, IColorObject, ITransfromationObject, IColorAndTransformationObject

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

Я хотел бы знать ваши идеи и предложения.

Спасибо.

Ответы [ 3 ]

10 голосов
/ 29 июня 2011

Классы абстрагируют реальную концепцию типов объектов .

Интерфейсы абстрагируют реальную концепцию поведения или способностей объекта .

Таким образом, возникает вопрос, является ли «цвет» свойством объекта или это способность объекта?

Когда вы проектируете иерархию, вы ограничиваете мир в более узкое пространство . Если вы возьмете цвет как свойство объекта, то у вас будет два вида объектов: те, которые имеют цвета, и те, которые не имеют. Это соответствует вашему "миру"?

Если вы смоделируете его как возможность (интерфейс), то у вас будут объекты, которые могут предоставить, скажем, приведение цветов в мир.

Для преобразования применяется та же логика. Вы можете разделить мир на два вида объектов: те, которые могут трансформироваться, и те, кто не может, или вы можете рассматривать это как способность, у объекта может быть способность трансформироваться в другую вещь.

Для меня, с этой точки зрения, имеет смысл:

  • Цвет - это свойство объекта. На самом деле каждый объект должен иметь цвет, даже если он прозрачный, даже если это отражение, даже если его «нет» (удачи в выяснении, что означает объект с цветом = нет в реальном мире, тем не менее, это может иметь смысл в вашем программная логика).
  • Преобразование - это способность, то есть интерфейс, то, что объект способен сделать , среди прочего объект может или не может быть способен.
1 голос
/ 29 июня 2011

Я работаю над иерархией классов в моем проекте, и в основном у меня похожая ситуация, как вы описали в своем вопросе.

Допустим, у меня есть базовый тип Object, который является абсолютным корнем всех других классов в моем наборе инструментов.,Поэтому, естественно, все происходит из него напрямую или через его подклассы.Существует общая функциональность, которую должен обеспечивать каждый класс, производный от объекта, но в некоторых листовых классах эффекты мало отличаются от других.Например, каждый объект имеет размер и положение, которые можно изменить с помощью свойств или методов, таких как Position = new Point (10, 10), Width = 15 и т. Д. Но существуют классы, которые должны игнорировать установку свойства или изменять его в соответствии с собственными силами.внутреннее состояниеПодумайте об элементе управления, прикрепленном к левой стороне родительского окна.Вы можете установить свойство Height все, что вам нравится, но оно, как правило, будет игнорироваться, потому что это свойство действительно зависит от высоты элемента управления родительского контейнера (или его высоты ClientArea или чего-либо подобного).

Таким образом, наличие абстрактного класса Object, реализующего базовое общеефункциональность в порядке, пока вы не достигнете точки, где вам нужно "настроить" поведение.Если Object предоставляет защищенный виртуальный метод SetHeight, который вызывается в параметре setter свойства Height, вы можете переопределить его в своем классе DockedControl и разрешить изменение высоты только в том случае, если закрепление установлено на None, в других случаях вы ограничиваете его или полностью игнорируете.

Таким образом, мы счастливы, но теперь у нас есть требование для объекта, который реагирует на события мыши, такие как Click или Hover.Таким образом, мы извлекаем MouseAwareObject из абстрактного класса Object и реализуем события и прочее.

И теперь клиенту нужны стыкуемые объекты с поддержкой мыши.Итак, мы унаследованы от DockableObject и ... хм, что теперь?Если мы можем сделать множественное наследование, мы можем сделать это, но мы сталкиваемся с проблемой алмаза с неоднозначностью дублированного интерфейса, и мы должны иметь дело с этим.У нас может быть два члена типов Dockable и MouseAware в новом классе и внешние вызовы через прокси для обеспечения функциональности.

И последнее, что приходит на ум, - это создать интерфейсы IDockable и IMouseAware и позволить им определять функциональность и добавлять их свободно только к объектам, которые должны обеспечивать конкретное поведение / реализации.

Я думаю, что яразделит мой базовый класс на части и оставит мой Объект с очень ограниченным «базовым» набором свойств и методов и остальной функциональностью, которая фактически необязательна для Объектов как тип, но необходима в конкретных случаях для перехода на интерфейсы, такие как IResizable, IDockable, IMakeAWorldABetterPlaceAbleи т. д. С помощью этого решения можно «прикреплять» поведения к объектно-производным классам без необходимости перетаскивать виртуальные или чисто виртуальные (абстрактные) методы из корневого базового класса вплоть до конечного класса.

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

Возможно, это не идеальноответьте на ваш вопрос, но, возможно, он намекает вам на собственное решение.

1 голос
/ 22 июня 2011

Я думаю, вы прыгаете прямо в интерфейсы, пропуская классы. Это требуется для вашего приложения. иметь интерфейс "IObject"? Возможно, вам поможет корневой класс «CObject» для вашей иерархии классов.

Похоже, что победителем станет решение № 1, у вас может быть «MyObject», будь то реализация интерфейса или прямой класс. Позже вы можете добавлять дополнительные классы или интерфейсы в иерархию классов по мере необходимости.

После просмотра нескольких приложений (некоторые мои, некоторые другие), я думаю, должен быть шаблон проектирования «Мой пользовательский корневой класс иерархии классов приложений» или «Мой пользовательский корневой интерфейс иерархии классов приложений».

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