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ć Squares, Circles 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 ShapeManagertworzenie 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 Squareparametry 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_casti implementacji pary pobierającej / ustawiającej ShapeManagerdla 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 Jobs dla drukarki 3D (np PrintPatternInZoneJob, TakePhotoOfZoneitp) z AbstractJobjak 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 :
PrintPatternInZonepotrzebuje listy punktów do wydrukowania, pozycji strefy, niektórych parametrów drukowania, takich jak temperaturaTakePhotoOfZonepotrzebuje 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 Jobwybrano a, zostanie wyświetlony widget wyświetlający i modyfikujący parametry strefy, ścieżki i wymiarów).
Następnie Jobsą umieszczane na liście Jobs, 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
PatternlubZone)
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? JobManagerma tylko listę AbstractJob*.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)gdzie PropertyKeymoż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.