Po przeprowadzeniu niektórych badań nie mogę znaleźć prostego przykładu rozwiązującego często spotykany problem.
Powiedzmy, że chcę utworzyć małą aplikację, w której mogę tworzyć Square
s, Circle
s i inne kształty, wyświetlać je na ekranie, modyfikować ich właściwości po ich wybraniu, a następnie obliczać wszystkie ich obwody.
Zrobiłbym klasę modelu w ten sposób:
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;
};
(Wyobraź sobie, że mam więcej klas kształtów: trójkąty, sześciokąty, za każdym razem ich zmienne miotające oraz powiązane z nimi funkcje pobierające i ustawiające. Problemy, z którymi się spotkałem, miały 8 podklas, ale dla przykładu zatrzymałem się na 2)
Mam teraz ShapeManager
tworzenie i tworzenie wszystkich kształtów w tablicy:
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;
};
Wreszcie mam widok ze spinboxami, aby zmienić każdy parametr dla każdego typu kształtu. Na przykład, kiedy wybieram kwadrat na ekranie, widget parametrów wyświetla tylko Square
parametry powiązane (dzięki AbstractShape::getType()
) i proponuje zmianę szerokości kwadratu. Aby to zrobić, potrzebuję funkcji pozwalającej mi modyfikować szerokość w ShapeManager
, i tak to robię:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Czy istnieje lepszy projekt pozwalający uniknąć użycia dynamic_cast
i implementacji pary pobierającej / ustawiającej ShapeManager
dla każdej zmiennej podklasy, którą mogę mieć? Próbowałem już użyć szablonu, ale nie udało mi się .
Problem mam w obliczu tak naprawdę nie jest z kształtami, ale z różnych Job
s dla drukarki 3D (np PrintPatternInZoneJob
, TakePhotoOfZone
itp) z AbstractJob
jak ich klasy bazowej. Metoda wirtualna jest execute()
i nie jest getPerimeter()
. Jedyne, czego potrzebuję, aby użyć konkretnego użycia, to wypełnienie konkretnych informacji, których potrzebuje praca :
PrintPatternInZone
potrzebuje listy punktów do wydrukowania, pozycji strefy, niektórych parametrów drukowania, takich jak temperaturaTakePhotoOfZone
potrzebuje strefy, którą należy zrobić, ścieżki, w której zdjęcie zostanie zapisane, wymiarów itp.
Kiedy zadzwonię execute()
, Jobs wykorzysta konkretne informacje, które musi wykonać, aby wykonać akcję, którą powinni wykonać.
Jedynym razem, gdy muszę użyć konkretnego typu zadania, jest wypełnienie lub wyświetlenie tych informacji (jeśli TakePhotoOfZone
Job
wybrano a, zostanie wyświetlony widget wyświetlający i modyfikujący parametry strefy, ścieżki i wymiarów).
Następnie Job
są umieszczane na liście Job
s, które podejmują pierwszą pracę, wykonują ją (dzwoniąc AbstractJob::execute()
), przechodzą do następnej, z przerwami i do końca listy. (Dlatego używam dziedziczenia).
Do przechowywania różnych typów parametrów używam JsonObject
:
zalety: taka sama struktura dla każdego zadania, brak dynamicznego nadawania podczas ustawiania lub odczytu parametrów
problem: nie można przechowywać wskaźników (do
Pattern
lubZone
)
Czy uważasz, że istnieje lepszy sposób przechowywania danych?
Potem , jak można przechowywać typ betonieJob
go używać kiedy muszę zmodyfikować parametry specyficzne tego typu? JobManager
ma tylko listę AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
gdzie PropertyKey
może być wyliczenie lub ciąg znaków, a „Szerokość” (co oznacza, że wywołanie funkcji ustawiającej zaktualizuje wartość szerokości) należy do jednej z dozwolonych wartości.