代码之家  ›  专栏  ›  技术社区  ›  Chris

OOP设计:字符串、枚举、接口还是其他?

  •  0
  • Chris  · 技术社区  · 16 年前

    我正在开始一个从遗留系统数据库中提取数据并将其写回的项目。我已经开始研究领域模型,并试图改进过去系统的设计,所以我想得到一些关于这个的反馈。

    这个例子是任意的,因此不需要在那里提供特定的建议,但是假设在数据库中有一个名为“WorkflowStep”的表,我正在为它编写一个类。表中有一列名为“currentStatus”,用于定义工作流的状态。它存储为varchar。此列的整个表中有五个不同的字符串,不太可能更改…值如“打开”、“关闭”、“保留”等。

    这个类需要跟踪这个值,但是以什么方式?我可以走简单的路线,将其存储在一个字符串中,但这并没有很好的定义,我设想未来的开发人员会寻找字符串的不同值来应用逻辑。我可以使用枚举使事情更明确,但这可能会导致整个地方的切换/案例地狱。我已经阅读过一些方法,其中工程师创建一个接口,比如说“i status”,然后创建具体的类来表示状态的每个可能状态,但是在相同的情况下,其他一些列可能有一百个不同的值,因此每个状态的100个类似乎是多余的。

    我的主要问题是:一种方法事实上比其他方法更好吗?如果不是,我应该考虑什么来选择一种方法?

    请注意,该项目仍处于初级阶段,我不确定该类的“status”属性将如何使用。这可能是毫无意义的,也可能是至关重要的:我还不确定。

    8 回复  |  直到 16 年前
        1
  •  2
  •   Ryan Brunner    16 年前

    我认为在任何地方都没有一个明确的答案,但这是一个常见的问题,并且以下方法始终为我提供了很好的服务:

    1. 如果您的业务逻辑或任何代码永远不会对这些值做任何事情(比较、基于枚举值的切换等),请将其保留为字符串。
    2. 如果要对其进行比较,请使用枚举。如果您有许多这样的表,或者这些值可以更改,那么这非常适合代码生成。
    3. 如果值具有与它们相关的其他元数据,请创建包含此元数据的类,以及创建相关类的枚举(在工厂方法中)。同样,代码生成非常适合这里。

    对于像currentStatus这样的东西,听起来选项2或3是自然的适合。如果以下任何一种情况适用,则选择3更可取:

    • 对于包含空格或非字母数字字符(Enum.ToString()无法处理的任何内容)的状态,需要一个“友好”名称。
    • 还有一些附加的逻辑或属性“伴随”某种状态。例如,如果您有一些逻辑,可以对“onhold”或“closed”项执行某个操作,但不能对“open”项执行该操作,那么Mike在类上具有“candoAction”属性是有意义的。
    • 您从中提取的数据库有一个状态参考表,其中包含与您可能希望在代码中使用的状态相关的信息。
        2
  •  2
  •   Arnis Lapsa    16 年前

    我个人更喜欢这种转换语句

    var x = new Dictionary<Status, Action>  
      {Status.Fail, ()=>Console.Writeline("Fail")}
      {Status.Ok, ()=>Console.Writeline("Ok")};  
    
    x[Status.Fail]();
    x[Status.Ok]();
    

    还有-你可能会喜欢 this article 作者:吉米(以防你坚持使用枚举型的解决方案)。

        3
  •  1
  •   Kai    16 年前

    这是一个非常常见的问题,很大程度上取决于对问题的理解。通常我认为这是简单性、可读性和可扩展性之间的交集。

    1. 枚举/常量列表 . 这是最简单的路径,如果您将在代码中工作,而不是在原始数据中工作,那么它具有良好的可读性。扩展性不是很好,因为您需要重构来保持这个简单。
    2. . 字符串的优点是,您可以轻松地一目了然地了解数据库数据。但是,它们倾向于向程序代码中添加cruft,而且它们也不是非常可扩展的。
    3. 参考表 .这是迄今为止这三种方法中最具扩展性的一种。可读性会有所降低,因为现在,只要想弄清楚发生了什么,就必须查找引用表中的值。这三个方面的编码是最复杂的。

    最简单=枚举。最可读=字符串。最可扩展=引用表。

    哪一个最适合你的问题?

        4
  •  1
  •   Vincent Ramdhanie    16 年前

    如果您相当确定状态的选项数量不会增加,那么枚举将起作用。我认为你可以通过一些仔细的设计来避免开关/案例的问题…也许使用策略模式。

    另一方面,如果选项的数量可能增加,那么创建WorkflowStatus类将是一种可行的方法。这将允许您在不修改代码的情况下添加更多状态。我认为你不必为每个个体的状态创建一个类。

        5
  •  1
  •   Pete OHanlon    16 年前

    通常,您最终会将这种类型的项作为枚举引用。如果要避免切换/大小写地狱,可以使用映射到每个枚举的操作-下面是一个示例:

    private Dictionary<WorkflowStep, Action> _actions = 
      new Dictionary<WorkflowStep, Action>();
    

    然后分配给

    _actions.Add(WorkflowStep.Open, OpenCommand);
    _actions.Add(WorkflowStep.Closed, ClosedCommand);
    

    您可以使用以下简单方法:

    private void OpenCommand()
    {
      // Do something
    }
    
    private void ClosedCommand()
    {
      // Do something.
    }
    

    最后,要调用它,您需要执行以下操作:

    if (_actions[step] != null)
    {
      _actions[step]();
    }
    

    请注意,您可以在不使用枚举的情况下对字符串执行此操作,但此方法有助于避免代码中出现魔力字符串。

        6
  •  1
  •   bua    16 年前

    这个 state-pattern 听起来很合理。

    如果您有更多不同的值,您可以使用其中一个根实现,并添加一些 decorator-pattern .

        7
  •  1
  •   Charles Bretana    16 年前

    我建议您研究域驱动设计中定义的“值类型”的概念。这个工作流步骤听起来像是一个主要的候选人。您没有提到您在用什么语言或环境编码,但是在.NET中,如果我最终使用了这个概念,我将创建一个表示这个对象的结构,并对其进行编码,以便在它的公共接口中,在整个系统的其余部分中,客户机代码使用它时,它显示为枚举,并将被用作枚举。

    MyTask.WorkFlowStatus = WorkFlowStep.OnHold; 
    // creates and assigns new instance of struct
    

    在C中

      public struct WorkflowStep 
      {
         public static readonly WorkflowStep Null   = new WorkflowStep();
         public static readonly WorkflowStep Open   = new WorkflowStep(1);
         public static readonly WorkflowStep Closed = new WorkflowStep(2);
         public static readonly WorkflowStep OnHold = new WorkflowStep(3);
         private int val;
         private bool def;
         public bool HasValue { get { return def; } }
         public bool IsNull   { get { return !def ; } }
         private WorkflowStep ( ) { } // disable public instantiation
         private WorkflowStep (int value) 
         {
           val = value;
           def = true ;
         }
      }
    

    您可以在模型中键入其他类的属性,这些属性需要作为 WorkflowStep . 这个结构的每个实例的内存占用非常小,int用于保存值,bool用于保存它的可空状态。您可以向结构添加其他方法和重载,以启用打印格式输出、字符串重载,无论您想要什么…

        8
  •  1
  •   Ewan Todd    16 年前

    我已经读过工程师制作接口的方法,比如说“i status”,然后制作具体的类来表示状态的每个可能状态,

    那就是 State Pattern .

    但是,在与此相同的情况下,其他一些列可能有100个不同的值,因此每个状态的100个类似乎是多余的。

    这似乎是一个单独的问题。如果没有更多的细节,就没什么好说的了。