ปัญหา
ฉันประสบปัญหาง่ายๆ แม้ว่าฉันไม่สามารถหา OOD ที่เหมาะสมสำหรับมันได้
สิ่งที่ฉันมี:
- คลาสพื้นฐาน
- คลาสย่อยเพิ่มวิธีการใหม่
foo()
- รายการพอยน์เตอร์ไปยังอินสแตนซ์คลาสฐาน
สิ่งที่ฉันต้องการ:
ฉันต้องวนซ้ำรายการนี้และเรียก foo()
สำหรับอ็อบเจ็กต์ที่รองรับเมธอดนี้ เช่น อ็อบเจ็กต์ของ (หรือมาจาก) คลาสย่อยที่กล่าวมาข้างต้น หรือพูดโดยทั่วไป ฉันต้องการการเข้าถึงคลาสย่อยแบบ "ไม่มีกลิ่น" แบบโพลีมอร์ฟิกผ่านรายการพอยน์เตอร์ไปยังคลาสพื้นฐาน
รหัสตัวอย่าง
class Entity {
// ...
// This class contains methods also needed by subclasses.
};
class SaveableEntity : public Entity {
public:
virtual void save() = 0;
};
// SaveableEntity has multiple subclasses with specific save() implementations.
std::vector<Entity *> list;
for (Entity *entity : list) {
// Here I need to save() the descendants of a SaveableEntity type.
}
ฉันเกิดไอเดียบางอย่างขึ้นมา แต่ก็ไม่มีไอเดียใดที่เหมาะกับฉันเลย นี่คือบางส่วนของพวกเขา:
วิธีที่ 1: dynamic_cast
เนื่องจากองค์ประกอบบางอย่างสามารถบันทึกได้และบางส่วนไม่สามารถบันทึกได้ วิธีที่ชัดเจนที่สุดที่ฉันเห็นคือการส่งแบบไดนามิก:
std::vector<Entity *> list;
for (Entity *entity : list) {
auto saveable = dynamic_cast<SaveableEntity *>(entity);
if (saveable) {
saveable->save();
}
}
อย่างไรก็ตาม การใช้ dynamic_cast
ดูเหมือน OOD ที่ไม่ดีในสถานการณ์นี้ (แก้ไขฉันหากฉันผิด) นอกจากนี้ วิธีการนี้อาจนำไปสู่การละเมิด LSP ได้อย่างง่ายดาย
วิธีที่ 2: ย้าย save()
ไปยังคลาสพื้นฐาน
ฉันสามารถลบ SaveableEntity
และย้ายวิธี save()
ไปที่ฐาน Entity
อย่างไรก็ตาม สิ่งนี้ทำให้เรานำวิธีการจำลองมาใช้:
class Entity {
virtual void save() {
// Do nothing, override in subclasses
}
};
สิ่งนี้จะกำจัดการใช้งาน dynamic_cast
แต่วิธีจำลองยังคงดูไม่ถูกต้อง: ตอนนี้คลาสพื้นฐานเก็บข้อมูล (วิธี save()
) ที่ไม่เกี่ยวข้องโดยสิ้นเชิง
วิธีที่ 3: ใช้รูปแบบการออกแบบ
- รูปแบบกลยุทธ์: คลาส
SaveStrategy
และคลาสย่อยเช่นNoSaveStrategy
,SomeSaveStrategy
,SomeOtherSaveStrategy
ฯลฯ อีกครั้ง การมีอยู่ของNoSaveStrategy
นำเรากลับไปสู่ข้อบกพร่องของวิธีการก่อนหน้านี้: คลาสพื้นฐานต้องทราบรายละเอียดเฉพาะเกี่ยวกับ คลาสย่อยซึ่งดูเหมือนเป็นการออกแบบที่ไม่ดี - รูปแบบ พร็อกซี หรือ มัณฑนากร สามารถสรุป
dynamic_cast
ได้อย่างง่ายดาย อย่างไรก็ตาม การดำเนินการนี้จะซ่อนเฉพาะโค้ดที่ไม่ต้องการเท่านั้น โดยไม่กำจัดการออกแบบที่ไม่ดีออกไป - เพิ่มเลเยอร์การแต่งเพลงทับการสืบทอด และอื่น ๆ...
คำถาม
บางทีฉันอาจขาดวิธีแก้ปัญหาที่ชัดเจน หรือวิธีที่อธิบายไว้ (1 หรือ 2) อาจไม่แย่และมีกลิ่นเหม็นในบริบทนี้เท่าที่ฉันเห็น
แล้วแนวทางการออกแบบแบบไหนที่เหมาะกับสถานการณ์เช่นนี้?