Language agnostic 助手类与函数继承。最佳做法

Language agnostic 助手类与函数继承。最佳做法,language-agnostic,inheritance,Language Agnostic,Inheritance,我有一些实现ICommand{Execute}接口的命令类。几个命令有重复的代码段。我有几个选择如何干燥: 创建静态助手类并将重复代码移到那里 使用受保护的帮助器方法创建命令继承 你有什么建议?为什么 已添加 谢谢大家的回复,很多答案都很相似,很有用 如果层次结构之外的其他类可能也需要重复逻辑,我会将其放入静态帮助器类中。否则,在具有受保护继承的基类中。在我看来,如果重复代码只与该类层次结构相关,并且不会在其外部使用,我会将其放入基类中。如果代码有可能跨不同的类使用,那么将其移动到公共项目中

我有一些实现ICommand{Execute}接口的命令类。几个命令有重复的代码段。我有几个选择如何干燥:

  • 创建静态助手类并将重复代码移到那里
  • 使用受保护的帮助器方法创建命令继承
你有什么建议?为什么

已添加
谢谢大家的回复,很多答案都很相似,很有用

如果层次结构之外的其他类可能也需要重复逻辑,我会将其放入静态帮助器类中。否则,在具有受保护继承的基类中。

在我看来,如果重复代码只与该类层次结构相关,并且不会在其外部使用,我会将其放入基类中。如果代码有可能跨不同的类使用,那么将其移动到公共项目中的帮助器类


享受吧

替代静态类的另一个选项是将公共代码放入新类中,并使用依赖项注入将helper类注入到命令中。这也适用于组合而非继承的概念。

这里可能没有正确/错误的答案,尽管我认为您肯定会很差地实现它。这很可能取决于您的实际需求以及命令之间的关联程度。通常,我可能会使用基类实现和继承层次结构,假设命令是相关的,代码直接与命令本身相关,而不是与某个外部实体相关,该实体本身应该是一个类。当然,它们是相关的,因为它们是命令,基类可以反映这一点

但是,如果您有只对不相关命令子集通用的代码,并且创建继承层次结构将强制建立一个不存在的关系,那么添加一个“helper”类(非静态,如果可能的话,以提高可测试性)将是解决此问题的一种非常自然的方法。您可能会发现,您可以将“helper”方法自然地分组到它们自己的类中。例如,如果有几个方法需要与您的身份验证子系统交互,您可能会为这些方法提供一个AuthenticationMediator。我也看不出两者之间有任何冲突。

如果逻辑在接口成员上运行,而不是在实现成员上运行,那么建议编写一个helper方法[或扩展方法]

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) { }
}
然后,我们可能应该更紧密地将其绑定到实现类。如果这是很常见的情况,那么基类可能是有意义的

就个人而言,复杂的层次结构比它们的价值更麻烦,请根据您自己对可维护性、易读性和重用的判断,您应该可以


希望这有帮助!:)

这完全取决于重复代码的性质

助手函数的输入/输出是什么?它们是否在一组逻辑相关的变量上运行?然后-是的,您最好创建一个基类,将这些变量作为成员,并创建一组相关的辅助函数


否则,如果helper函数中的参数不一致,那么无论如何都会将这些函数实现为静态函数,对吗?在这种情况下,我看不出有什么理由使继承复杂化,我只会使用helper函数(或者,如果您的语言不将函数视为第一类公民,请使用静态helper类)。

无状态代码使用helper

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

目标是减少重复代码的数量,这取决于它是什么类型的代码。然而,我真的很讨厌人们做得过火,制造出超级抽象类或帮助函数,这些类或函数会跳转到其他扼杀抽象代码,这些代码被命名为“Executor”、“Invoker”、“datatransferer”、“DataManager”和“SharedCode”。在所有这些跳跃的最后,它实际上完成了它应该完成的工作,但彼此之间的联系如此紧密,以至于你不知道在哪里对添加的新功能进行新的更改。我应该在DataTransfer中执行还是在DataManager中执行


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

在自己的代码中使用助手是一种糟糕的做法,它源于懒惰

您真正需要帮助的唯一可能的情况是从第三方库(如selenium等)扩展密封类的行为

什么是助手?它是一个静态方法,位于类本身的某个位置

我们遇到了什么问题

  • 静态方法。在单元测试方面有问题。例如,我们的开发人员向助手添加了缓存。因此,不可能简单地模拟这些方法,而我必须添加许多逻辑来进行简单的模拟。是的,有可能错误地使用静态方法……但在开始时,没有人希望向助手添加状态
  • 地点。方法位于另一个类中,远离基本逻辑。这是一个错误的代码设计概念
  • 何时使用助手?当您需要扩展第三方库而无法继承它时。否则只需继承lib


    什么时候不使用助手?减少重复代码。您需要将此代码移动到其他服务。阅读DDD以提高重构技能,并了解如何以最佳方式组织服务

    或相反的方式:使helper类成为decorator并将command类注入helper类。+1在考虑代码重用时,不要考虑“继承”,而要考虑“聚合”
    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;
    }