Vấn đề
Hãy nói rằng tôi có một lớp được gọi là DataSource
cung cấp một ReadData
phương thức (và có thể là các phương thức khác, nhưng hãy giữ mọi thứ đơn giản) để đọc dữ liệu từ một .mdb
tệp:
var source = new DataSource("myFile.mdb");
var data = source.ReadData();
Vài năm sau, tôi quyết định rằng tôi muốn có thể hỗ trợ .xml
các tệp ngoài.mdb
các tệp dưới dạng nguồn dữ liệu. Việc thực hiện "đọc dữ liệu" khá khác nhau đối với các tệp .xml
và .mdb
tệp; do đó, nếu tôi thiết kế hệ thống từ đầu, tôi sẽ định nghĩa nó như thế này:
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 */ }
}
Tuyệt vời, một triển khai hoàn hảo của mẫu phương thức Factory. Thật không may, DataSource
nằm trong một thư viện và tái cấu trúc mã như thế này sẽ phá vỡ tất cả các cuộc gọi hiện có của
var source = new DataSource("myFile.mdb");
trong các khách hàng khác nhau sử dụng thư viện. Khốn nạn cho tôi, tại sao tôi không sử dụng phương pháp nhà máy ngay từ đầu?
Các giải pháp
Đây là những giải pháp tôi có thể đưa ra:
Làm cho hàm tạo DataSource trả về một kiểu con (
MdbDataSource
hoặcXmlDataSource
). Điều đó sẽ giải quyết tất cả các vấn đề của tôi. Thật không may, C # không hỗ trợ điều đó.Sử dụng các tên khác nhau:
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 { ... }
Đó là những gì tôi đã kết thúc bằng cách sử dụng vì nó giữ cho mã tương thích ngược (nghĩa là các cuộc gọi
new DataSource("myFile.mdb")
vẫn hoạt động). Nhược điểm: Các tên không được mô tả như họ cần phải có.Tạo
DataSource
một "trình bao bọc" để thực hiện thực tế: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 { ... } }
Nhược điểm: Mọi phương thức nguồn dữ liệu (như
ReadData
) phải được định tuyến bằng mã soạn sẵn. Tôi không thích mã soạn sẵn. Nó dư thừa và làm lộn xộn mã.
Có bất kỳ giải pháp thanh lịch mà tôi đã bỏ lỡ?
new
không phải là một phương thức của đối tượng lớp (để bạn có thể phân lớp chính nó - một kỹ thuật được gọi là siêu dữ liệu - và kiểm soát những gì new
thực sự làm) nhưng đó không phải là cách C # (hoặc Java hoặc C ++) hoạt động.