ปัญหา
สมมติว่าฉันมีคลาสที่เรียกว่าDataSourceมีReadDataวิธี (และบางทีคนอื่น ๆ แต่ให้สิ่งที่ง่าย) เพื่ออ่านข้อมูลจาก.mdbไฟล์:
var source = new DataSource("myFile.mdb");
var data = source.ReadData();
ไม่กี่ปีต่อมาฉันตัดสินใจว่าฉันต้องการให้สามารถรองรับ.xmlไฟล์เพิ่มเติมจาก.mdbไฟล์เป็นแหล่งข้อมูล การนำไปใช้สำหรับ "การอ่านข้อมูล" นั้นค่อนข้างแตกต่างกันสำหรับ.xmlและ.mdbไฟล์ ดังนั้นถ้าฉันจะออกแบบระบบตั้งแต่เริ่มต้นฉันจะนิยามมันดังนี้:
abstract class DataSource {
abstract Data ReadData();
static DataSource OpenDataSource(string fileName) {
// return MdbDataSource or XmlDataSource, as appropriate
}
}
class MdbDataSource : DataSource {
override Data ReadData() { /* implementation 1 */ }
}
class XmlDataSource : DataSource {
override Data ReadData() { /* implementation 2 */ }
}
เยี่ยมมากการใช้รูปแบบวิธีการของโรงงานอย่างสมบูรณ์แบบ น่าเสียดายที่DataSourceตั้งอยู่ในห้องสมุดและเปลี่ยนรหัสใหม่เช่นนี้จะทำให้การโทรที่มีอยู่ทั้งหมดของ
var source = new DataSource("myFile.mdb");
ในไคลเอนต์ต่าง ๆ ที่ใช้ไลบรารี ทำไมฉันไม่ใช้วิธีการจากโรงงานในตอนแรก?
โซลูชั่น
นี่เป็นวิธีแก้ปัญหาที่ฉันสามารถทำได้:
ทำให้ตัวสร้างแหล่งข้อมูลส่งคืนชนิดย่อย (
MdbDataSourceหรือXmlDataSource) นั่นจะแก้ปัญหาทั้งหมดของฉัน น่าเสียดายที่ C # ไม่รองรับสิ่งนั้นใช้ชื่ออื่น:
abstract class DataSourceBase { ... } // corresponds to DataSource in the example above class DataSource : DataSourceBase { // corresponds to MdbDataSource in the example above [Obsolete("New code should use DataSourceBase.OpenDataSource instead")] DataSource(string fileName) { ... } ... } class XmlDataSource : DataSourceBase { ... }นั่นคือสิ่งที่ฉันสิ้นสุดที่ใช้เพราะมันช่วยให้ย้อนกลับเข้ากันได้กับรหัส (เช่นการโทรไป
new DataSource("myFile.mdb")ยังที่ทำงาน) ข้อเสียเปรียบ: ชื่อไม่ได้อธิบายอย่างที่ควรจะเป็นสร้าง
DataSource"wrapper" สำหรับการนำไปใช้งานจริง:class DataSource { private DataSourceImpl impl; DataSource(string fileName) { impl = ... ? new MdbDataSourceImpl(fileName) : new XmlDataSourceImpl(fileName); } Data ReadData() { return impl.ReadData(); } abstract private class DataSourceImpl { ... } private class MdbDataSourceImpl : DataSourceImpl { ... } private class XmlDataSourceImpl : DataSourceImpl { ... } }ข้อเสียเปรียบ: ทุกวิธีแหล่งข้อมูล (เช่น
ReadData) ต้องถูกกำหนดเส้นทางโดยรหัสสำเร็จรูป ฉันไม่ชอบรหัสสำเร็จรูป มันซ้ำซ้อนและ clutters รหัส
มีผู้ใดที่สง่างามวิธีการแก้ปัญหาที่ฉันได้พลาด?
newไม่ใช่วิธีการของวัตถุคลาส (เพื่อให้คุณสามารถคลาสย่อยตัวเอง - เทคนิคที่รู้จักกันในชื่อ metaclasses - และควบคุมสิ่งที่newเกิดขึ้นจริง) แต่นั่นไม่ใช่วิธีการทำงานของ C # (หรือ Java หรือ C ++)