问题
假设我有一个名为的类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
)都必须通过样板代码进行路由。我不喜欢样板代码。这是多余的,会使代码混乱。
有没有我错过的优雅解决方案?
5
您能否详细说明#3的问题?在我看来,这很优雅。(或者优雅的你,同时保持向后兼容性。)
—
PDR
我将定义一个发布API的接口,然后通过让工厂为您的旧代码创建包装器,并通过让工厂创建实现该接口的类的相应实例来包装新代码来重用现有方法。
—
托马斯
@pdr:1.方法签名的每项更改都必须在一个地方进行。2.我可以将Impl类设置为内部和私有类,如果客户端希望访问仅在Xml数据源中可用的特定功能,则这会带来不便。或者我可以将它们公开,这意味着客户现在有两种不同的方式来做同一件事。
—
Heinzi 2013年
@Heinzi:我更喜欢选项3。这是标准的“外观”模式。您应该检查是否真的必须将每个数据源方法委托给实现,还是仅委托给某些实现。也许还有一些通用代码保留在“ DataSource”中?
—
Doc Brown
遗憾的是,
—
Donal Fellows
new
它不是类对象的方法(因此您可以将类本身子类化(一种称为元类的技术,并控制new
实际执行的操作)),但这不是C#(或Java或C ++)的工作方式。