Skip to main content
Code Review

Return to Answer

replaced http://yuml.me/ with https://yuml.me/
Source Link
deleted 11 characters in body
Source Link
Mathieu Guindon
  • 75.5k
  • 18
  • 194
  • 467
added a flexible factory implementation that can deal with varying constructor arguments
Source Link
Mathieu Guindon
  • 75.5k
  • 18
  • 194
  • 467

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.



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.


Source Link
Mathieu Guindon
  • 75.5k
  • 18
  • 194
  • 467
Loading
lang-cs

AltStyle によって変換されたページ (->オリジナル) /