我有1个类库,其中有一些代码可以执行一些操作,例如它将执行2个操作,例如:

- Add
- Multiply


将来可能会有更多的操作,例如除法。

上面仅是一个示例,因为我对每个Add,Multiply操作都有一些长时间运行的操作。

因此,该库的想法是接收输入,然后针对这些输入执行长时间运行的代码。

这就是我的想法:

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
    }
}


现在假设如果我要执行加法运算或乘法运算,那么我将如何基于以下类型调用各个方法:

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


但是我没有为上述要求设计代码结构的正确方法。

注意:将IOperations创建为Interface的原因是用于dependency injection

最佳答案

这是strategy design pattern的很好的候选者。


  定义一系列算法,封装每个算法,并使它们可互换。


介面

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

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


运作方式

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();
    }
}


战略

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);
    }
}


用法

// 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


在工厂设计中使用此模式的优点之一是,无需更改设计即可添加或删除操作。在工厂设计中,您有一个硬编码到其中的switch case语句,每次添加操作时都需要更改。

当然,如果您为每个IOperation使用相同的类型,那么对于输入和输出的设置方式实际上没有任何限制。我仅以这种方式显示它,因为将输出作为Process方法的返回值是明智的,但是您使用的实现可能有所不同。

其他例子


Factory method with DI and IoC
Best way to use StructureMap to implement Strategy pattern
Dependency Injection Unity - Conditional Resolving
Dependency injection type-selection

09-30 15:37
查看更多