Pergunta

On Wiki page for Factory method pattern, there is following example:

public interface IPerson
{
    string GetName();
}

public class Villager : IPerson
{
    public string GetName()
    {
        return "Village Person";
    }
}

public class CityPerson : IPerson
{
    public string GetName()
    {
        return "City Person";
    }
}

public enum PersonType
{
    Rural,
    Urban
}

public class Factory
{
    public IPerson GetPerson(PersonType type)
    {
        switch (type)
        {
            case PersonType.Rural:
                return new Villager();
            case PersonType.Urban:
                return new CityPerson();
            default:
                throw new NotSupportedException();
        }
    }
}

Here we are switching on the PersonType to determine which object to instantiate. I know that it makes sense to use Factory method pattern to decouple the creation of an object out of multiple objects of same type but the above code does not satisy the open-closed principle. Whenever there is new type of IPerson we will have to make changes to the implementation of the Factory and if we consider future, overtime this factory method can grow pretty big.

Another pretty common example for Factory pattern is instantiation of database where you can choose among different database servers. In this case we can have a config file and specify which database to instantiate and we are kind of creating only one object out of multiple choices. In the above case for IPerson we are instantiating multiple object of multiple choices. Is Factory pattern a correct choice or am I thinking too much?

Foi útil?

Solução

the above code does not satisy the open-closed principle. Whenever there is new type of IPerson we will have to make changes to the implementation of the Factory and if we consider future, overtime this factory method can grow pretty big.

If you were strictly following the open-closed principle, you would not actually modify the factory. You'd extend it.

For example, if you added a new PersonType named Homeless, you could do this:

public class ExtendedFactory : Factory
{
    public override IPerson GetPerson(PersonType type)
    {
        if (type == PersonType.Homeless) return new Hobo();
        return base.GetPerson(type);
    }
}

That being said, I would probably modify the factory in place, as following the OCP in this case would lead to more complexity than is really necessary.

Outras dicas

Yes, this is an appropriate example for the factory method:

  • The GoF identifies two main variants for the implementation of this pattern. This is the second one, where the Factory is concrete. The cleaner variant would be an abstract Factory class with concrete factories overriding the abstract factory method.

  • Another variant is about the invocation of the factory method. The basic pattern does not create any coupling: the abstract Factory class is meant for dependency injection and the concrete method therefore returns only one kind of product. In your case, it's the more elaborate variant of the parameterized factory method.

Indeed, your concrete parametized implementation creates a coupling between the factory and the all the specialisations.

A more flexible approach in the sense of the open/close principle would be to let all the specialisations register dynamically (example) to the factory. The enum argument would then be replaced by a dynamic ID (for example returned at the registration) or a string. However, even if there would no longer be an undesired coupling in the code, there would still be a hidden dependency between the factory consumer and the classes it want to instantiate (since it has to know the possible arguments it could use).

Your concern of static factory method violating open close principle is really not a concern when the pattern is employed in the main side of the code. If it is used in application side then it could be an issue.

When code follows Open-Close principle the code becomes scalable. However the complexity is also increased. So the decision of follow open-close principle or not has to be exercised based on the tradeoffs. If we make our code 100% open-close, then the complexity will be very very high.

It would be a good design for an application (be it web, desktop, embedded, and so on) to segregate the code in to application (business) code and main (bootstrapping) code.

Application side code

  • Would contain business logic and high level policies.
  • Most of the code in the project would come in this side.
  • Here most of the principles are followed (or at least hoped to be followed).
  • This side ideally could have 100% unit test coverage.
  • This side should not depend on main side.

Main side code

  • Contains implementation of dependencies and the factory logic for choosing the specific implementation for a dependency.
  • This is also called the bootstrapping code.
  • The code at the main side has to be generally small and very simple and doesn't need to scale as it is written for a specific deployment or a specific client.
  • The main side of the code is outer most layer of code. Nothing else in the project depends on the main side code. The main side code depends on almost every other part of the project.
  • Due to the nature of main side code, the cost of modification is quite low.

Summary

The static factory patterns are generally used in the main side code where the complexity is low and the cost of modification is also low.

The below class diagram could help in visualization. The interactions between the classes in the main side is NOT shown. In the diagram below bootstrappingMain and dependencyProject is considered as main side code Class Diagram of a generic application

Licenciado em: CC-BY-SA com atribuição
scroll top