The problem
Let's say I have a class called DataSource
which provides a ReadData
method (and maybe others, but let's keep things simple) to read data from an .mdb
file:
var source = new DataSource("myFile.mdb");
var data = source.ReadData();
A few years later, I decide that I want to be able to support .xml
files in addition to .mdb
files as data sources. The implementation for "reading data" is quite different for .xml
and .mdb
files; thus, if I were to design the system from scratch, I'd define it like this:
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 */ }
}
Great, a perfect implementation of the Factory method pattern. Unfortunately, DataSource
is located in a library and refactoring the code like this would break all existing calls of
var source = new DataSource("myFile.mdb");
in the various clients using the library. Woe is me, why didn't I use a factory method in the first place?
Solutions
These are the solutions I could come up with:
Make the DataSource constructor return a subtype (
MdbDataSource
orXmlDataSource
). That would solve all my problems. Unfortunately, C# does not support that.Use different names:
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 { ... }
That's what I ended up using since it keeps the code backwards-compatible (i.e. calls to
new DataSource("myFile.mdb")
still work). Drawback: The names are not as descriptive as they should be.Make
DataSource
a "wrapper" for the real implementation: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 { ... } }
Drawback: Every data source method (such as
ReadData
) must be routed by boilerplate code. I don't like boilerplate code. It's redundant and clutters the code.
Is there any elegant solution that I have missed?
new
isn't a method of the class object (so that you could subclass the class itself — a technique known as metaclasses — and control whatnew
actually does) but that's not how C# (or Java, or C++) works.