1

I have 1 class library which has some code to perform some operation.For instance it will perform 2 operation like:

- Add  
- Multiply

In future may be I can have few more operation like division for eg.

Above are just an example because i have some long running operation with each of this Add,Multiply operation.

So idea of this library is to receive input and then execute long running code against those inputs.

This is what I am thinking:

public class Input
{
   //input properties
}

public interface IOperations
{
    public abstract void Process(Input obj);
}


public class Add : IOperations
{
    Input obj;
    public Add(Input obj)
    {
        this.obj = obj;    
    }
    public override void Process(Input obj)
    {
       //Add method implementation
    }
}


public class Multiply : IOperations
{
    Input obj;
    public Multiply(Input obj)
    {
        this.obj = obj;    
    }
    public override void Process(Input obj)
    {
       //Multiply method implementation
    }
}

Now suppose if I want to perform Add operation or multiply operation then how I will call respective methods based on below type:

string type;
if(type=="Add")
   //perform add operation 
else if(type=="Multiply")
  //perform multiply operation 

But I am not getting proper way to design code structure for above requirement.

Note : Reason for creating IOperations as Interface is for dependency injection

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216
  • Your code doesn't compile?! You've a public declaration in your interface and you're also trying to override an interface implementation?! – Wheels73 Mar 07 '18 at 10:41
  • @vc74 Sorry but this was just an abstract idea in my mind which i have shared hence i have written in question that i am not getting proper way to design structure for this requirement.Reason for interface is for dependency injection – I Love Stackoverflow Mar 07 '18 at 10:42
  • @Wheels73 Sorry but this was just an abstract idea in my mind which i have shared hence i have written in question that i am not getting proper way to design structure for this requirement.Reason for using interface is for dependency injection – I Love Stackoverflow Mar 07 '18 at 10:42
  • You can implement Strategy pattern, similarly like in this [example](http://www.c-sharpcorner.com/article/introduction-to-the-gof-strategy-pattern-in-C-Sharp/). In addition to that, you can enrich interface with Name/Type string property. After that you'll be able to inject (using DI container) all interface implementations, and just filter out implementation you need by Name/Type property – Darjan Bogdan Mar 07 '18 at 10:45
  • Ok. I'd forget the abstract class idea. If all you want to do is implement a class for each math operation, then make each specific class implement IOperations and that will perform the necessary function. You can use a simple factory pattern to spit out which implementation you need based upon "type". – Wheels73 Mar 07 '18 at 10:48
  • @Wheels73 Can you please post sample implementation in the form of answer please – I Love Stackoverflow Mar 07 '18 at 10:48
  • This is unclear, mainly because it's incomplete. I think you picked a good username. Start coding and come back with some concrete questions. – H H Mar 07 '18 at 10:52
  • @HenkHolterman Sorry i dont know what to add now to make this question complete.If you can tell me then i can update my question again with that info.Thanks – I Love Stackoverflow Mar 07 '18 at 10:55
  • @DarjanBogdanSo my above requirement is applicable for GOF pattern and what do you mean by this sentence:"After that you'll be able to inject (using DI container) all interface implementations, and just filter out implementation you need by Name/Type property".Can you provide any sample example in the form of answer for this please – I Love Stackoverflow Mar 07 '18 at 10:59
  • @Learning-Overthinker-Confused - Posted. But remember this is just one approach and are often open to opinion. – Wheels73 Mar 07 '18 at 11:01

2 Answers2

2

Ok, as discussed

Define your classes for the input and result.

public class Input
{
}

public class Result
{
}

Define your interface

public interface IOperations
{
    Result Process(Input obj);
}

Define a factory to return the required implementation

public class MathFactory
{
    public IOperations GetOperatorByType(string type)
    {
        switch (type)
        {
            case "Add":
                return new Add(new Input());
            case "Multiply":
                return new Multiply(new Input());
        }

        throw new Exception("Unknown type.");
    }
}

Define your concrete implementations

public class Add : IOperations
{
    Input obj;
    public Add(Input obj)
    {
        this.obj = obj;
    }

    public Result Process(Input obj)
    {
        //Perform Add here
        return new Result();
    }
}

public class Multiply : IOperations
{
    Input obj;
    public Multiply(Input obj)
    {
        this.obj = obj;
    }

    public Result Process(Input obj)
    {
        //Perform multiply  here
        return new Result();
    }
}

Finally invoke from code.

 private void button1_Click(object sender, EventArgs e)
 {
     var mathFactory = new MathFactory();
     var operation = mathFactory.GetOperatorByType("Add");
     var result = operation.Process(new Input());
}

Obviously you will have to tinker with how the input object is used (constructor as is now or perhaps on the interface)... and how you construct your result and expose the answer as a property.

Hope that helps.

Wheels73
  • 2,850
  • 1
  • 11
  • 20
2

This is a good candidate for the strategy design pattern.

Define a family of algorithms, encapsulate each one, and make them interchangeable.

Interfaces

public interface IOperation
{
    Output Process(Input input);
    bool AppliesTo(string operation);
}

public interface IOperationStrategy
{
    Output Process(string operation, Input input);
}

Operations

public class Add : IOperation
{
    public bool AppliesTo(string operation)
    {
        return nameof(Add).Equals(operation, StringComparison.OrdinalIgnoreCase);
    }

    public Output Process(Input input)
    {
        // Implementation
        return new Output();
    }
}

public class Multiply : IOperation
{
    public bool AppliesTo(string operation)
    {
        return nameof(Multiply).Equals(operation, StringComparison.OrdinalIgnoreCase);
    }

    public Output Process(Input input)
    {
        // Implementation
        return new Output();
    }
}

Strategy

public class OperationStrategy : IOperationStrategy
{
    private readonly IOperation[] operations;

    public OperationStrategy(params IOperation[] operations)
    {
        if (operations == null)
            throw new ArgumentNullException(nameof(operations));
        this.operations = operations;
    }

    public Output Process(string operation, Input input)
    {
        var op = operations.FirstOrDefault(o => o.AppliesTo(operation));
        if (op == null)
            throw new InvalidOperationException($"{operation} not registered.");

        return op.Process(input);
    }
}

Usage

// I am showing this in code, but you would normally 
// do this with your DI container in your composition 
// root, and the instance would be created by injecting 
// it somewhere.
var strategy = new OperationStrategy(
    new Add(), // Inject any dependencies for operation here
    new Multiply()); // Inject any dependencies for operation here

// And then once it is injected, you would simply do this.
// Note that it would not be appropriate to use an Enum for
// the operation, because the compiled Enum would have to 
// remain in sync with the runtime operation values (not possible).
// That said, the data type doesn't necessarily need to be string.

var input = new Input { Value1 = 2, Value2 = 3 };

var output = strategy.Process("add", input);
// output.Value is 5

var output = strategy.Process("multiply", input);
// output.Value is 6

One of the advantages of using this pattern over a factory design is that the design doesn't need to change to add or remove operations. In a factory design, you have a switch case statement hard coded into it that needs to change every time you add an operation.

Of course, there really are no constraints on how your inputs and outputs are setup provided you use the same types for each IOperation. I am only showing it this way, since it is sensible to get the output as a return value of the Process method, but the implementation you use might be different.

Additional Examples

NightOwl888
  • 55,572
  • 24
  • 139
  • 212