Проблема
Допустим, у меня есть класс с именем, 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");
в различных клиентах, использующих библиотеку. Горе мне, почему я вообще не использовал фабричный метод?
Решения
Вот решения, которые я мог бы придумать:
Сделайте так, чтобы конструктор DataSource возвращал подтип (
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«обертку» для реальной реализации: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) должен маршрутизироваться с помощью стандартного кода. Мне не нравится шаблонный код. Это избыточно и загромождает код.
Есть ли какое-нибудь элегантное решение, которое я пропустил?
newне является методом объекта класса (так что вы можете создать подкласс самого класса - метод, известный как метаклассы - и контролировать то, что на newсамом деле делает), но это не так, как работает C # (или Java, или C ++).