【问题标题】:ReSharper and StyleCop vs. the step-down rule (Clean Code)ReSharper 和 StyleCop 与降级规则(清洁代码)
【发布时间】:2014-02-19 13:14:54
【问题描述】:

我想这可能是一个有点分裂的帖子,但这是我一段时间以来一直在努力表达的东西,我想把它放到更广泛的开发社区。​​p>

我的工作是在提交文件签入之前运行 ReSharper 自动格式化工具,该工具通过访问修饰符对区域中的事物进行分组,并按字母顺序对其中的成员进行排序。它基本上遵循了这篇文章中描述的类布局模式:Alphabetizing methods in Visual Studio,人们似乎对此非常热衷。

我很乐意使用任何编码标准,但我很难将这种方法与编写干净的代码相协调,主要是因为我严格遵守降级规则(Robert C Martin - Clean Code) ,而字母顺序打破了这一点。

降压规则描述如下:

我们希望代码读起来像自上而下的叙述。我们希望每个函数都跟在下一个抽象级别的函数之后,这样我们就可以阅读程序,在阅读函数列表时一次降低一个抽象级别。我称之为降级规则。函数的理想参数数量为零。接下来是一个。然后两个。应尽可能避免三个论点。

按照这种方法,我可能会编写以下(人为的)代码:

public class Processor
{
    public Processor(ProcessData data)
    {
        Configure(data);
    }


    public void Configure(ProcessData data)
    {
        ClearState();
        UnpackData();
        ProcessData();
        TransformData();
        PostProcessData();
    }

    private void ClearState() { /*Snip*/ }

    private void UnpackData() { /*Snip*/ }

    private void ProcessData() { /*Snip*/ }

    private void TransformData() { /*Snip*/ }

    private void PostProcessData() { /*Snip*/ }


    public IEnumerable<GroupedRecordSet> AggregateRecords(IEnumerable<Record> records)
    {
        var groups = CalculateGrouping(records);
        PopulateGroups(groups, records);
        return groups;
    }

    private IEnumerable<GroupedRecordSet> CalculateGrouping(IEnumerable<Record> records) { /*snip*/ }

    private void PopulateGroups(IEnumerable<GroupedRecordSet> groups, IEnumerable<Record> records) { /*snip*/ }
}

然后,当我运行自动格式化时,我最终会看到以下内容(已删除评论):

public class Processor
{
    #region Constructors and Destructors

    public Processor(ProcessData data)
    {
        Configure(data);
    }

    #endregion

    #region Public Methods and Operators

    public IEnumerable<GroupedRecordSet> AggregateRecords(IEnumerable<Record> records)
    {
        var groups = this.CalculateGrouping(records);
        this.PopulateGroups(groups, records);
        return groups;
    }

    public void Configure(ProcessData data)
    {
        this.ClearState();
        this.UnpackData();
        this.ProcessData();
        this.TransformData();
        this.PostProcessData();
    }

    #endregion

    #region Methods

    private IEnumerable<GroupedRecordSet> CalculateGrouping(IEnumerable<Record> records) { /*snip*/ }

    private void ClearState() { /*snip*/ }

    private void PopulateGroups(IEnumerable<GroupedRecordSet> groups, IEnumerable<Record> records) { /*snip*/ }

    private void PostProcessData() { /*snip*/ }

    private void ProcessData() { /*snip*/ }

    private void TransformData() { /*snip*/ }

    private void UnpackData() { /*snip*/ }

    #endregion
}

现在,我发现第二个样式一目了然更难理解,而且我发现自己在不寻常的范围内保持第一个样式的可读性,在第二个范围内。其中包括:

  • 所有者方法名称前缀 - 即 ConfigureClearState、ConfigureUnpackData、AggregateRecordsCalculateGroupings、AggregateRecordsPopulateGroups 等。这会导致成员名称过长,尤其是在“拥有”方法需要其他自己的“拥有”方法时。
  • De-factoring - 将代码从我最初重构的小方法中移回原来的方法中。这会导致方法很长。
  • 部分类 - 我实际上还没有达到这一点,但我完全有可能最终将相关方法放入部分类中,以使它们与代码主体分开。这使解决方案资源管理器充满了成堆的代码文件。

显然,我对这些方法中的任何一种都不满意,但据我所知,它们是在操作参数范围内保持易读性的唯一真正选择。

显然,第二种方法是微软的房子风格,所以我想我的问题是:

  • 第二种方法是微软家风格对吗?
  • 如果是这样 - Microsoft 如何在第二种样式中保持干净可读的代码?
  • 其他人是否遇到过这种差异,人们使用了哪些方法来实现高可读性?
  • 编写简洁代码的一般风格偏好是什么?

我找到了一份微软编码风格的副本:http://blogs.msdn.com/b/brada/archive/2005/01/26/361363.aspx

【问题讨论】:

    标签: c# coding-style resharper code-cleanup


    【解决方案1】:

    Robert Martin 方法以可读性为目标。为了充分享受这些好处,您必须应用额外的约定或规则(例如,命名、放弃 cmets 以选择有意义和描述性的名称、单一职责、短方法……)。然后您可以像阅读普通文本文档一样阅读您的代码。如果缩进下一个抽象级别的功能块也会增强可读性。这样您就可以通过代码格式表达抽象级别:

    public void Level1Member()
    {
        RunLevel2Member();
        RunAnotherLevel2Member();
    }
    
        private void RunLevel2Member()
        {
            RunLevel3Member();
        }
    
            private void RunLevel3Member()
            {
                //....
            }
    
        private void RunAnotherLevel2Member()
        {
            //..
        }
    

    您可能会发现自己在使用字母样式只是为了上下滚动以获取上下文时滥用鼠标滚轮。另一方面,在重构代码时,您不需要维护任何缩进和抽象级别。

    这是具有不同目标的两种不同方法。一个喜欢增强可读性(对于人类)并让您通过程序流程找到方法,而另一个喜欢您通过名称快速找到方法。字母排序支持将所有公共方法放在一起的通用约定,这也增加了找出类的目的和行为的机会(一目了然)。 Step-down 很高兴地按照不同的目标混合了公共和私有方法。

    你不能两者兼得。所以在任何情况下都不会违反数百条规则并忽略重构只是为了混合这两种风格。毫无意义,并且使代码的可读性大大降低。

    如果您认为阅读降压风格比阅读字典风格的代码感觉更舒适自然,那么您应该使用它。我愿意。

    从未听说过 Microsoft 内部约定,例如按字母顺序对代码进行排序。官方 .NET 约定不针对代码结构的组织或约定(例如,建议将事件委托放置在类的顶部等)。

    降级规则只是一种非官方的风格。一些自动格式化工具不支持缩进方法并将它们放回一层。

    顺便说一句,使用部分类来掩盖糟糕的设计(太大的类/太多的责任)对于关心干净和可维护代码的人来说不是一个选择。

    为了让你自己更轻松,我会尝试向你的团队展示你的风格的优势,或者接受你团队中大多数人喜欢的风格。

    不要忘记,您的 IDE 通过突出显示代码中的方法或提供“转到实现”或“查找用法”等功能或显示方法调用顺序的代码映射来很好地帮助您。还有一些关于代码可读性的更重要的规则,比如好的名称和好的设计。

    是的,但我个人更喜欢降级。

    【讨论】:

    • 我同意,而且我还发现按字母排序非常多余,因为我们在 IDE 中已经有很多地方可以按字母顺序访问成员并直接跳转到它们。
    • 另外 - 不确定我对缩进代码以使步骤物理化的感觉如何,我一直倾向于用双白线分隔方法语句的逻辑组。
    • 是的,这取决于口味。我认为在滚动页面时,确定缩进的变化比确定行距(1 行)的差异更容易,只是为了知道你在代码中的位置。众所周知,线条之间的更多空间阅读起来不舒服(眼球运动)。缩进使抽象级别更加可见。这个想法是你只需要阅读第一级(非缩进),也许是第二级方法来理解你的代码的基本功能。每个进一步的缩进或抽象级别都揭示了该类的基本功能如何完成的更多细节。
    • 降压规则在要求或建议在使用之前声明 ba 函数的语言中是不可能的。即javascript。
    • @Ray,就像在 C/C++ 中一样,你有声明和定义。在您实际使用定义之前,编译器需要了解它们。声明没有正文。它在该声明函数的定义中。在这种情况下,您只需将所有声明保存在一个位置(例如头文件)并将定义移动到源文件。您可以对这两个文件进行排序和排列以满足您的偏好。你命名为 JavaScript。 JavaScript 只知道“声明”,这就像 C/C++ 中的定义。 JavaScript(如 Java、C#)允许您以任何顺序将“定义”放在任何您喜欢的位置。
    猜你喜欢
    • 2017-04-15
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 2020-05-30
    • 2012-05-21
    相关资源
    最近更新 更多