15

Let's say (just for the sake of example) I have three classes that implement IShape. One is a Square with a constructor of Square(int length). Second is a Triangle with a constructor of Triangle(int base, int height). Third is a Circle with a constructor of Circle(double radius).

Considering all the classes share the same interface, my mind goes to the factory pattern as a creational pattern to use. But, the factory method would be awkward as it must provide parameters for these various constructors - for instance:

IShape CreateShape(int length, int base, int height, double radius)
{
    ...
    return new Circle(radius);

    ...
    return new Triage(base, height);

    ...
    return new Square(length);
}

This factory method seems quite awkward. Is this where an abstract factory or some other design pattern comes into play as a superior approach?

3
  • Abstract Factory will make significantly harder and complex. A single factory should be ok. Have you considered adding one method per shape to the factory? And do that following the interface segregation principle?
    – Laiv
    Mar 31, 2019 at 0:20
  • If I add a method per shape, then I’m unsure why I need the factory. Could you perhaps provide a code sample to illustrate?
    – Craig
    Mar 31, 2019 at 0:25
  • 4
    Patterns help to solve a problem in your code. What problem you has? Why consumer can not create a shape it need directly with a constructor?
    – Fabio
    Mar 31, 2019 at 1:19

4 Answers 4

25

You have a solution looking for a problem, that is why you run into trouble.

A factory method is not an end in itself, it is a means to an end. So you need to start identifying the problem you want to solve first, which means you need a use case for constructing those objects, providing you with the necessary context. Like:

  • you have an external data source like a file stream or database with object descriptions

  • you want a factory to create IShape objects from this data source (so having one and only one place in code to modify in case the list of shapes gets extended)

In the "file stream" context, for example, a CreateShape factory method could probably get a string as a parameter, containing one object description (maybe some CSV string, a JSON string or an XML snippet), and the requirement would be to parse that string to create the right object:

IShape CreateShape(string shapeDescription)
{
    switch(getShapeType(shapeDescription))
    {
      case "Circle":
          radius=parseRadius(shapeDescription);
          return new Circle(radius);

      case "Triangle":
          base=parseBase(shapeDescription);
          height=parseHeight(shapeDescription);
          return new Triangle(base, height);
    ...

}

Now the parameter list of this method does not look quite so awkward any more, I guess?

Other potential use cases:

  • shapes are created based on user inputs: the factory gets part of the user input data as a parameter

  • creating shapes based on some dynamic business logic

You also need to take other, non-functional requirements into account:

  • do you want your factory to assist in decoupling from that external data source? For example, for unit testing? Then make it not just a method, make it a class with an interface, which can be mocked out.

  • do you want the factory itself to be a reusable component, following the Open/Closed principle, where the code does not have to be touched even when new shapes should be added? Then you need to build it in a more generic way, either using reflection, generics, the Prototype pattern, or the Strategy pattern.

And yes, for certain use cases you will probably need no factory method at all.

So in short: clarify your requirements first. If you don't know the context for using the factory method, you don't need it yet.

6
  • What about the strategy pattern? dofactory.com/net/strategy-design-pattern
    – Tarabass
    Apr 4, 2019 at 19:18
  • 2
    @Tarabass: to solve which problem precisely?
    – Doc Brown
    Apr 4, 2019 at 19:36
  • To be compatible with OCP.
    – Tarabass
    May 24, 2019 at 6:47
  • @Tarabass: I can think of a scenario where utilizing strategy pattern would make sense in my example above (see my edit). But like all the other things I said above, this is a trade-off: one has to check if the additional complexity is worth it, and if a simpler solution may not be sufficient.
    – Doc Brown
    May 24, 2019 at 11:38
10

Factory class

Use a factory class, which can have several methods. The factory should have its own interface.

interface IShapeFactory
{
    IShape CreateRectangle(float width, float height);
    IShape CreateCircle(float radius);
}

class ShapeFactory : IShapeFactory
{
    ///etc....

Generic factory method

If you'd rather stick with a factory method, and wish to parameterize the type using a generic type parameter, you have a little work to do to make the inputs generic as well.

The trick is to define an interface for the input parameters (e.g. IShapeArgsFor<T>). Because the interface is tied to T, the compiler can infer the rest:

T CreateShape<T>(IShapeArgsFor<T> input) where T : IShape

Supported by

interface IShapeArgsFor<T> where T : IShape
{
}

And

class CircleArgs : IShapeArgsFor<Circle>
{
    public float Radius { get; }
}

class RectangleArgs : IShapeArgsFor<Rectangle>
{
    public float Height { get; }
    public float Width { get; }
}

etc....

You'd then call it like this:

var circle = CreateShape(new CircleArgs { Radius = 3 });
3
  • 2
    Why the factory method better then new Circle(radius: 3);? What problem we solved with a factory method or class?
    – Fabio
    Mar 31, 2019 at 7:51
  • 2
    Depends on what Circle does. If it's just a plain data container with no behavior, there isn't much benefit. If it has behavior, side effects, or dependencies, the factory gives you something to inject (if you use dependency injection) and something to stub (if you use automated unit testing).
    – John Wu
    Mar 31, 2019 at 7:55
  • Thanks, that I was expecting to hear, constructor and factory has same API contract(same arguments need to be provided), and because shape's constructors do not receive any other dependencies - you don't need to mock it for automated tests, so there no need in a factory for this particular case.
    – Fabio
    Mar 31, 2019 at 8:41
2

This factory method seems quite awkward.

The client code which calls the factory has to pass parameters for all possible shapes. Furthermore, the parameters which don't apply for the desired shape need to be stubbed out. To get a triangle, the call would be

CreateShape(length: 0, base: 21, height: 42, radius: 0)  // returns a triangle
// 0 or a negative number is a special value

How would the calling code know what parameters to stub out?
There are two options:

  • The calling code doesn't know. It gets the shape data somewhere and passes it through to the factory. The incoming shape data already has all the necessary stubs.
    This is a valid scenario.

  • The calling code adds the stubs. Effectively, the calling code would have to "know" what shape it wants (otherwise it doesn't know which parameters are unneeded).
    That would defeat the purpose of the factory.

7
  • What shape would you create with CreateShape(length: 10, base: 21, height: 10, radius: 1). In other words, would you validate all the permutations to decide what shape to create? What if it gets all the arguments? what shape will you create?
    – Laiv
    Mar 31, 2019 at 0:34
  • @Laiv I would validate the inputs, of course. I would throw an exception if the shape is defined ambiguously. For the arguments which you have provided, I would have to throw an exception, because it's not possible to determine if you want a Square or a Circle. Mar 31, 2019 at 0:35
  • Hm. Would not you find the API of such Factory to be a little bit messy?
    – Laiv
    Mar 31, 2019 at 0:36
  • @Laiv I do find it messy and contrived. I'd add an explicit parameter for the desired shape type, if it were my question. Mar 31, 2019 at 0:38
  • How this factory differ with constructors?
    – Fabio
    Mar 31, 2019 at 1:21
1

One option could be to use an abstract builder with abstract factory pattern.

E.g. ShapeBuilder and its subclasses CircleBuilder, TriangleBuilder and RectangleBuilder.

The class that calls factory can construct a builder object and cast it back to ShapeBuilder.

The createShape method would accept a ShapeBuilder object and make a decision on ShapeBuilder.type and further cast ShapeBuilder to one of the subclasses of it.

The createShape method then can safely access the attributes required for building the object and send the object back.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.