简体   繁体   English

OOP function 的最佳实践,具有多个可能的控制流

[英]Best Practice for OOP function with multiple possible control flows

In my project, I have this special function that does needs to evaluate the following:在我的项目中,我有这个特殊的 function 确实需要评估以下内容:

  1. State -- represented by an enum -- and there are about 6 different states State——由一个枚举表示——大约有6种不同的状态
  2. Left Argument左参数
  3. Right Argument正确的论点

Left and Right arguments are represented by strings, but their values can be the following:左右 arguments 用字符串表示,但它们的值可以如下:

  1. "_" (a wildcard) “_”(通配符)
  2. "1" (an integer string) “1”(一个 integer 字符串)
  3. "abc" (a normal string) “abc”(普通字符串)

So as you can see, to cover all every single possibility, there's about 2 * 3 * 6 = 36 different logics to evaluate and of course, using if-else in one giant function will not be feasible at all.如您所见,要涵盖所有的所有可能性,大约需要评估 2 * 3 * 6 = 36 种不同的逻辑,当然,在一个巨大的 function 中使用 if-else 根本不可行。 I have encapsulated the above 3 input into an object that I'll pass to my function.我已将上述 3 个输入封装到 object 中,然后将其传递给我的 function。

How would one try to use OOP to solve this.如何尝试使用 OOP 来解决这个问题。 Would it make sense to have 6 different subclasses of the main State class with an evaluate() method, and then in their respective methods, I have if else statements to check:使用 evaluate() 方法拥有主要 State class 的 6 个不同子类是否有意义,然后在它们各自的方法中,我有 if else 语句要检查:

  • if left & right arg are wildcards, do something如果左右 arg 是通配符,做一些事情
  • if left is number, right is string, do something else如果左边是数字,右边是字符串,做别的
  • Repeat for all the valid combinations in each State subclass对每个 State 子类中的所有有效组合重复

This feels like the right direction, but it also feels like theres alot of duplicate logic (for example check if both args are wildcards, or both strings etc.) for all 6 subclasses.这感觉是正确的方向,但也感觉所有 6 个子类都有很多重复的逻辑(例如检查两个 args 是通配符还是两个字符串等)。 Then my thought is to abstract it abit more and make another subclass:然后我的想法是进一步抽象它并制作另一个子类:

For each state subclass, I have stateWithTwoWildCards, statewithTwoString etc.对于每个 state 子类,我有 stateWithTwoWildCards、statewithTwoString 等。

But I feel like this is going way overboard and over-engineering and being "too" specific (I get that this technically adheres tightly to SOLID, especially SRP and OCP concepts).但我觉得这太过分了,过度设计并且“太”具体了(我知道这在技术上与 SOLID 紧密相关,尤其是 SRP 和 OCP 概念)。 Any thoughts on this?对此有什么想法吗?

Possibly something like template method pattern can be useful in this case.在这种情况下,模板方法模式之类的东西可能很有用。 Ie you will encapsulate all the checking logic in the base State.evaluate method and create several methods which subclasses will override.即,您将在基础State.evaluate方法中封装所有检查逻辑,并创建几个子类将覆盖的方法。 Something along this lines:沿着这条线的东西:

class StateBase
   def evaluate():
       if(bothWildcards)
          evalBothWildcards()
       else if(bothStrings)
          evalBothStrings()
       else if ...
   
   def evalBothWildcards():
      ...
   def evalBothStrings():
      ...

Where evalBothWildcards , evalBothStrings , etc. will be overloaded in inheritors.其中evalBothWildcardsevalBothStrings等将在继承者中重载。

If you have a lot if else statements, it is possible to use Chain of Responsibility pattern .如果您有很多if else语句,则可以使用 责任链模式 As wiki says about Chain of Responsibility :正如wiki所说Chain of Responsibility

The chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects.责任链模式是一种行为设计模式,由一个命令对象源和一系列处理对象组成。 Each processing object contains logic that defines the types of command objects that it can handle;每个处理 object 都包含定义它可以处理的命令对象类型的逻辑; the rest are passed to the next processing object in the chain. rest 被传递给链中的下一个处理 object。 A mechanism also exists for adding new processing objects to the end of this chain还存在一种将新处理对象添加到此链末尾的机制

So let's dive in code.因此,让我们深入研究代码。 Let me show an example via C#.让我通过 C# 展示一个示例。

So this is our Argument class which has Left and Right operands:所以这是我们的Argument Right ,它有Left操作数:

public class Arguments
{
    public string Left { get; private set; }

    public string Right { get; private set; }

    public MyState MyState { get; private set; }

    public MyKey MyKey => new MyKey(MyState, Left);

    public Arguments(string left, string right, MyState myState)
    {
        Left = left;
        Right = right;
        MyState = myState;
    }
}

And this is your 6 states:这是你的 6 个状态:

public enum MyState
{
    One, Two, Three, Four, Five, Six
}

This is start of Decorator pattern.这是装饰器模式的开始。 This is an abstraction of StateHandler which defines behaviour to to set next handler:这是 StateHandler 的抽象,它定义了设置下一个处理程序的行为:

public abstract class StateHandler
{
    public abstract MyState State { get; }

    private StateHandler _nextStateHandler;

    public void SetSuccessor(StateHandler nextStateHandler)
    {
        _nextStateHandler = nextStateHandler;
    }

    public virtual IDifferentLogicStrategy Execute(Arguments arguments)
    {
        if (_nextStateHandler != null)
            return _nextStateHandler.Execute(arguments);

        return null;
    }
}

and its concrete implementations of StateHandler :及其StateHandler的具体实现:

public class OneStateHandler : StateHandler 
{
    public override MyState State => MyState.One;

    public override IDifferentLogicStrategy Execute(Arguments arguments)
    {   
        if (arguments.MyState == State)
            return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);
        
        return base.Execute(arguments);
    }
}

public class TwoStateHandler : StateHandler
{
    public override MyState State => MyState.Two;

    public override IDifferentLogicStrategy Execute(Arguments arguments)
    {
        if (arguments.MyState == State)
            return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);

        return base.Execute(arguments);
    }
}

and the third state handler looks like this:第三个 state 处理程序如下所示:

public class ThreeStateHandler : StateHandler
{
    public override MyState State => MyState.Three;

    public override IDifferentLogicStrategy Execute(Arguments arguments)
    {
        if (arguments.MyState == State)
            return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);

        return base.Execute(arguments);
    }
}

Let's pay attention to the following row of code:我们注意下面这行代码:

return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);

The above code is an example of using Strategy pattern .上面的代码是一个使用策略模式的例子。 We have different ways or strategies to handle your cases.我们有不同的方式或策略来处理您的案件。 Let me show a code of strategies of evaluation of your expressions.让我展示一个评估你的表达式的策略代码。

This is an abstraction of strategy:这是策略的抽象:

public interface IDifferentLogicStrategy
{
    string Evaluate(Arguments arguments);
}

And its concrete implementations:及其具体实现:

public class StrategyWildCardStateOne : IDifferentLogicStrategy
{
    public string Evaluate(Arguments arguments)
    {
        // your logic here to evaluate "_" (a wildcard)
        return "StrategyWildCardStateOne";
    }
}

public class StrategyIntegerStringStateOne : IDifferentLogicStrategy
{
    public string Evaluate(Arguments arguments)
    {
        // your logic here to evaluate "1" (an integer string)
        return "StrategyIntegerStringStateOne";
    }
}

And the third strategy:第三个策略:

public class StrategyNormalStringStateOne : IDifferentLogicStrategy
{
    public string Evaluate(Arguments arguments)
    {
        // your logic here to evaluate "abc" (a normal string)
        return "StrategyNormalStringStateOne";
    }
}

We need a place where we can store strategies by state and argument value.我们需要一个可以通过 state 和参数值存储策略的地方。 At first, let's create MyKey struct.首先,让我们创建MyKey结构。 It will have help us to differentiate State and arguments:它将帮助我们区分 State 和 arguments:

public struct MyKey
{
    public readonly MyState MyState { get; }

    public readonly string ArgumentValue { get; } // your three cases: "_", 
        // an integer string, a normal string

    public MyKey(MyState myState, string argumentValue)
    {
        MyState = myState;
        ArgumentValue = argumentValue;
    }
    
    public override bool Equals([NotNullWhen(true)] object? obj)
    {
        return obj is MyKey mys
            && mys.MyState == MyState
            && mys.ArgumentValue == ArgumentValue;
    }

    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            hash = hash * 23 + MyState.GetHashCode();
            hash = hash * 23 + ArgumentValue.GetHashCode();
            return hash;
        }
    }
}

and then we can create a simple factory:然后我们可以创建一个简单的工厂:

public class StrategyStateFactory 
{
    private Dictionary<MyKey, IDifferentLogicStrategy> 
        _differentLogicStrategyByStateAndValue = 
            new Dictionary<MyKey, IDifferentLogicStrategy>()
    {
        { new MyKey(MyState.One, "_"), new StrategyWildCardStateOne() },
        { new MyKey(MyState.One, "intString"), 
            new StrategyIntegerStringStateOne() },
        { new MyKey(MyState.One, "normalString"), 
            new StrategyNormalStringStateOne() }
    };

    public IDifferentLogicStrategy GetInstanceByMyKey(MyKey myKey) 
    {
        return _differentLogicStrategyByStateAndValue[myKey];
    }
}

So we've written our strategies and we've stored these strategies in simple factory StrategyStateFactory .所以我们已经编写了我们的策略并将这些策略存储在简单的工厂StrategyStateFactory中。

Then we need to check the above implementation:然后我们需要检查上面的实现:

StateHandler chain = new OneStateHandler();
StateHandler secondStateHandler = new TwoStateHandler();
StateHandler thirdStateHandler = new ThreeStateHandler();

chain.SetSuccessor(secondStateHandler);
secondStateHandler.SetSuccessor(thirdStateHandler);

Arguments arguments = new Arguments("_", "_", MyState.One);    
IDifferentLogicStrategy differentLogicStrategy = chain.Execute(arguments);

string evaluatedResult = 
differentLogicStrategy.Evaluate(arguments); // output: "StrategyWildCardStateOne"

I believe I gave basic idea how it can be done.我相信我给出了如何做到这一点的基本想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM