1

I'm implementing the template method pattern and in my abstract class I have a method(TemplateMethod) that gets some values and passes them to Step2 method.

The conflict I have is that my two concrete implementations use just a partial set of these parameters so I don't like the idea of passing all parameters and I wouldn't want to modify this method signature if another concrete implementation would require different parameters.

I have read about Parameter Object pattern and saw another very similar question in SO but I'm still not convinced. Any suggestions?

Here is my simplified code:

void Main()
{
    var concreteClassA = new ConcreteClassA();
    concreteClassA.TemplateMethod();
    var concreteClassB = new ConcreteClassB();
    concreteClassB.TemplateMethod();
}

public abstract class AbstractClass
{
    IEngine1 _engine1;
    IEngine2 _engine2;
    public void TemplateMethod() {
        Step1();
        //Get some values
        var id = _engine1.GetId();
        var name = _engine1.GetName();
        var anotherId = _engine2.GetAnotherId();
        var description = _engine2.GetDescription();
        //Pass all values to step 2
        Step2(id, name, anotherId, description);
    }
    public virtual void Step1() { }
    public virtual void Step2(int id, string name, int anotherId, int description) {}
}

public interface IEngine1 { 
    int GetId();
    string GetName();
}
public interface IEngine2
{
    int GetAnotherId();
    int GetDescription();
}

public class ConcreteClassA : AbstractClass
{
    public override void Step2(int id, string name, int anotherId, int description)
    {
        //This class only needs Id and name!
        var entity = new Entity {
            Id = id,
            Name = name
        }
        DoSomethingWithEntity(entity);
    }
    private void DoSomethingWithEntity(Entity entity) {
            //Logic here
    }
}

public class ConcreteClassB : AbstractClass
{
    public override void Step2(int id, string name, int anotherId, int description)
    {
        //This one needs other parameters
        var entity = new Entity
        {
            AnotherId = anotherId,
            Name = name,
            Description = description
        }
        DoSomethingElseWithEntity(entity);
    }
    private void DoSomethingElseWithEntity(Entity entity)
    {
        //Logic here
    }
}

public class Entity { 
    public int Id { get; set; }
    public string Name { get; set; }
    public int AnotherId { get; set; }
    public int Description { get; set;}
}
Adolfo Perez
  • 2,834
  • 4
  • 41
  • 61
  • 1
    Are you using this pattern because it is the right tool for what you are trying to do, or because you *want* to implement the pattern? – poke Mar 03 '18 at 16:32
  • My original code had a big method with different branches like If type == X then build Entity with params a,b,c. If Type == Y then build entity with params b,c,d. I thought template method pattern would be a good option for this – Adolfo Perez Mar 03 '18 at 16:35
  • Looks like there is no need for `TemplateMethod` because each concrete implementation can get parameters it needs from `engine`s directly. – Evk Mar 03 '18 at 16:36
  • It would be great if you could not use `AbstratClass`, `ConcreteClassA`, etc. as opposed to the real names, because it's possible your pattern is another one (Strategy?). When you don't give the details, answers tend to shed more heat than light. – Fuhrmanator Mar 04 '18 at 21:38

1 Answers1

3

You can try to pass a model. You just let the model be a parameter

When You will be changing parameters will not change the Step2 method signature.

Just add property in ParamterContext

Here is the sample code

public class ParameterContext
{
    public int id { get; set; }
    public string name { get; set; }
    public int anotherId { get; set; }
    public int description { get; set; }
}


public abstract class AbstractClass
{
    IEngine1 _engine1;
    IEngine2 _engine2;
    public void TemplateMethod()
    {
        Step1();
        //Get some values
        var id = _engine1.GetId();
        var name = _engine1.GetName();
        var anotherId = _engine2.GetAnotherId();
        var description = _engine2.GetDescription();
        //Pass all values to step 2
        Step2(new ParameterContext() {
            id = id,
            name = name,
            anotherId = anotherId,
            description = description
        });
    }
    public virtual void Step1() { }
    public virtual void Step2(ParameterContext parameter) { }
}

public interface IEngine1
{
    int GetId();
    string GetName();
}
public interface IEngine2
{
    int GetAnotherId();
    int GetDescription();
}

public class ConcreteClassA : AbstractClass
{
    public override void Step2(ParameterContext para)
    {
        //This class only needs Id and name!
        var entity = new Entity
        {
            Id = para.id,
            Name = para.name
        };
        DoSomethingWithEntity(entity);
    }
    private void DoSomethingWithEntity(Entity entity)
    {
        //Logic here
    }
}

public class ConcreteClassB : AbstractClass
{
    public override void Step2(ParameterContext para)
    {
        //This one needs other parameters
        var entity = new Entity
        {
            AnotherId = para.anotherId,
            Name = para.name,
            Description = para.description
        };
        DoSomethingElseWithEntity(entity);
    }
    private void DoSomethingElseWithEntity(Entity entity)
    {
        //Logic here
    }
}

Edit

Another way is

The IEngine1 and IEngine2 interface declared as protect which in AbstractClass class,You can use their method instead of passing parameter.

public abstract class AbstractClass
{
    public AbstractClass() { }

    public AbstractClass(IEngine1 eng1, IEngine2 eng2)
    {
        _engine1 = eng1;
        _engine2 = eng2;
    }

    protected IEngine1 _engine1;
    protected IEngine2 _engine2;
    public void TemplateMethod()
    {
        Step1();
        //Get some values
        //var id = _engine1.GetId();
        //var name = _engine1.GetName();
        //var anotherId = _engine2.GetAnotherId();
        //var description = _engine2.GetDescription();
        //Pass all values to step 2
        Step2();
    }
    public virtual void Step1() { }
    public virtual void Step2() { }
}

public interface IEngine1
{
    int GetId();
    string GetName();
}
public interface IEngine2
{
    int GetAnotherId();
    int GetDescription();
}

public class ConcreteClassA : AbstractClass
{
    public override void Step2()
    {
        //This class only needs Id and name!
        var entity = new Entity
        {
            Id = _engine1.GetId(),
            Name = _engine1.GetName()
        };
        DoSomethingWithEntity(entity);
    }
    private void DoSomethingWithEntity(Entity entity)
    {
        //Logic here
    }
}

public class ConcreteClassB : AbstractClass
{
    public override void Step2()
    {
        //This one needs other parameters
        var entity = new Entity
        {
            AnotherId = _engine2.GetAnotherId(),
            Name = _engine1.GetName(),
            Description = _engine2.GetDescription()
        };
        DoSomethingElseWithEntity(entity);
    }
    private void DoSomethingElseWithEntity(Entity entity)
    {
        //Logic here
    }
}

public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int AnotherId { get; set; }
    public int Description { get; set; }
}
D-Shih
  • 44,943
  • 6
  • 31
  • 51
  • This almost looks like Strategy, since you're passing a context. – Fuhrmanator Mar 04 '18 at 21:39
  • If there are different algorithm on `Step2()` method,that is Strategy pattern. I just encapsulated parameter as Context,and passing as parameter. To solve the `Step2()` signature without changing the parameter method – D-Shih Mar 05 '18 at 05:10