jeudi 3 octobre 2019

Pros and cons of Factory pattern vs Inners pattern

There is a design pattern a collegue of mine uses where instead of following a factory pattern he does something close but not quite. Here's an example of it:

The Inners Pattern

Consider the following IExecuter interface:

public interface IExecuter
{
    bool CanExecute(IExecutable executable);

    void Execute(IExecutable executable);
}

There are three classes that implement IExecuter:

internal class FooExecuter : IExecuter
{
    public bool CanExecute(IExecutable executable)
    {
        return executable.Type == ExecutableType.Foo;
    }

    public void Execute(IExecutable executable)
    {
        if (CanExecute(executable))
        {
            // Execute foo.
        }
        throw new NotSupportedException();
    }
}

internal class BarExecuter : IExecuter
{
    public bool CanExecute(IExecutable executable)
    {
        return executable.Type == ExecutableType.Bar;
    }

    public void Execute(IExecutable executable)
    {
        if (CanExecute(executable))
        {
            // Execute bar.
        }
        throw new NotSupportedException();
    }
}

public class Executer : IExecuter
{
    private readonly IReadOnlyList<IExecuter> _inners;

    public Executer()
    {
        _inners = new List<IExecuter>
        {
            new FooExecuter(),
            new BarExecuter()
        }
    }

    public bool CanExecute(IExecutable executable)
    {
        return _inners.Any(i => i.CanExecute(executable));
    }

    public void Execute(IExecutable executable)
    {
        for (var i = 0; i < _inners.Count; i++)
        {
            var inner = _inners[i];
            if (inner.CanExecute(executable))
            {
                inner.Execute(executable);
            }
        }
        throw new NotSupportedException();
    }
}

Now, the only class that consumers need to worry about creating is the outer Executer class. When the consumer wants to call an execute action, this is the code needed:

var executer = new Executer();
executer.Execute(executable);

The Executer takes care of routing the object to the appropriate inner executer.

The Factory Pattern

The same logic can be implemented following the factory pattern as follows:

public interface IExecuter
{
    void Execute(IExecutable executable);
}

internal class FooExecuter : IExecuter
{
    public void Execute(IExecutable executable)
    {
        // Execute foo.
    }
}

internal class BarExecuter : IExecuter
{
    public void Execute(IExecutable executable)
    {
        // Execute bar.
    }
}

public class ExecuterFactory
{
    public IExecuter CreateExecuter(IExecutable executable)
    {
        switch (executable.Type)
        {
            case ExecuterType.Foo:
                return new FooExecuter();
            case ExecuterType.Bar:
                return new BarExecuter();
            default:
                throw new NotSupportedException();
        }
    }
}

The consumer needs to write 3 lines of code to execute:

var factory = new ExecuterFactory();
var executer = factory.CreateExecuter(executable);
executer.Execute(executable);

There are some pros and cons to the inner pattern that are apparent to me.

Inner pattern pros:

  • The consumer doesn't care about knowing there will be different executers for different executables.
  • The consumer gets to write less code.
  • The inners can be nested, where BarExecuter can contain inners of its own and same for the FooExecuter.
  • The nested conditions can be simplified because their effect is compounded. Applying the same conditions in a factory class can result in some really complex conditions.

Inner pattern cons:

  • All inner objects are created when creating the outer object whether you need them or not. But the pattern is usually followed when the object creation is not costly.
  • The inner conditions can conflict, so the order of the _inners list actually matters. But same applies for the Factory pattern as well.

Does any body know if this "Inner pattern" is a known design pattern? If yes, what is it?

Are there any other pros or cons to the factory vs the inner pattern?

If both are fine, are there clear set of rules for when should I use which?

Aucun commentaire:

Enregistrer un commentaire