DEV Community

Discussion on: How to avoid the Factory pattern in C#

Collapse
 
thatblairguy profile image
That Blair Guy • Edited

As presented, I can sort of see an advantage to the factory method technique from the standpoint of not knowing at compile time what concrete types will be available at runtime. (E.g. If the factory method is in a different assembly from the one containing the concrete types.)

My strongest argument against it though is that the factory method is tied to whatever class declares it. A class should only have one purpose, and if that purpose is to create objects matching a particular base class/interface, it should be a factory.

I don't generally do a separate factory for each class. Instead, it's one per Interface (or abstract class, or whatever the common base may be). That would look something like this:

static class AnimalFactory
{
    public static Create(AnimalType type)
    {
       IAnimal animal;
       switch(type)
       {
          case AnimalType.Dog:
              animal = new Dog();
              break;
          case AnimalType.Cat:
              animal = new Cat();
              break;
          case AnimalType.Bird:
              animal = new Bird();
              break;
       }
    }
}
Enter fullscreen mode Exit fullscreen mode

This is a common pattern. But, as alluded to above, the drawback to this particular style is that you have to know all the concrete classes in advance.

A compromise then would be a factory class with a constructor which takes a name/value pair consisiting of an enum (e.g. AnimalType) and the matching creation function. The factory class then would have a Create method which takes an AnimalType value as a parameter, looks up the matching creation method, and calls it. There could very likely be an argument on the factory's Create to pass a data structure containing type-specific attributes such as weight, color, beak-size, number of feet, and so forth.

Very rough version for illustration, almost certainly non-compiling:

enum AnimalType
{
  Dog = 1,
  Cat = 2,
  Horse = 3
}

delegate IAnimal AnimalCreationDelegate();

class AnimalDescriptor
{
  public AnimalType Type;

  // This ends up being a delegate
  public AnimalCreationDelegate Creator()
}

class AnimalFactory
{
  IEnumerable<AnimalDescriptor> creatorList;

  public AnimalFactory(IEnumerable<AnimalDescriptor> creators)
  {
    this.creatorList = creators;
  }  

  public IAnimal Create(AnimalType type)
  {
    AnimalCreationDelegate creator = creatorList.Find(type);
    return creator();
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
thompcd profile image
Corey Thompson

I don't know that this accomplishes the main issue, you still have to know the concrete class in advance. You don't have a concrete class but still have to recompile to add a new type to your enum. I feel that I shouldn't need to add all concrete types to my factory class. By using the author's delegate method, I can define all of my concrete classes where they best fit in my project and just implement a good interface that indicates all of the uses of that class.

The downside of this is definitely traceability though.. Getting a new person on the team to be able to find the source and see what's happening at a glance is a nice perk for sure, which doesn't happen in the more abstract version.

I started doing a lot of JS a year ago.. It seems it's tainted me.

Collapse
 
thatblairguy profile image
That Blair Guy

The enum certainly forces a recompile, but you can use strings instead.

But the Creator methods (whether declared as delegates or Funcs) would encapsulate the knowledge of the concrete classes. All the factory needs to know is that it has a function to call and that function has a specific signature.

Collapse
 
shimmer profile image
Brian Berns

Thank you for the detailed response. Examples are definitely helpful! :)

I think what you’ve done is fine since it’s basically a functional approach that avoids the explosion of traditional factory classes. My only suggestion is to use Func<IAnimal> instead of a delegate. The modern syntax is easier to read and work with, IMHO.

Collapse
 
smartcodinghub profile image
Oscar

Func are nameless. Making arguably "harder" to understand when you find it in the code. Creating a delegate allows you to name it and make easier to "inject" it using DI. And having a name also expresses what you intend to do with that function.

Collapse
 
thatblairguy profile image
That Blair Guy • Edited

The delegate is definitely a bit old school, but for purposes of illustration, I felt the explicit signature lent some clarity to what I was trying to communicate. In real-world code, a Func<T> might very well be preferable.

As I commented elsewhere, factories which only wrap a new are good for demonstrating "Here's how a factory works," but don't provide much other value. Having a separate factory for each class, particularly when they're just "wrap the new", is something to avoid; I'm tempted to call it an anti-pattern.

I suspect there's something similar at play here with the description of factory methods. I understand how to write them, you've communicated that pretty darn well. But I'm struggling with the compelling reason why they're preferable to well-designed factory classes.