助手类与功能继承。最佳实践。

7

我有一些实现了ICommand { Execute }接口的命令类。几个命令都有重复的代码段。我有几个选项来实现DRY(Don't Repeat Yourself):

  • 创建静态帮助类并将重复代码移动到那里
  • 创建命令继承,使用受保护的辅助方法

你会选择哪一个?为什么?

添加:感谢所有回复的人,许多答案都很相似且有用!

8个回答

6

除了静态类之外,另一个选项是将公共代码放入一个新类中,并使用依赖注入将助手类注入到命令中。这也符合组合优于继承的概念。


或者另一种方式是:将辅助类作为装饰器,并将命令类注入到辅助类中。 - Ozan
2
在考虑代码重用时,不要想着“继承”,而是想着“聚合”。 - Björn Pollex

4
这完全取决于您所复制代码的性质。
您的辅助函数有哪些输入和输出?它们是否操作一组逻辑相关的变量?如果是,那么最好创建一个基类,并将这些变量作为其成员以及相关的辅助函数。
否则,如果您的辅助函数中的参数不一致,您仍会将这些函数实现为静态函数吧?我不认为在这种情况下继承会使事情更加复杂,我只会用辅助函数来实现(或者,如果您的语言不能将函数视为第一类公民,则使用静态辅助类)。

3
如果有可能其他继承层次外的类也需要重复逻辑,我会把它放在静态帮助类中。否则,放在基类中并使用受保护继承。

3
在我看来,如果重复的代码仅与该类层次结构相关且不会在其外部使用,则应将其放入基类中。如果存在代码可能跨越不同类使用的可能性,则将其移动到通用项目中的帮助程序类中。
祝愉快!

1

在你自己的代码中使用帮助程序是一种源于懒惰的不良实践。

唯一可能需要帮助程序的情况是扩展第三方库(如selenium等)中封闭类的行为。

什么是帮助程序?它是一个静态方法,位于类本身之外的某个地方。

我们会遇到哪些问题呢?

  1. 静态方法。存在单元测试问题。例如,我们的开发人员向帮助程序添加了缓存。结果,无法简单地模拟这些方法,而我不得不添加太多逻辑来进行模拟。是的,静态方法的错误用法是可能的...但最初没有人期望将状态添加到帮助程序中。
  2. 位置。该方法位于另一个类中,远离基本逻辑。这是代码设计的错误概念。

何时使用帮助程序?当您需要扩展第三方库而无法继承它时。否则,只需继承库即可。

何时不使用帮助程序?为了减少重复代码。您需要将此代码移动到另一个服务中。阅读有关DDD的内容以提高重构技能,并了解如何以最佳方式组织服务。


1

这里可能没有一个正确/错误的答案,尽管我想你肯定可以实现得很糟糕。这很大程度上取决于您的实际要求以及您的命令之间的关系。一般来说,我可能会选择基类实现和继承层次结构,假设命令相关且代码直接与命令本身相关,而不是与某个外部实体相关,该实体应该是其自己的类。当然,它们通过它们是命令这一事实相关联,并且基类可以反映出来。

如果您有仅适用于无关命令子集的公共代码,并且创建继承层次结构将强制存在不存在的关系,则添加“helper”类(如果可能,则为非静态,以提高可测试性)将是解决问题的完全自然的方法。您可能会发现,您可以自然地将“helper”方法分组到它们自己的类中。例如,如果几个方法需要与您的身份验证子系统交互,则可以为这些方法设置AuthenticationMediator。我也认为两者都可以做一些。


0
如果逻辑操作涉及到接口成员,而不是实现成员,则建议编写帮助方法[或扩展方法]。
public IRandom
{
    byte NextByte ();
}

public static class IRandomExtensions
{
    // logic that acts against public interface IRandom
    // may be applied to all implementations
    public static int GetNextUnbiasedInteger (this IRandom random) { }
    public static IEnumerable<T> Shuffle<T> (
        this IRandom random, 
        IEnumerable<T> items) { }
}

如果业务逻辑针对实现成员进行操作,

public class SomeCommand : ICommand
{
    // an implementation-specific member, NOT a member
    // of ICommand
    public int SomeControlCount { get; set; }
}

// a method that references implementation-speciic
// details. where should this go?
public void DoSomething ()
{
    SomeCommand command;
    int count = command.SomeControlCount;
}

那么我们应该将其与实现类更紧密地绑定。如果这是一个足够常见的情况,则可能需要一个基类。

个人认为,复杂的继承结构会带来比它们所值得的更多麻烦,请根据可维护性、可读性和可重用性自行判断,你应该没问题!

希望这可以帮到你!:)


0

无状态代码使用辅助程序。

具有多个方法的状态相关代码使用继承。例如,当多个方法使用共享成员变量并因此维护共享状态时。

目标是减少重复代码的数量,这取决于代码的类型。然而,我真的很讨厌人们过度使用这种方法,并制作超级抽象的类或帮助函数,这些类或函数跳转到其他奇怪的抽象代码,例如“Executor”、“Invoker”、“DataConveyer”、“DataManager”、“SharedCode”。最终,它们确实完成了它们应该完成的工作,但它们之间相互关联,以至于您不确定在哪里进行新更改以添加此新功能。我应该在DataConveyer中还是在DataManager中进行呢?

因此,黄金目标应该是保持简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接