2010-07-04 2 views
3

Я делаю дизайн рамки OO, и я столкнулся со следующей проблемой.Объектно-ориентированная проблема дизайна, Лисков Замена Принцип

Допустим, что в рамках меня есть интерфейса с Shape и пользователи могут свободно осуществлять и расширяют (добавление новых функций) Shape интерфейса для создания собственных фигур, например, Площадь и Круг. Чтобы эти новые объекты были доступны, пользователи должны зарегистрировать их в ShapeFactory, определяя имя формы (строки) и объекта.

Кроме того, каркас обеспечивает интерфейс, который называется ShapeWorker, который определяет следующую функцию:

class ShapeWorker 
{ 
public: 
    void processShape(Shape& shape) = 0; 
}; 

Пользователи свободны Для реализации ShapeWorker интерфейса, чтобы сделать конкретный работник формы, например, SquareWorker и CircleWorker. Чтобы эти новые объекты были доступны, пользователи должны зарегистрировать их в WorkerFactory, указав имя формы (строки) и объекта.

В определенный момент, рамки, учитывая строка, представляющая имя фигуры, создает новый Shape, используя ShapeFactory, а потом (где-то в коде) создает новый ShapeWorker, используя WorkerFactory с таким же именем. Затем вызывается processShape с предоставлением созданного ранее экземпляра Shape.

[ ... ] 
Shape* myShape = shapeFactory.create(shapeName); 
[ ... ] 
ShapeWorker* myWorker = workerFactory.create(shapeName); 
myWorker->processShape(*myShape); 
[ ... ] 

Дело в том, что, делая это, я заставить пользователя реализующий, например, SquareWorker сделать авансовый бросок от Форма на площади в функции processShape так, чтобы доступ к интерфейсу полных Square «ы:

class SquareWorker 
{ 
public: 
    void processShape(Shape& shape) 
    { 
    Square& square = dynamic_cast< Square& >(shape); 
    // using Square interface 
    } 
}; 

Это противоречит принципу замещения Лиск.

Теперь, этот подход неправильный? Что это было бы лучшим решением? Обратите внимание, что я не хочу реализовать processShape как функцию Shape.

Надеюсь, описание было достаточно ясным.

Заранее за вашу помощь.

Симо

+1

Вы должны добавить тег C++, я думаю. –

+0

Можете ли вы дать обоснование заявления «Обратите внимание, что я не хочу реализовывать функцию processShape как функцию члена Shape»? – SCFrench

+0

Одна из проблем заключается в том, что код со ссылками на Shape и ShapeWorker не имеет понятия, безопасно ли передавать Shape в ShapeWorker без какой-либо прослеживаемости обратно к точке создания, чтобы убедиться, что оба объекта были созданы с использованием того же shapeName. – SCFrench

ответ

5

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

Использование шаблонов может помочь вам: вы можете создать базовый класс для всех работников

template <class T> 
class BaseShapeWorker : ShapeWorker 
{ 
public: 
    void processShape(Shape& shape) 
    { 
    T& specificShape = dynamic_cast< T& >(shape); 
    processShape(specificShape) 
    } 
protected: 
    virtual void processShape(T& shape) = 0; 
}; 

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

+1

+1. NVI - хороший образец для использования :) –

+0

Спасибо, jdehaan, это отличный совет! Это упростит работу для разработчиков. – Simone

Смежные вопросы