This pattern looks very much like a creational pattern called Abstract Factory.
What's nice about design patterns, is that they're a very effective mean of communication with fellow programmers; provided that they know the patterns, when you say TreeFactory
it should be clear what the type is for.
You've called your abstraction TreeType
, which is less clear. The
naming you're using makes things quite confusing, especially in the
client code - compare this:
TreeType oTreeType = new ConcreteTreeType();
ITree banana = oTreeType.GetTree("COCONUT");
Console.WriteLine(banana.GetTreeName());
To this:
ITreeFactory factory = new CoconutTreeFactory();
ITree tree = factory.Create();
Console.WriteLine(tree.GetTreeName());
In C# it's common practice to name interface starting with a capital i
, so this:
public interface TreeType
{
ITree GetTree(string tree);
}
I'd write like this:
public interface ITreeFactory
{
// follow @Stuart's advice here, don't rely on magic strings!
ITree Create(TreeType treeType);
}
TreeType
would be a much better name for an enum
type (enum names should be singular):
public enum TreeType
{
Banana,
Coconut,
Apple,
Orange
}
What you have here, however, isn't quite exactly an abstract factory, because the client code, despite working against an ITree
, knows exactly what type he's getting... which is weird:
The client code can know about ITreeFactory
and ITree
, but the point of an abstract factory is that he doesn't need to know/care about the exact implementation of ITree
that the factory is producing, so your client code can be provided with an ITreeFactory
and call its Create()
method to get an ITree
- whatever implementation that is.
If the client code needs to use the same abstract factory to create multiple instances of different ITree
implementations, then I'd go with a generic abstract factory:
public interface ITreeFactory<TTree> where TTree : ITree
{
ITree Create<TTree>();
}
If TTree
has a parameterless constructor, you can add a generic type constraint to ensure TTree
always has a default constructor, and implement a generic concrete factory class like this:
public class TreeFactory<TTree> : ITreeFactory<TTree>
where TTree : ITree, new()
{
public ITree Create<TTree>()
{
return new TTree();
}
}
That can be called like this:
ITree appleTree = factory.Create<AppleTree>();
ITree orangeTree = factory.Create<OrangeTree>();
I don't like this very much though, because it allows the client code to control the exact implementation type he's getting out of the factory - this smells of Control Freak, a Dependency Injection Anti-Pattern... but sometimes it's just what's needed.
If TTree
doesn't have a parameterless constructor, then the generic concrete factory has a problem:
public class TreeFactory<TTree> : ITreeFactory<TTree>
where TTree : ITree
{
public ITree Create<TTree>()
{
return new ????(); // so, what type is TTree? how do we pass ctor parameters?
}
}
That can't work! This is where the enum type kicks in, when the type of the factory's product is unknown at compile-time - now because each tree type might have different constructor arguments, abstracting them into their own type can simplify things:
public class TreeFactory : ITreeFactory
{
private readonly IDictionary<TreeType, Func<ITreeArgs,ITree>> _create;
public TreeFactory(IDictionary<TreeType, Func<ITreeArgs,ITree>> factoryMethods)
{
_create = factoryMethods;
}
public ITree Create(TreeType treeType, ITreeArgs args)
{
return _create[treeType](args);
}
}
Where ITreeArgs
could perhaps encapsulate an object
that merely exposes an array of constructor arguments, whatever they are. Notice that the factory class itself doesn't even know/care how each TreeType
gets created - it receives a dictionary of factory methods (one for each TreeType
) and simply calls into it in the Create
method; this means if you want to modify how this factory class generates such or such ITree
implementation, you don't even need to change the factory class's code!
This is probably overkill for your example code however, since all your ITree
implementations seem to have a default constructor, therefore the generic factory would work.
It's often best to leave the client code oblivious of implementation details:
public class ClientObject
{
private readonly ITreeFactory _factory;
public ClientObject(ITreeFactory factory)
{
_factory = factory;
}
public void DoSomething()
{
var tree = _factory.Create();
Console.WriteLine(tree.Name); // assuming a "Name" property on `ITree` here
}
}
All ClientObject
knows, is that he's getting an ITree
. The exact implementation type is completely irrelevant, and ClientObject
is completely decoupled from any specific ITree
and ITreeFactory
implementations.
From the very first comment at the top of your code, I believe this is what you're trying to achieve.