【问题标题】:naming standards/conventions and what should a 'Service' class should do命名标准/约定以及“服务”类应该做什么
【发布时间】:2012-10-13 19:24:04
【问题描述】:

你会如何命名一个只执行很小一部分功能的类,例如获取定期付款资料并将其标记为“失败”。

将其命名为RecurringPaymentProfileMarkAsFailedService 是否正确?我知道这只是一个名字,但我想遵守标准/约定。这是“服务”类应该做的,还是不同的设计模式?我正在尝试遵循 SRP 原则,因此,如果我是正确的,最终会有很多小班,每个班专门从事一项任务。我想定义一个正确的命名标准。

【问题讨论】:

    标签: design-patterns naming-conventions


    【解决方案1】:

    虽然我从未见过它被严格记录在案,但我通常使用“服务”一词来表示处理某些相关最终用户功能子集的类。

    例如,假设我有一个银行账户系统,我可能有以下服务:

    public class AccountService
    {
      public boolean transfer(Account src, Account dest, double amt) // ....
    
      public Account create(User user, double initAmt) // ...
    
      public void close(Account account) // ...
    }
    

    所以用户可以做的“高级”功能都封装在我的服务中。服务将访问数据库,实例化任何助手,进行计算等它需要的。但就像我说的,这正是我在这个行业中观察到的。

    就 SRP 而言:SRP 并不意味着您需要为每个要执行的功能创建一个全新的类。您不需要一个类将配置文件标记为失败,另一个将其标记为成功,另一个来更改配置文件的名称......

    SRP 意味着每个类处理“一件事”,但“一件事”可以是一组密切相关的功能。另一个需要考虑的重要 OO 设计原则是封装。也就是说,一个类应该尽量减少它暴露给客户的东西——只暴露其他人需要的东西。如果您有大量的小类来管理 PaymentProfile 的内部状态,那么您将暴露 PaymentProfile 的所有细节。我给你举个小例子:

    假设您的 PaymentProfile 对象有一个布尔值 isSuccess。然后你的类 RecurringPaymentProfileMarkAsFailedService 做这样的事情:

     public class RecurringPaymentProfileMarkAsFailedService {
       public void mark(PaymentProfile profile)
       {
         profile.setSuccess(false);
       }
     }
    

    酷 - 一切正常。现在我们收到一个功能请求,不仅 PaymentProfile 可以具有“成功”和“失败”状态,而且现在我们需要支持“内部错误”,这意味着某些内部系统在更新期间崩溃了。我们不能再使用布尔值,因此我们将布尔值 isSuccess 更改为可以是以下之一的枚举:SUCCESS、FAILURE、ERROR。现在我们必须重构我们的 MarkAsFailedService。

    这是一个微不足道的重构 b/c 它只是一个类,但是对于一个复杂的类树,这些重构可以在整个系统中回显。这表明我透露了太多关于我的 PaymentProfile 的实现细节。一个类应该被封装,因此对其实现的更改对其客户端几乎没有影响。

    【讨论】:

    • 模拟和测试驱动开发怎么样?我的主要问题是具有几乎每个功能的类是可以更容易地模拟外部依赖项。例如,MarkAsFailure() 方法可能需要发送关于它的通知。如果通知实现为MarkAsFailureNotificationSender 类,则可以通过构造函数注入将其注入到具有MarkAsFailure() 的类中,并验证模拟上的调用。对此有何想法/想法?
    【解决方案2】:

    我相信我理解 SRP 的“精神”,但我个人认为,因此得出结论认为您必须为可能只是 RecurringPaymentProfileService 上的一个方法 (MarkAsFailed) 创建一个单独的类(假设您将此逻辑描述为“功能的一小部分”)。

    我认为,当您经常为这样长而笨拙的名称而苦苦挣扎时,这表明您在代码结构方面的竞争原则之间还没有完全达成正确的平衡。

    我的指导原则是,服务应尝试遵守 SRP,因为它与处理(返回/执行)特定业务实体或流程(例如付款资料。但我的倾向是,在试图将服务的内部拆分到第 n 级时,您可能最终会违反一大堆其他原则。

    【讨论】:

    • 个人这就是我最初的想法,但我一直在尝试它,加上通过构造函数注入的依赖注入使代码更具单元测试性。我认为将类拆分成更小的类是值得的额外“麻烦”,我只是对命名模式有点困惑,并希望遵守“标准”,而不是自己设定。
    【解决方案3】:

    嗯,不管怎样,从你的描述中听起来你正在使用命令设计模式。

    通常当我发现类名太长时,我会使用包或路径层次结构。在这种情况下,它可能类似于:

    包:commands.recurringPaymentProfile

    类:MarkAsFailed

    所以:我使用“命令”而不是“服务”,然后将适用于 recurringPaymentProfile 的那些组合到一个包中。

    【讨论】:

    • 我不知道这是否与Command 模式完全匹配。我正在使用命令和命令处理程序,然后使用 SimpleInjector(IoC 容器)“注入”装饰器来添加事务和并发检查,我发现这是添加此类重复但必需的代码的一种非常好的方法。对于命令,我目前将它们限制为仅数据库代码,并且我确实有另一个 MarkAsFailedCommand 只是将配置文件标记为失败并保存在数据库中。此“服务”具有其他逻辑,例如调用 MarkAsFailed 命令和发送通知。
    • 好的,听起来令人兴奋:) 尽管如此,我还是建议尝试使用包/层次结构来管理名称长度蠕变。为我工作。我真的认为在一般使用中没有任何标准来处理这个问题。
    猜你喜欢
    • 1970-01-01
    • 2014-12-26
    • 2011-08-27
    • 1970-01-01
    • 2018-06-12
    • 2016-04-13
    • 2014-10-26
    • 2014-02-10
    • 2016-09-21
    相关资源
    最近更新 更多