ปัญหา
สมมติว่าฉันมีคลาสที่เรียกว่า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 ++)