最佳做法:在类定义中对公共/受保护/私有的排序?


92

我正在从头开始一个新项目,希望它是干净的/具有良好的编码标准。这里经验丰富的开发人员喜欢按什么顺序将内容布置在一个类中?

A:1)公共方法2)私有方法3)公共变量4)私有变量

B:1)公共变量2)私有变量3)公共方法4)私有方法

C:1)公共变量2)公共方法3)私有方法4)私有变量

我通常喜欢将公共静态变量放在顶部,但是然后将公共静态方法列在构造函数之前,还是应该始终将构造函数列在最前面?诸如此类的事情...

我知道这很严格,但我只是想知道:什么是最佳做法?

PS:不,我不使用Cc#。我知道。我很傻


9
不使用C#没有错。作为专业开发人员,这些年来我从未写过C#的故事。使用适合任务的任何语言,并告诉任何说不同意见的人可以坚持下去!
以太

Answers:


141

罗伯特·C·马丁(Robert C. Martin)在Clean Code中建议编码人员始终将成员变量放在类的顶部(先是常量,然后是私有成员),并应按这种方式对方法进行排序,以使它们读起来像没有故事的故事。读者需要跳过太多代码。与使用访问修饰符相比,这是组织代码的更明智的方法。


10
我也很幸运,最后补充说:getters / setters。对我来说,这有助于减轻班级的负担。
院长J,

5
构造函数位于顶部,紧随成员变量之后。在OOP中,执行从对象实例化开始。
2014年

6
导致读者过多地跳动代码可能需要权衡迫使读者阅读私有方法的所有细节。报纸上的隐喻可能被误解了,因为您的公共方法应该大致代表您的班级所做的事情,而您的私有方法应提供详细信息(几乎就像您可以在需要时引用的脚注一样)。
洪敬轩

1
我很困惑。您说:(首先是常量,然后是私有成员)。好。公众成员去哪儿了?
亲爱的

1
@Honey他们会紧跟常量和私有成员之后。因此,顺序如下:常量,私有成员,公共成员。
皮埃尔·吉列

48

最佳做法是保持一致

就个人而言,我更喜欢把public第一个方法,其次是protected方法,通过以下private的方法。会员数据通常应始终是私有或受保护的,除非您有充分理由不这样做。

我将public方法放在顶部的理由是它定义了接口为您的类,因此任何仔细阅读头文件的人都应该能够立即看到此信息。

在一般情况下,privateprotected成员对大多数人看的头文件,除非他们正在考虑修改类的内部不那么重要了。保持它们“畅通无阻”可确保仅在需要了解的基础上维护此信息,这是封装更重要的方面之一。


LeopardSkikPBH,我完全同意...这很有道理!我想我对var还是funcs优先感到困惑。谢谢!
临时名称2009年

11
我不同意最佳做法是一致的。有很多方法可以一致地编写不可读,无法维护的代码。
杰森

3
@Jason的意思是说,最好不要呆在路边,因为在那里仍然可能发生事故。
Rex M

1
@Jason-也许我应该更清楚了。在这种特殊的,相当主观的情况下(方法的顺序),我认为最佳实践是一致的。每个人都会对订购东西的最佳方式有意见,但是如果您天生就始终如一,那么它应该可以维护。我同意“保持一致”并不总是所有代码领域的最佳实践,尤其是当您考虑到经常必须处理的糟糕代码质量时。
LeopardSkinPillBoxHat

4
@Rex M:不,我所说的完全不符合您的解释。我的观点是,在这种情况下,仅保持一致并不是一个强有力的论据。在某些情况下,一致性很好(例如,放置大括号)。但是,这里的选择实际上会影响代码的可读性。因此,需要一个比一致性更强的论点。
杰森

8

我认为我与大多数人有不同的看法。我更喜欢将相关项目分组在一起。我不能忍受不得不跳来上课。代码应该流动,并根据可访问性(公共,私有,受保护等)使用实例化的顺序,或者实例与静态,成员与属性,函数之间的排序无济于事。因此,如果我Method没有使用私有助手方法实现的公共方法HelperMethodAHelperMethodB等等。然后,而不是必须远离该文件在对方这些方法,我将让他们彼此接近。同样,如果我有一个由静态方法实现的实例方法,我也将它们组合在一起。

所以我的课程通常是这样的:

class MyClass {
    public string Method(int a) {
        return HelperMethodA(a) + HelperMethodB(this.SomeStringMember);
    }

    string HelperMethodA(int a) { // returns some string }

    string HelperMethodB(string s) { // returns some string }

    public bool Equals(MyClass other) { return MyClass.Equals(this, other); }

    public static bool Equals(MyClass left, MyClass right) { // return some bool }

    public double SomeCalculation(double x, double y) {
        if(x < 0) throw new ArgumentOutOfRangeException("x");
        return DoSomeCalculation(x, y); 
    }

    const double aConstant;
    const double anotherConstant;
    double DoSomeCalculation(double x, double y) {
        return Math.Pow(aConstant, x) * Math.Sin(y) 
            + this.SomeDoubleMember * anotherConstant;
    }       
}

8

就我个人而言,我喜欢将公共放在首位,先进行保护,然后再进行私有化。这样做的原因是,当有人破解标题时,他/她首先看到他/她可以访问的内容,然后在向下滚动时看到更多详细信息。

一个人不必为了使用它而查看类的实现细节,那么该类的设计就做得不好。


3

我曾经很在乎。在过去的几年中,使用现代IDE几乎只有1或2次按键操作,我已经大大放松了我的标准。现在,我从静态变量,成员变量开始,然后从构造函数开始,对此我不必太担心。

在C#中,我让Resharper自动组织事物。


我同意。我浏览文件中成员的正常模式是使用内置于我正在使用的IDE或编辑器中的工具。成员的实际分组成为次要的。但是,我同意应该对成员进行分组以避免纯粹的随机排序,并且我使用resharper自动进行分组和排序。
菲利普·恩根

2

这是我的订单

  1. 静态变量
  2. 静态方法
  3. 公共变量
  4. 保护变量
  5. 私有变量
  6. 建设者
  7. 公开方法
  8. 受保护的方法
  9. 私人方法

我使用以下规则:

  • 静态先于
  • 在构造函数之前在方法之前的变量(我认为构造函数在方法的类别中)
  • 在受保护之前在私有之前是公共的

这个想法是在行为(方法)之前定义对象(数据)。静电需要分开,因为它们并不是对象的真正组成部分,也不是行为。


谢谢barkmadley ...这很有趣!您将在构造函数之前放置4和5。我一定会考虑的
临时名称2009年

像这样的顺序,尽管在顶部附近有静态方法很有趣。我与一个将私有变量放在底部的开发人员一起工作,我能看到这个主意,但感觉不对
Carlton

2

我通常同意公共的,受保护的私人命令以及静态数据,成员数据,成员函数的顺序。

尽管有时我会将类似的成员(getter和setter)分组,但我通常更喜欢按字母顺序列出组中的成员,以便可以更轻松地定位它们。

我也喜欢垂直排列数据/功能。我将制表符/空格移到足够右边,以便所有名称在同一列中对齐。


1
嘿-我内心深处的“制表符”!:-)我不是强迫性的。老实说我不是!
临时名称2009年

1

对于每个人来说,正如Elzo所说,现代IDE使得在下拉菜单等中带有彩色图标,可以轻松轻松地找到成员及其修饰符。

我的观点是,对于程序员而言,了解该类的目的是什么以及如何预期其行为更为重要。

因此,如果它是一个Singleton,则将语义(静态getInstance()类)放在首位。

如果这是一个具体工厂,我首先要放置getNew()函数和register / initialize函数。

... 等等。首先,我的意思是紧随c'tors和d'tor之后-因为它们是实例化任何类的默认方式。

随后的功能如下:

  1. 逻辑调用顺序(例如initialize(),preProcess(),process(),postProcess())或
  2. 相关功能(例如访问器,实用程序,操纵器等),

取决于该类主要是要成为具有某些功能的数据存储区,还是要具有少量数据成员的功能提供者。



0

我更容易理解public后面加上protected和private的顺序,最好在头文件顶部的注释中简单地描述类逻辑,并在函数调用顺序中描述类逻辑和内部使用的算法。

我使用Qt c ++已有一段时间,看到了一些新的关键字,例如signalslot我更喜欢保持上面的顺序,并在这里与您分享我的想法。

#ifndef TEMPLATE_H
#define TEMPLATE_H


class ClassName
{
    Q_OBJECT
    Q_PROPERTY(qreal startValue READ startValue WRITE setStartValue)
    Q_ENUMS(MyEnum)

public:

    enum MyEnum {
        Hello = 0x0,
        World = 0x1
    };

    // constructors

    explicit ClassName(QObject *parent = Q_NULLPTR);
    ~ClassName();

    // getter and setters of member variables

    // public functions (normal & virtual) -> orderby logic

public slots:

signals:

protected:

    // protected functions it's rule followed like public functions


private slots:

private:

    // methods

    // members

};

#endif // TEMPLATE_H
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.