После некоторых исследований я не могу найти простой пример решения проблемы, с которой я часто сталкиваюсь.
Допустим, я хочу создать небольшое приложение, в котором я могу создавать Square
s, Circle
s и другие фигуры, отображать их на экране, изменять их свойства после их выбора, а затем вычислять все их периметры.
Я бы сделал класс модели следующим образом:
class AbstractShape
{
public :
typedef enum{
SQUARE = 0,
CIRCLE,
} SHAPE_TYPE;
AbstractShape(SHAPE_TYPE type):m_type(type){}
virtual ~AbstractShape();
virtual float computePerimeter() const = 0;
SHAPE_TYPE getType() const{return m_type;}
protected :
const SHAPE_TYPE m_type;
};
class Square : public AbstractShape
{
public:
Square():AbstractShape(SQUARE){}
~Square();
void setWidth(float w){m_width = w;}
float getWidth() const{return m_width;}
float computePerimeter() const{
return m_width*4;
}
private :
float m_width;
};
class Circle : public AbstractShape
{
public:
Circle():AbstractShape(CIRCLE){}
~Circle();
void setRadius(float w){m_radius = w;}
float getRadius() const{return m_radius;}
float computePerimeter() const{
return 2*M_PI*m_radius;
}
private :
float m_radius;
};
(Представьте, что у меня есть больше классов форм: треугольники, гексагоны, каждый раз с переменными их пропрера и связанными геттерами и сеттерами. Проблемы, с которыми я столкнулся, имели 8 подклассов, но ради примера я остановился на 2)
Теперь у меня есть ShapeManager
, создание и сохранение всех фигур в массиве:
class ShapeManager
{
public:
ShapeManager();
~ShapeManager();
void addShape(AbstractShape* shape){
m_shapes.push_back(shape);
}
float computeShapePerimeter(int shapeIndex){
return m_shapes[shapeIndex]->computePerimeter();
}
private :
std::vector<AbstractShape*> m_shapes;
};
Наконец, у меня есть представление со спинбоксами для изменения каждого параметра для каждого типа фигуры. Например, когда я выбираю квадрат на экране, виджет параметров отображает только Square
связанные параметры (спасибо AbstractShape::getType()
) и предлагает изменить ширину квадрата. Для этого мне нужна функция, позволяющая мне изменять ширину ShapeManager
, и вот как я это делаю:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Есть ли лучший дизайн, позволяющий избежать использования dynamic_cast
и реализации пары getter / setter ShapeManager
для каждой переменной подкласса, которую я могу иметь? Я уже пытался использовать шаблон, но не удалось .
Проблема я столкнулся на самом деле не с фигурами , но с различными Job
с для 3D - принтера (например: PrintPatternInZoneJob
, TakePhotoOfZone
и т.д.) с AbstractJob
как их базового класса. Виртуальный метод есть execute()
и нет getPerimeter()
. Единственный раз, когда мне нужно использовать конкретное использование, это заполнить конкретную информацию, необходимую для работы :
PrintPatternInZone
нужен список точек для печати, положение зоны, некоторые параметры печати, такие как температураTakePhotoOfZone
какая зона должна быть сделана на фотографии, путь, где будет сохранена фотография, размеры и т. д.
Затем execute()
, когда я позвоню , Джобс будет использовать конкретную информацию, необходимую для реализации действия, которое они должны выполнять.
Единственный раз, когда мне нужно использовать конкретный тип задания, - это когда я заполняю или отображаю эту информацию (если TakePhotoOfZone
Job
выбрана буква a, будет отображаться виджет, отображающий и изменяющий параметры зоны, пути и размеров).
Затем Job
s помещаются в список Job
s, которые берут первое задание, выполняют его (вызывая AbstractJob::execute()
), переходят к следующему, и так далее до конца списка. (Вот почему я использую наследование).
Для хранения различных типов параметров я использую JsonObject
:
Преимущества: та же структура для любой работы, отсутствие динамического вещания при настройке или чтении параметров
проблема: не могу хранить указатели (на
Pattern
илиZone
)
Как вы думаете, есть ли лучший способ хранения данных?
Тогда как бы вы сохранили конкретный тип,Job
чтобы использовать его, когда мне нужно изменить конкретные параметры этого типа? JobManager
есть только список AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
где PropertyKey
может быть перечисление или строка, и «Ширина» (что означает, что вызов метода установки обновит значение ширины) входит в число допустимых значений.