避免因数据库类型而导致的 switch 语句

Avoiding switch statements due to database types

我目前正在处理一个带有数据库的项目,其中我有几个 table 仅包含类型枚举。像这样的东西:

OrderStatus:
ID | Description
1  | Open
2  | Shipped
3  | Canceled
...

当我从数据库中获取类型并想在代码中检查它们时,这通常会导致一个 switch 语句,这有点难以维护,尤其是当新项目添加到 table.

switch(order.OrderStatus.ID) {
    case 1:
        handleOpenOrder();
        break;
    case 2:
        handleShippedOrder();
        break;
    case 3:
        handleCanceledOrder();
        break;
    default:
        break;
}

通常人们会使用继承来解决这个问题,但在这种情况下,我必须在某个时候检查从数据库返回的值的类型。

我目前从数据库中获取作为正常订单状态实体的订单状态。 ORM(NHibernate)有没有办法自动将实体转换为更具体的对象?例如 OrderStatusOpen class?

如果订单处理程序全部包含在一个 class 中并且只在那里使用,您可以简单地执行类似以下的操作并在实现新的处理程序方法时扩展 Dictionary<int, Action>

public class Foo
{
    private readonly _handlerMap = new Dictionary<int, Action>
    {
        { 1, () => handleOpenOrder() },
        { 2, () => handleShippedOrder() },
        { 3, () => handleCanceledOrder() }
    }

    public void TheMethodPreviouslyContainingTheSwitch(Order order)
    {
        var action = _handlerMap[order.OrderStatus.ID];
        action.Invoke();
    }
}

如果您在几个 class 中有那个 switch 语句,我可能会为每个处理程序实现一个 class 并提取一个匹配的接口。如果有必要,我可以详细说明。

编辑:

我想到的基于接口的方法基本上是一样的。这可能仍然不是所有问题的答案;-) 我从头开始写这个(可能无法编译):

public interface IOrderHandler
{
    int StatusId { get; }
    void Handle(Order order);
}

public class OpenOrderHandler : IOrderHandler
{
    public int StatusId => 1;

    public void Handle(Order order)
    {
        // ...
    }
}

public class OrderHandlerFactory
{
    // this factory could be injected to all you dependant classes that need
    // handlers to handle orders

    private readonly _handlerMap = new Dictionary<int, Action>
    {
        // defining the IDs here and in the classes' defintions may be a bit 
        // redudant, but... meh ;)
        { 1, () => new OpenOrderHandler() },
        { 2, () => new ShippedOrderHandler() },
        { 3, () => new CancledOrderHandler() }
    }

    public IOrderHandler CreateHandlerByStatus(int orderStatusId)
    {
        // if the creation of a handler is expensive, it may be useful to
        // only create a single handler that matches the orderStatusId
        var action = _handlerMap[orderStatusId];
        var handler = action.Invoke();
        return handler;
    }

    public ICollection<IOrderHandler> CreateHandlers()
    {
        // it may be fine to create all handlers in each call of this method
        // if the creation is inexpensive
        return _handlerMap.Select(kvp => kvp.Value.Invoke()).ToList();
    }
}

public class Foo
{
    private readonly OrderHandlerFactory _orderHandlerFactory;

    public Foo(OrderHandlerFactory orderHandlerFactory)
    {
        _orderHandlerFactory = orderHandlerFactory;
    }

    public void TheMethodPreviouslyContainingTheSwitch(Order order)
    {
        var handler = _orderHandlerFactory.CreateHandlerByStatus(order.OrderStatus.ID);
        handler.Handle(order);
    }

    public void ThisMayBeUsefulIfMultipleHandlersRelateToAcertainStatus(Order order)
    {
        var handlers = _orderHandlerFactory.CreateHandlers();
        handlers
            .Where(handler => handler.StatusId == order.OrderStatus.ID)
            .All(handler => handler.Handle(order));
    }
}