Sau khi thực hiện một số nghiên cứu, tôi dường như không thể tìm thấy một ví dụ đơn giản nào giải quyết vấn đề mà tôi gặp phải thường xuyên.
Giả sử tôi muốn tạo một ứng dụng nhỏ nơi tôi có thể tạo Square
s, Circle
s và các hình dạng khác, hiển thị chúng trên màn hình, sửa đổi các thuộc tính của chúng sau khi chọn chúng, sau đó tính toán tất cả các chu vi của chúng.
Tôi sẽ làm lớp mô hình như thế này:
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;
};
(Hãy tưởng tượng tôi có nhiều lớp hình dạng hơn: hình tam giác, hình lục giác, với mỗi lần biến proprers của chúng và các biểu đồ và setters liên quan.
Bây giờ tôi có một ShapeManager
, khởi tạo và lưu trữ tất cả các hình dạng trong một mảng:
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;
};
Cuối cùng, tôi có một khung nhìn với các hộp quay để thay đổi từng tham số cho từng loại hình dạng. Ví dụ: khi tôi chọn một hình vuông trên màn hình, tiện ích tham số chỉ hiển thị Square
các tham số liên quan (nhờ AbstractShape::getType()
) và đề xuất thay đổi độ rộng của hình vuông. Để làm điều đó tôi cần một chức năng cho phép tôi sửa đổi độ rộng trong ShapeManager
và đây là cách tôi thực hiện:
void ShapeManager::changeSquareWidth(int shapeIndex, float width){
Square* square = dynamic_cast<Square*>(m_shapes[shapeIndex]);
assert(square);
square->setWidth(width);
}
Có một thiết kế tốt hơn tránh cho tôi sử dụng dynamic_cast
và để thực hiện một cặp getter / setter ShapeManager
cho mỗi biến lớp con tôi có thể có không? Tôi đã thử sử dụng mẫu nhưng không thành công .
Vấn đề tôi đang phải đối mặt là không thực sự với Shapes nhưng với nhau Job
s cho một máy in 3D (ví dụ: PrintPatternInZoneJob
, TakePhotoOfZone
, vv) với AbstractJob
như lớp cơ sở của họ. Phương pháp ảo là execute()
và không getPerimeter()
. Lần duy nhất tôi cần sử dụng cụ thể là điền thông tin cụ thể mà công việc cần :
PrintPatternInZone
cần danh sách các điểm cần in, vị trí của vùng, một số thông số in như nhiệt độTakePhotoOfZone
cần khu vực nào để chụp ảnh, đường dẫn ảnh sẽ được lưu, kích thước, v.v ...
Khi đó tôi sẽ gọi execute()
, Jobs sẽ sử dụng thông tin cụ thể mà họ có để nhận ra hành động mà họ phải làm.
Lần duy nhất tôi cần sử dụng loại công việc cụ thể là khi tôi điền hoặc hiển thị thông tin luận án (nếu TakePhotoOfZone
Job
được chọn, một tiện ích hiển thị và sửa đổi các tham số vùng, đường dẫn và kích thước sẽ được hiển thị).
Các Job
s sau đó được đưa vào một danh sách các Job
công việc đầu tiên, thực hiện nó (bằng cách gọi AbstractJob::execute()
), tiếp theo, tiếp tục và tiếp tục cho đến khi kết thúc danh sách. (Đây là lý do tại sao tôi sử dụng thừa kế).
Để lưu trữ các loại tham số khác nhau, tôi sử dụng JsonObject
:
lợi thế: cùng cấu trúc cho bất kỳ công việc nào, không có Dynamic_cast khi cài đặt hoặc đọc tham số
vấn đề: không thể lưu trữ con trỏ (đến
Pattern
hoặcZone
)
Bạn có điều gì có một cách tốt hơn để lưu trữ dữ liệu?
Sau đó, làm thế nào bạn sẽ lưu trữ loại cụ thể của việcJob
sử dụng nó khi tôi phải sửa đổi các tham số cụ thể của loại đó? JobManager
chỉ có một danh sách AbstractJob*
.
changeValue(int shapeIndex, PropertyKey propkey, double numericalValue)
nơi PropertyKey
có thể là enum hoặc chuỗi và "Width" (biểu thị rằng lệnh gọi đến setter sẽ cập nhật giá trị của chiều rộng) là một trong những giá trị được phép.