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, Circle
s 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 ShapeManager
bir 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 Square
ilgili 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 ShapeManager
ve 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_cast
alıcı / ayarlayıcı çiftini kullanmamı ve uygulamamı ShapeManager
engelleyen 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ı Job
s : 3D yazıcı için (örn PrintPatternInZoneJob
, TakePhotoOfZone
ile, vs.) AbstractJob
onları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 :
PrintPatternInZone
yazdırmak için nokta listesine, bölgenin konumuna, sıcaklık gibi bazı baskı parametrelerine ihtiyaç duyarTakePhotoOfZone
fotoğ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
Job
seçilirse, bölge, yol ve boyut parametrelerini görüntüleyen ve değiştiren bir widget gösterilecektir).
Daha Job
sonra Job
s, 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 (
Pattern
veya 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
? JobManager
sadece bir listesi var AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
durumlarda PropertyKey
bir 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.