О том, что вы отправили в ответ на @DOC:
@ DOK менеджер не удовлетворен ... хотя он дал мне разрешение на разработку, но все же он сказал, что вы должны изучить это.
Полагаю, причина, по которой ваш менеджер не удовлетворен текущим дизайном, заключается в том, что он страдает от запаха кода , называемого Иерархии параллельного наследования . В основном, это описывает то, что вы испытываете: каждый раз, когда создается новый подкласс, вы должны создать его подкласс в параллельной иерархии.
Это усложнит изменение вашего дизайна и, следовательно, его поддержку.
Обзор текущих ответов
Я хотел бы прокомментировать некоторые вещи, с которыми я не согласен в текущих ответах.
В основном вам предлагается либо:
- Наследование от
ScreenShape
класса
- Реализовать логику рисования в каждом подклассе
Shape
Я вижу две проблемы с опцией n ° 1:
- Он определяет Shape как интерфейс, поэтому вам придется заново его реализовывать в каждом подклассе (
Rectangle
и т. Д.)
- Чтобы преодолеть предыдущее, вы можете
ScreenShape
реализовать его, потому что все виды форм будут наследовать его. Это плохо, и ответ @Perception объясняет почему.
- Предположим, вас сейчас просят экспортировать фигуры в XML-файл (это еще одна операция с фигурами, такая же, как их отображение). Следуя этому подходу, вам нужно реализовать это поведение для класса
XMLShape
и наследовать его, но вы уже наследуете от ScreenShape
. Видите проблему?
Опция n ° 2 заставляет вас раздуть модель вашего домена из-за проблем с презентацией, опять же, как сказал @Perception:)
Некоторые цели для достижения
Из предыдущего видно, что:
- Вы хотите, чтобы ваши классы изменялись при изменении модели вашего домена, но не при изменении способа отображения фигур или их экспорта в XML.
- Это приводит вас к Принципу единой ответственности , который гласит, что у класса должна быть только одна причина для изменения.
- Мы обнаружили, что отображение, а также экспорт в XML (в приведенном мною примере) - это операции, которые вы будете выполнять с фигурами, и вы захотите легко добавлять новые операции позже, не меняя классов фигур.
Предлагаемое решение
Прежде всего, очистите иерархию Shape
и оставьте только то, что принадлежит вашему проблемному домену.
Затем вам нужны разные способы отображения фигур без реализации этого поведения в иерархии Shape
. Если вы не будете иметь дело с вложенными фигурами (фигурами, которые содержат другие фигуры), вы можете использовать технику под названием Double Dispatch . Он позволяет вам отправлять вызов метода в зависимости от типа получателя, кодируя эту информацию в имени метода, передаваемого в аргумент, который он получает, следующим образом:
public class Circle extends Shape {
public void DisplayOn(IShapeDisplay aDisplay) {
aDisplay.DisplayCircle(this);
}
}
public class Rectangle extends Shape {
public void DisplayOn(IShapeDisplay aDisplay) {
aDisplay.DisplayRectangle(this);
}
}
interface IShapeDisplay {
void DisplayCircle(Circle aCircle);
void DisplayRectangle(Rectangle aRectangle);
}
Классы, реализующие IShapeDisplay
, будут ответственны за отображение каждого вида фигуры. Приятно то, что вам удалось вычистить эти неприятные детали из иерархии Shape
, инкапсулировав их в своем собственном классе. Благодаря этому теперь вы можете изменять этот класс без изменения подклассов Shape
.
Заключительные комментарии
Подробнее о запахах кода и параллельных иерархиях наследования можно прочитать в книге Мартина Фаулера: Рефакторинг: улучшение дизайна существующего кода .
Кроме того, вы можете обратиться к книге Design Patterns: Элементы многократно используемого объектно-ориентированного программного обеспечения , если вам нужно разобраться с вложением фигур: шаблонами Composite и Visitor.
Надеюсь, это было полезно для вас!