Setelah melakukan beberapa penelitian, saya tidak dapat menemukan contoh sederhana untuk menyelesaikan masalah yang sering saya temui.
Katakanlah saya ingin membuat aplikasi kecil di mana saya dapat membuat Square
s, Circle
s, dan bentuk lainnya, menampilkannya di layar, memodifikasi properti mereka setelah memilihnya, dan kemudian menghitung semua perimeter mereka.
Saya akan melakukan kelas model seperti ini:
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;
};
(Bayangkan saya memiliki lebih banyak kelas bentuk: segitiga, segi enam, dengan setiap kali variabel penekan mereka dan getter dan setter terkait. Masalah yang saya hadapi memiliki 8 subclass tetapi demi contoh saya berhenti di 2)
Sekarang saya punya ShapeManager
, instantiating dan menyimpan semua bentuk dalam array:
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;
};
Akhirnya, saya memiliki pandangan dengan spinbox untuk mengubah setiap parameter untuk setiap jenis bentuk. Sebagai contoh, ketika saya memilih kotak di layar, widget parameter hanya menampilkan Square
parameter terkait (terima kasih AbstractShape::getType()
) dan mengusulkan untuk mengubah lebar kotak. Untuk melakukan itu saya memerlukan fungsi yang memungkinkan saya untuk memodifikasi lebar ShapeManager
, dan ini adalah bagaimana saya melakukannya:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Apakah ada desain yang lebih baik yang menghindari saya untuk menggunakan dynamic_cast
dan mengimplementasikan pasangan pengambil / penyetel ShapeManager
untuk setiap variabel subkelas yang mungkin saya miliki? Saya sudah mencoba menggunakan template tetapi gagal .
Masalah yang dihadapi saya adalah tidak benar-benar dengan Shapes tetapi dengan berbeda Job
s untuk printer 3D (ex: PrintPatternInZoneJob
, TakePhotoOfZone
, dll) dengan AbstractJob
sebagai kelas dasar mereka. Metode virtual adalah execute()
dan tidak getPerimeter()
. Satu-satunya waktu saya perlu menggunakan penggunaan konkret adalah mengisi informasi spesifik yang dibutuhkan pekerjaan :
PrintPatternInZone
perlu daftar titik untuk dicetak, posisi zona, beberapa parameter pencetakan seperti suhuTakePhotoOfZone
membutuhkan zona apa untuk mengambil foto, jalur di mana foto akan disimpan, dimensi, dll ...
Ketika saya akan menelepon execute()
, Jobs akan menggunakan informasi spesifik yang mereka miliki untuk menyadari tindakan yang seharusnya mereka lakukan.
Satu-satunya waktu saya perlu menggunakan jenis konkret Pekerjaan adalah ketika saya mengisi atau menampilkan informasi tesis ini (jika a TakePhotoOfZone
Job
dipilih, widget yang menampilkan dan memodifikasi parameter zona, jalur, dan dimensi akan ditampilkan).
The Job
s kemudian dimasukkan ke dalam daftar Job
s yang mengambil pekerjaan pertama, mengeksekusinya (dengan memanggil AbstractJob::execute()
), pergi ke yang berikutnya, dan terus sampai akhir daftar. (Inilah sebabnya saya menggunakan warisan).
Untuk menyimpan berbagai jenis parameter, saya menggunakan JsonObject
:
Keuntungan: struktur yang sama untuk pekerjaan apa pun, tidak ada dynamic_cast saat mengatur atau membaca parameter
masalah: tidak dapat menyimpan pointer (ke
Pattern
atauZone
)
Apakah Anda ada cara yang lebih baik untuk menyimpan data?
Lalu bagaimana Anda menyimpan tipe konkritJob
untuk menggunakannya ketika saya harus memodifikasi parameter spesifik dari tipe itu? JobManager
hanya memiliki daftar AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
mana PropertyKey
bisa menjadi enum atau string, dan "Lebar" (yang menandakan bahwa panggilan ke setter akan memperbarui nilai lebar) adalah salah satu dari nilai yang diizinkan.