ООП дизайн проблема: полиморфизм - PullRequest
1 голос
/ 11 мая 2010

Я пытаюсь решить проблему проектирования, используя полиморфизм на основе наследования и динамическое связывание. У меня есть абстрактный суперкласс и два подкласса. Суперкласс содержит общее поведение. SubClassA и SubClassB определяют несколько разных методов: SubClassA определяет метод executeTransform (), а SubClassB - нет.

Итак, следующий пример

1 var v:SuperClass;

2 var b:SubClassB = new SubClassB();

3 v = b;

4 v.performTransform();

вызовет ошибку компиляции в строке 4, так как executeTransform () не определен в суперклассе. Мы можем заставить его скомпилировать, приведя ...

(v as SubClassA).performTransform();

однако, это вызовет исключение времени выполнения, так как v на самом деле является экземпляром SubClassB, который также не определяет executeTransform ()

Таким образом, мы можем обойти это, проверив тип объекта перед его приведением:

if( typeof v == SubClassA)
{
  (cast v to SubClassA).performTransform();
}

Это обеспечит, что мы будем вызывать executeTransform () только для v, которые являются экземплярами SubClassA. Это довольно нелегкое решение для моих глаз, но по крайней мере это безопасно. Я использовал интерфейс на основе полиморфизма (значение интерфейса тип, который не может быть созданным и определяющим API классов, которые его реализуют) в прошлом, но это также кажется неуклюжим. Для приведенного выше случая, если SubClassA и SubClassB реализовали ISuperClass который определил executeTransform, тогда они оба должны будут выполнить executeTransform (). Если у SubClassB нет реальной необходимости в executeTransform (), вам придется реализовать пустую функцию.

Там должен быть шаблон проектирования, который решает проблему.

Ответы [ 5 ]

4 голосов
/ 11 мая 2010

Мой немедленный комментарий заключается в том, что моделирование вашего объекта неверно. Зачем считать SubClassA отношением SuperClass ( is-a ), если я предполагаю, что это , а не .

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

3 голосов
/ 11 мая 2010

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

Нет схемы проектирования, чтобы обойти это, потому что это предполагаемое поведение.

В вашем определении performTransform принадлежит только SubClassA. Таким образом, чтобы иметь возможность вызывать performTransform для объекта, объект должен иметь тип SubClassA (или подтип SubClassA.

Вызывать performTransform на SuperClass не имеет смысла, потому что не каждый экземпляр SuperClass определяет этот метод.

Понижение с SuperClass до SubClassA должно, безусловно, вызвать ошибку, если экземпляр не является SubClassA - это должно быть очевидно.

Итак, вы должны либо изменить свои определения так, чтобы performTransform принадлежал SuperClass (в этом случае, как вы сказали, каждый экземпляр типа SuperClass должен иметь некоторую реализацию для метода, даже пустую один) или вы должны убедиться, что вы вызываете методы только для типов, которые их определяют.

1 голос
/ 11 мая 2010

Если вы говорите, что ваш велосипед - это автомобиль, это не значит, что в него можно заправлять бензином. Смысл полиморфизма в том, чтобы позволить вам думать о вещах как о суперклассе - это все банковские счета, все эти фигуры, если использовать классические примеры, - и не быть пойманным тем, кем они являются на самом деле. Иногда подклассы добавляют возможность. Во многих случаях эта возможность используется в конкретных реализациях в каждом подклассе. Таким образом, чтобы использовать ваши имена, некоторый метод Adjust(), который находится в сигнатуре SuperClass, реализован (иначе) в SubClassA и SubClassB. Версия SubClassA называет свою собственную performTransform как часть процесса, и мы все живем долго и счастливо. В тот момент, когда какой-то код должен решить, следует ли вызывать performTransform или нет, вы больше не просто думаете об этом как о SuperClass. Это не обязательно что-то, что должно быть решено, это просто то, что есть.

1 голос
/ 11 мая 2010

Я не уверен, что для решения требуется шаблон, а просто небольшая редизайн. Если имеет смысл что-либо для вызова performTransform, то он должен быть в суперклассе как виртуальный метод и переопределен в подклассах.

Таким образом, суперкласс определяет поток с абстрактной точки зрения, а подклассы реализуют их соответствующим образом. В вашем случае простейшими вариантами является либо просто оставить performTransform пустым в суперклассе, либо реализовать его как пустой метод в подклассе, который ему не нужен (когда вы смешиваете этот подход с коротким комментарием, вы получаете ремонтопригодная система ИМО).

Наиболее близким шаблоном, который я могу себе представить, является шаблон Null Object, где этот метод performTransform является просто фиктивной функцией для сохранения совместимости, но не выполняет фактическую задачу.

0 голосов
/ 11 мая 2010

Было бы лучше иметь вызов метода executeTransform () в методе, который принимает в качестве параметра только тип SubClassB - по крайней мере, тогда вам не придется выполнять проверку типа.

Сказав, что, если у вас вообще есть эта проблема, это может означать, что наследование не может быть лучшим решением - композиция может быть лучшим способом решения проблемы.

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