【问题标题】:Is calling into other code a (SOLID) Single Responsibility Principles (SRP) violation?调用其他代码是否违反(SOLID)单一责任原则(SRP)?
【发布时间】:2013-04-10 12:27:08
【问题描述】:

考虑这个类与业务逻辑:

public static class OrderShipper
{
    public static void ShipOrder(Order order) {
        AuthorizationHelper.AuthorizedUser();

        using (new PerformanceProfiler()) {
            OperationRetryHelper.HandleWithRetries(() => ShipOrderInTransaction(order));
        }
    }

    private static void ShipOrderInTransaction(Order order) {
        using (var transaction = new TransactionHelper()) {
            ShipOrderInternal(order);

            transaction.Commit();
        }            
    }

    private static void ShipOrderInternal(order) {
        // lots of business logic
    }
}

该类包含一些业务逻辑,并执行一些横切关注点。虽然毫无疑问,这个类违反了Open/Closed Principle这个类是否违反了Single Responsibility Principle

我对此表示怀疑,因为该类本身不负责授权用户、分析性能和处理事务。

毫无疑问,这是一个糟糕的设计,因为该类仍然(静态地)依赖于那些横切关注点,但仍然:它是否违反了 SRP。如果有,这是为什么呢?

【问题讨论】:

  • 如果您将责任设置为“发货订单”,我会说“不”

标签: c# oop design-patterns design-principles


【解决方案1】:

这是一个很好的问题,标题有点误导(你不可能在不“调用其他代码”的情况下构建应用程序)。请记住,SOLID 原则更多的是指导而不是必须遵守的绝对规则;如果您将 SRP 得出其合乎逻辑的结论,那么您最终将得到每个类一个方法。最小化横切关注点影响的方法是创建一个尽可能易于使用的外观。在您的示例中,您做得很好-每个横切关注点仅使用一行。

实现此目的的另一种方法是通过AOP,在C# 中可以通过PostSharpIoC interception 实现这一点

【讨论】:

  • 别忘了你也可以使用好的旧装饰器添加横切关注点(如this article 所示)。
  • MikeSW 在他的回答中指出我的代码确实违反了 SRP。你认为什么是争论?
  • 这取决于你如何看待它。我认为这个原则可能太过分了——在我看来,你并没有违反 SRP,因为改变你的班级的根本原因是运送订单的业务逻辑。其他职责被委派给其他地方。我可以在这里建议改进的唯一地方是使用工厂创建您的 ProfilerTransaction 对象 - 然后您只绑定到您的外部依赖项的接口,这很好(假设接口不会改变)
【解决方案2】:

类方法协调一些其他类活动没有任何问题,这并没有破坏 SRP。如果这些类的逻辑是 OrderShipper 的一部分,您将破坏它。

我不确定PerformanceProfiler 做了什么,但它是其中唯一看起来很奇怪的组件。

【讨论】:

  • MikeSW 声称该代码实际上破坏了 SRP。你怎么看他的说法?
【解决方案3】:

让我们通过将类转换为命令来使其更加可见:

// Command pattern
public class ShipOrder
{
    ITransactionFactory _transactionFactory;


    public OrderShipper(ITransactionFactory factory)
    {
        if (factory == null) throw new ArgumentNullException("factory");

        _transactionFactory = factory;
    }

    [PrincipalPermission(Roles = "User")]
    public void Execute(Order order) 
    {
        if (order == null) throw new ArgumentNullException("order");

        using (new PerformanceProfiler()) 
        {
            HandleWithRetries(() => ShipOrderInTransaction(order));
        }
    }

    private void ShipOrderInTransaction(Order order) 
    {
        using (var transaction = _transactionFactory.Create()) 
        {
            ShipOrderInternal(order);

            transaction.Commit();
        }            
    }

    protected void ShipOrderInternal(order) 
    {
        // bussiness logic which is divided into different protected methods.
    }
}

因此您可以使用以下方式调用它:

var cmd = new ShipOrder(transactionFactory);
cmd.Execute(order);

这很可靠。

【讨论】:

  • 你没有回答我的问题。我的代码是否违反 SRP?
  • That's pretty solid 是我的答案。即是的,您的代码是可靠的(它只需要一些爱)。
  • 我不明白 OP 代码是如何可靠的。您的解决方案可能类似于,但对我来说,它与 OP 的方法明显不同
  • 我不知道我的代码是 SOLID。它违反了 OCP,因为每次添加新的横切关注点时都必须更改代码。但如果我理解正确,你认为它不违反 SRP。
  • 你是对的。我的代码很可靠,但 OP 只是 SRP。 OCP/LSP 不适用于静态类。
【解决方案4】:

是的,它确实破坏了 SRP,至少根据类名。

类本身不负责授权用户、分析性能和处理事务。

您是在回答自己,它应该只包含运输订单逻辑。而且它不应该是静态的(为什么是静态的?!)。

@jgauffin 提供的解决方案是一种可能性,尽管我不完全相信 OrderShipper 应该知道交易或者它应该只是交易的一部分。此外,性能分析器 IMO 在此类中也没有位置。但是只有这些信息我不能提出解决方案。虽然分析是一个横切的关注点,最好在这个类之外处理,也许使用一个属性。

顺便说一句,使用消息驱动的方法(如 jgauffin 建议的那样),它应该允许基础架构提供分析和可靠性 (HandleWithRetries) 支持

【讨论】:

  • 你是目前唯一一个声称我的代码实际上违反了 SRP 的人。你能用一些证据来支持这一点吗?也许通过引用定义或其他参考?
  • 大声笑,定义和参考......这个类应该做什么?发货订单?那么这就是它的责任。如果它处理除了运输订单之外的其他问题,那么您有不止一项责任。例如,这个类不应该处理用户授权。关于分析,我没有足够的信息,但我很确定您使用带有静态方法的静态类这一事实有很大的影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多