หลังจากทำการวิจัยบางอย่างฉันไม่สามารถหาตัวอย่างง่ายๆในการแก้ไขปัญหาที่ฉันพบบ่อยได้
สมมติว่าฉันต้องการสร้างแอปพลิเคชั่นเล็ก ๆ ที่ฉันสามารถสร้าง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)
ตอนนี้ฉันมี a ShapeManager
, instantiating และเก็บรูปร่างทั้งหมดในอาร์เรย์:
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
sสำหรับเครื่องพิมพ์ 3D (เช่นPrintPatternInZoneJob
, TakePhotoOfZone
ฯลฯ ) ด้วยAbstractJob
เป็นชั้นฐานของพวกเขา วิธีเสมือนและไม่ได้execute()
ครั้งเดียวที่ฉันต้องใช้การใช้งานที่เป็นรูปธรรมคือการกรอกข้อมูลเฉพาะที่งานต้องการ :getPerimeter()
PrintPatternInZone
ต้องการรายการจุดที่จะพิมพ์ตำแหน่งของโซนพารามิเตอร์การพิมพ์บางอย่างเช่นอุณหภูมิTakePhotoOfZone
ต้องการโซนที่จะถ่ายภาพเส้นทางที่จะบันทึกรูปภาพขนาด ฯลฯ ...
เมื่อฉันจะโทรหาexecute()
งานจะใช้ข้อมูลเฉพาะที่พวกเขาต้องตระหนักถึงการกระทำที่พวกเขาควรจะทำ
ครั้งเดียวที่ฉันต้องใช้งานรูปธรรมของงานคือเมื่อฉันกรอกหรือแสดงข้อมูลเหล่านี้ (หากTakePhotoOfZone
Job
เลือก a, วิดเจ็ตที่แสดงและแก้ไขพารามิเตอร์โซน, พา ธ และมิติข้อมูล)
Job
s จะใส่แล้วลงในรายการของJob
s ซึ่งใช้งานครั้งแรกที่รัน (โดยการเรียกAbstractJob::execute()
) ไปถัดไปบนและบนจนกว่าจะสิ้นสุดของรายการ (นี่คือเหตุผลที่ฉันใช้มรดก)
ในการจัดเก็บพารามิเตอร์ประเภทต่างๆฉันใช้JsonObject
:
ข้อได้เปรียบ: โครงสร้างเดียวกันสำหรับงานใด ๆ ไม่มี dynamic_cast เมื่อตั้งค่าหรืออ่านพารามิเตอร์
ปัญหา: ไม่สามารถจัดเก็บพอยน์เตอร์ (ถึง
Pattern
หรือZone
)
คุณมีวิธีที่ดีกว่าในการจัดเก็บข้อมูลหรือไม่?
จากนั้นคุณจะเก็บประเภทคอนกรีตของการJob
ใช้เมื่อฉันต้องแก้ไขพารามิเตอร์เฉพาะของประเภทนั้นอย่างไร JobManager
เพียง AbstractJob*
แต่มีรายชื่อของ
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
ที่PropertyKey
สามารถเป็น enum หรือสตริงและ "ความกว้าง" (ซึ่งหมายความว่าการเรียกไปยัง setter จะอัปเดตค่าความกว้าง) เป็นหนึ่งในค่าที่อนุญาต