Bazı araştırmalar yaptıktan sonra sık karşılaştığım bir sorunu çözen basit bir örnek bulamıyorum.
Diyelim ki Square, s, Circles ve diğer şekilleri oluşturabildiğim, bir ekranda görüntüleyebildiğim, seçtikten sonra özelliklerini değiştirebildiğim ve ardından tüm çevrelerini hesaplayabildiğim küçük bir uygulama oluşturmak istiyorum .
Model sınıfını şöyle yaparım:
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;
};
(Daha fazla şekil sınıfım olduğunu düşünün: üçgenler, altıgenler, her seferinde proprers değişkenleri ve ilişkili alıcılar ve ayarlayıcılar. Karşılaştığım problemlerin 8 alt sınıfı vardı ama 2'de durduğum örnek için)
Şimdi ShapeManagerbir dizi, tüm şekiller bir dizi depolama ve depolama var:
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;
};
Son olarak, her şekil türü için her parametreyi değiştirmek için spinboxes ile bir görünüm var. Örneğin, ekranda bir kare seçtiğimde, parametre widget'ı yalnızca Squareilgili parametreleri (sayesinde AbstractShape::getType()) görüntüler ve karenin genişliğini değiştirmeyi önerir. Bunu yapmak için bana genişliği değiştirmek için izin veren bir işleve ihtiyacım var ShapeManagerve ben bunu nasıl yaparım:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Sahip olabileceğim her alt sınıf değişkeni için dynamic_castalıcı / ayarlayıcı çiftini kullanmamı ve uygulamamı ShapeManagerengelleyen daha iyi bir tasarım var mı? Zaten şablonu kullanmaya çalıştım ama başarısız oldum .
Yüzleştiğim sorun Şekiller ile ancak gerçekten değil farklı Jobs : 3D yazıcı için (örn PrintPatternInZoneJob, TakePhotoOfZoneile, vs.) AbstractJobonların temel sınıf olarak. Sanal yöntem execute()değil getPerimeter(). Somut kullanımı kullanmam gereken tek zaman, bir işin ihtiyaç duyduğu belirli bilgileri doldurmaktır :
PrintPatternInZoneyazdırmak için nokta listesine, bölgenin konumuna, sıcaklık gibi bazı baskı parametrelerine ihtiyaç duyarTakePhotoOfZonefotoğrafın hangi bölgeye, fotoğrafın kaydedileceği yola, boyutlara vb. ihtiyaç duyar.
Daha sonra arayacağımda execute(), İşler yapmaları gereken eylemi gerçekleştirmek için ihtiyaç duydukları bilgileri kullanacaklar.
Bir İşin somut tipini kullanmam gereken tek zaman, bu bilgileri doldururken veya görüntülerken (a TakePhotoOfZone Jobseçilirse, bölge, yol ve boyut parametrelerini görüntüleyen ve değiştiren bir widget gösterilecektir).
Daha Jobsonra Jobs, ilk işi alan, yürüten (çağırarak AbstractJob::execute()), bir sonrakine gider, listenin sonuna kadar devam eden s listesine eklenir . (Bu yüzden miras kullanıyorum).
Farklı parametre türlerini saklamak için aşağıdakileri kullanıyorum JsonObject:
avantajları: herhangi bir iş için aynı yapı, parametreleri ayarlarken veya okurken dynamic_cast yok
Sorun: İşaretçileri saklayamıyor (
Patternveya veyaZone)
Veri depolamanın daha iyi bir yolu var mı?
O zaman bu tipin belirli parametrelerini değiştirmek zorunda kaldığım beton tipini nasıl saklarsınızJob ? JobManagersadece bir listesi var AbstractJob*.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)durumlarda PropertyKeybir enum ne de bir tel ve izin verilen değerler arasında yer almaktadır (ayarlayıcı çağrı genişliği değerini günceller anlamına gelir) "En" olabilir.