【问题标题】:Routing an object in C# without using switch statements在 C# 中路由对象而不使用 switch 语句
【发布时间】:2014-08-07 17:07:54
【问题描述】:

我正在用 c# .net 4.0 编写一个软件,但在确保代码库在特定领域具有可扩展性、可重用性和灵活性方面遇到了困难。

我们有数据需要在不同的组织单位中进行分解。随着公司的发展,这些单位将需要更改、分类、删除和添加。

无论我们如何分割数据结构,我们都会遇到大量条件语句(开始时大约 100 条左右),我们试图避免这些语句,从而使我们能够轻松地修改 OU。

我们希望找到一种面向对象的方法,允许我们根据对象的属性将对象路由到不同的工作流,而不必每次都添加 switch 语句。

例如,假设我有一个名为“Order”的对象进入系统。这个对象里面有“orderItems”。这些不同种类的“orderItems”中的每一种都需要在代码中触发不同的函数才能进行适当的处​​理。每个“orderItem”都有不同的工作流程。条件看起来基本上是这样的 -

if(order.orderitem == 'photo')
  {do this} 
else if(order.orderitem == 'canvas')
  {do this}

编辑:试图澄清。

【问题讨论】:

  • 看起来您需要应用设计模式。当您必须根据条件切换逻辑时,工厂和抽象工厂模式是候选者。否则,请向我们展示代码,以便我们建议相应地对其进行重构。
  • 你研究过控制反转(IoC)吗?stackoverflow.com/questions/250451/…

标签: c# .net object conditional-statements object-oriented-analysis


【解决方案1】:

我不确定你的问题是否定义得很好,你需要更多细节在这里 - 一段数据样本,一段代码样本,你尝试了什么......

无论我们如何分割数据结构,我们都会遇到大量条件语句(开始时超过 100 个左右),我们试图避免

这通常意味着您正在尝试在代码中编码数据 - 只需添加一个数据字段(或几个)。

您的ifs 可能是相互关联的,很难提出 100 个独立的 if - 这意味着您有 100 个独立的分支来处理 100 个独立的数据条件。在我的职业生涯中,我还没有遇到过真正需要硬编码 100 个 if 的事情。

在最坏的情况下,您可以使附加数据字段包含配置文件甚至您选择的脚本。无论哪种情况 - 如果您需要 100 个 if,您的数据就不完整


随着您在问题中提出的更新,这里有一个简单方法,一种低技术。你可以通过依赖注入和一些配置做得更好,但也可能会过度,所以要小心:

public class OrderHandler{ 
  public static Dictionary<string,OrderHandler> Handlers = new Dictionary<string,OrderHandler>(){
  {"photo", new PhotoHandler()},
  {"canvas", new CanvasHandler()},
};

  public virtual void Handle(Order order){
    var handler = handlers[order.OrderType];
    handler.Handle(order);
  } 
}

public class PhotoHandler: OrderHandler{...}
public class CanvasHandler: OrderHandler{...}

【讨论】:

  • 我试图尽可能清楚。因此,例如,假设我有一个名为“Order”的对象进入系统。这个对象里面有“orderItems”。这些不同种类的“orderItems”中的每一种都需要在代码中触发不同的函数才能进行适当的处​​理。每个“orderItem”都有不同的工作流程。条件看起来基本上是这样的 - if(order.orderitem == 'photo'){do this} else if(order.orderitem == 'canvas'){do this}
  • @Cory 这是一个简单的示例解决方案
【解决方案2】:

您可以执行的操作称为“基于消息的路由”或“基于消息内容的”路由,具体取决于您的实现方式。

简而言之,与其在业务逻辑中使用条件语句,不如实现组织单元来查找他们感兴趣的消息。

例如: 假设您的组织有以下部门 - “植物产品”、“纸制品”、“公用事业”。假设只有一个订单进入的地方 - 订购(模块)。
这是一个示例传入消息。

Party:"ABC Cop"
Department: "Plant Product"
Qty: 50
Product: "Some plan"

发布包含此信息的消息。在处理“植物产品”订单的模块中,对其进行配置,使其监听具有“部门=植物产品”的消息。这样,您将责任推到部门模块上,而不是主订购模块上。

您可以使用 NServiceBus、BizTalk 或您可能已经拥有的任何其他 ESB 来执行此操作。

This 是您在 BizTalk 中的操作方式,this 是您在 NServiceBus 中的操作方式

【讨论】:

    【解决方案3】:

    您是否考虑过子类型化OrderItem

    public class PhotoOrderItem : OrderItem {}
    public class CanvasOrderItem : OrderItem {}
    

    另一种选择是使用策略模式。在OrderProcessStrategyOrderItem 类定义中添加一个额外的属性,并使用PhotoOrderStrategy/CanvasOrderStrategy 来包含所有不同的逻辑。

    public class OrderItem{
       public IOrderItemStrategy Strategy;
    }
    
    public interface IOrderItemStrategy{
       public void Checkout();
       public Control CheckoutStub{get;}
       public bool PreCheckoutValidate();
    }
    
    public class PhotoOrderStrategy : IOrderItemStrategy{}
    public class CanvasOrderStrategy : IOrderItemStrategy{}
    

    【讨论】:

      【解决方案4】:

      具体为例: 您可以有一些 Evaluator 接受订单并迭代每个订单项。如果逻辑引发事件,而不是处理在其事件参数中携带照片、画布详细信息的事件。

      拥有一组对象“发起者”,这些对象定义:1) 一个可以处理 Evaluator 消息的处理程序,2) 一个简单的布尔值,可以设置为指示他们是否知道如何处理消息中的某些内容,以及 3)可以执行或启动工作流的 Action 或 Process 方法。设计一个接口来抽象这些。

      发布消息。访问每个 Initiator,询问它是否可以处理 lineItem,如果它可以告诉它这样做。处理由“发起者”启动,他们可以调用其他工作流等。

      将上面列出的部分命名为最适合您的域的部分。这应该提供一些灵活性。取决于发起者之间的并发处理要求和工作流依赖性,可能会出现问题。

      一般而言,如果不了解更多细节、项目规模、工作流程、用例等,很难发表评论。

      【讨论】:

        猜你喜欢
        • 2012-11-03
        • 1970-01-01
        • 1970-01-01
        • 2017-10-05
        • 2023-01-08
        • 2012-04-14
        • 2021-12-09
        • 2014-12-25
        相关资源
        最近更新 更多