关于命名约定:选择好的标识符名称

Picking good identifier names

好的,所以你可以阅读标识符命名的指导原则,直到你的脸是蓝色的…驼色的,帕斯卡的,让他们描述…但它们不能帮助您为给定的应用程序域选择最佳名称。

最容易选择的(imho)是一个或两个单词名词组:

  • 入口型
  • 员工
  • 宽度收集

但并不是每门课都能很好地融入到名词中,所以我看到很多人通过在动词的结尾加上-er把动词变成名词:

  • 会计经理
  • 记录计数器
  • 工艺转炉

我看到的最大的问题是,很多时候它们是模棱两可的…尤其是经理。它到底在管理什么?

所以我的问题是,你如何为一门课取一个好名字?我所说的"好"是指信息丰富、不含糊。

我知道,我知道。在几乎每一个现代的IDE中都内置了重构支持,您可以在不眨眼的情况下更改名称,那么有什么意义呢?一个选择不当的名字会迷惑和误导任何偶然发现它的人,直到它被重新命名,所以它仍然是一个有效的问题。

相关的

What’s the best approach to naming classes?


我一直在读罗伯特·C·马丁的《干净的代码》,从第17章开始我还没有读到这一部分,但我认为这是最接近回答问题的部分。

N1: Choose Descriptive Names

Don’t be too quick to choose a name.
Make sure the name is descriptive.
Remember that meanings tend to drift
as software evolves, so frequently
reevaluate the appropriateness of the
names you choose. This is not just a
"feel-good" recommendation. Names in
software are 90 percent of what make
software readable. You need to take
the time to choose them wisely and
keep them relevant. Names are too
important to treat carelessly.
Consider the code below. What does it
do? If I show you the code with
well-chosen names, it will make
perfect sense to you, but like this
it’s just a hodge-podge of symbols and
magic numbers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public int x() {
    int q = 0;
    int z = 0;
    for (int kk = 0; kk < 10; kk++) {
        if (l[z] == 10)
        {
            q += 10 + (l[z + 1] + l[z + 2]);
            z += 1;
        }
        else if (l[z] + l[z + 1] == 10)
        {
            q += 10 + l[z + 2];
            z += 2;
        } else {
            q += l[z] + l[z + 1];
            z += 2;
        }
    }
    return q;
}

Here is the code the way it should be
written. This snippet is actually less
complete than the one above. Yet you
can infer immediately what it is
trying to do, and you could very
likely write the missing functions
based on that inferred meaning. The
magic numbers are no longer magic, and
the structure of the algorithm is
compellingly descriptive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int score() {
    int score = 0;
    int frame = 0;
    for (int frameNumber = 0; frameNumber < 10; frameNumber++) {
        if (isStrike(frame)) {
            score += 10 + nextTwoBallsForStrike(frame);
            frame += 1;
        } else if (isSpare(frame)) {
            score += 10 + nextBallForSpare(frame);
            frame += 2;
        } else {
            score += twoBallsInFrame(frame);
            frame += 2;
        }
    }
    return score;
}

The power of carefully chosen names is
that they overload the structure of
the code with description. That
overloading sets the readers’
expectations about what the other
functions in the module do. You can
infer the implementation of
isStrike() by looking at the code above. When you read the isStrike
method, it will be"pretty much what
you expected."

1
2
3
private boolean isStrike(int frame) {
    return rolls[frame] == 10;
}

N2: Choose Names at the Appropriate Level of Abstraction

Don’t pick names that communicate
implementation; choose names the
reflect the level of abstraction of
the class or function you are working
in. This is hard to do. Again, people
are just too good at mixing levels of
abstractions. Each time you make a
pass over your code, you will likely
find some variable that is named at
too low a level. You should take the
opportunity to change those names when
you find them. Making code readable
requires a dedication to continuous
improvement. Consider the Modem
interface below:

1
2
3
4
5
6
7
public interface Modem {
    boolean dial(String phoneNumber);
    boolean disconnect();
    boolean send(char c);
    char recv();
    String getConnectedPhoneNumber();
}

At first this looks fine. The
functions all seem appropriate.
Indeed, for many applications they
are. But now consider an application
in which some modems aren’t connected
by dialing. Rather they are connected
permanently by hard wiring them
together (think of the cable modems
that provide Internet access to most
homes nowadays). Perhaps some are
connected by sending a port number to
a switch over a USB connection.
Clearly the notion of phone numbers is
at the wrong level of abstraction. A
better naming strategy for this
scenario might be:

1
2
3
4
5
6
7
public interface Modem {
    boolean connect(String connectionLocator);
    boolean disconnect();
    boolean send(char c);
    char recv();
    String getConnectedLocator();
}

Now the names don’t make any
commitments about phone numbers. They
can still be used for phone numbers,
or they could be used for any other
kind of connection strategy.

N3: Use Standard Nomenclature Where Possible

Names are easier to understand if they
are based on existing convention or
usage. For example, if you are using
the DECORATOR pattern, you should use
the word Decorator in the names of the
decorating classes. For example,
AutoHangupModemDecorator might be the
name of a class that decorates a Modem
with the ability to automatically hang
up at the end of a session. Patterns
are just one kind of standard. In
Java, for example, functions that
convert objects to string
representations are often named
toString. It is better to follow
conventions like these than to invent
your own. Teams will often invent
their own standard system of names for
a particular project. Eric Evans
refers to this as a ubiquitous
language for the project. Your code
should use the terms from this
language extensively. In short, the
more you can use names that are
overloaded with special meanings that
are relevant to your project, the
easier it will be for readers to know
what your code is talking about.

N4: Unambiguous Names

Choose names that make the workings of
a function or variable unambiguous.
Consider this example from FitNesse:

1
2
3
4
5
6
7
8
9
private String doRename() throws Exception
{
    if(refactorReferences)
        renameReferences();
    renamePage();
    pathToRename.removeNameFromEnd();
    pathToRename.addNameToEnd(newName);
    return PathParser.render(pathToRename);
}

The name of this function does not say
what the function does except in broad
and vague terms. This is emphasized by
the fact that there is a function
named renamePage inside the function
named doRename! What do the names tell
you about the difference between the
two functions? Nothing. A better name
for that function is
renamePageAndOptionallyAllReferences.
This may seem long, and it is, but
it’s only called from one place in the
module, so it’s explanatory value
outweighs the length.

N5: Use Long Names for Long Scopes

The length of a name should be related
to the length of the scope. You can
use very short variable names for tiny
scopes, but for big scopes you should
use longer names. Variable names like
i and j are just fine if their scope
is five lines long. Consider this
snippet from the old standard"Bowling
Game":

1
2
3
4
5
private void rollMany(int n, int pins)
{
    for (int i=0; i<n; i++)
        g.roll(pins);
}

This is perfectly clear and would be
obfuscated if the variable i were
replaced with something annoying like
rollCount. On the other hand,
variables and functions with short
names lose their meaning over long
distances. So the longer the scope of
the name, the longer and more precise
the name should be.

N6: Avoid Encodings

Names should not be encoded
with type or scope information.
Prefixes such as m_ or f are useless
in today’s environments. Also project
and/or subsystem encodings such as
vis_ (for visual imaging system) are
distracting and redundant. Again,
today’s environments provide all that
information without having to mangle
the names. Keep your names free of
Hungarian pollution.

N7: Names Should Describe Side-Effects

Names should
describe everything that a function,
variable, or class is or does. Don’t
hide side effects with a name. Don’t
use a simple verb to describe a
function that does more than just that
simple action. For example, consider
this code from TestNG:

1
2
3
4
5
6
public ObjectOutputStream getOos() throws IOException {
    if (m_oos == null) {
        m_oos = new ObjectOutputStream(m_socket.getOutputStream());
    }
    return m_oos;
}

This function does a bit more than get
an"oos"; it creates the"oos" if it
hasn’t been created already. Thus, a
better name might be
createOrReturnOos.


1)不要缩写。只有在缩写词是行业标准的情况下才使用,如HTML。

2)在你的名字中保持自我一致。

3)使用您的应用程序所在域中的术语。当你想出一个名字时,假装你在添加人们不熟悉的新名字时要花费有限的子弹。

你可能会觉得你的课程中有一些微妙的特征将它与"foo"区分开来,并且因为这个细微的差别,你会觉得自己倾向于为它制定一些新的概念。如果80%的使用它的人不在乎细微的差别,那么就坚持使用已知的术语。

4)在大多数OO语言中,自动完成很重要。如果可能的话,第一个词应该是人们在浏览名称空间时最可能输入的词。其他人觉得用英语读得好更重要,所以这是有争议的。


我讨厌看到这样的课程:

XXX-HANDXXXME管理器XXX控制器

处理程序和管理器很难描述类是什么。如果我们找不到一个好的班级名称,我们应该确保我们的班级所做的事情是明确的,并且不试图做太多的事情。较小的类通常更容易命名类,因为它们的作用域很窄。

不过,有时我们会用尽创造性的东西来称呼事物,然后又回到原来的样子。


选择简洁、信息丰富、适合事物的名称是编程中最困难的部分之一。

没有计算公式。这是你从经验中学到的技能。

注意强调简洁。Java中的长名称是该语言的主要转折点之一。它基本上已经达到了在没有自动完成的IDE的情况下在Java中编程是不可能的。

缩写词在命名时可以是你的朋友。例如,"mgr"而不是"manager"。我真的需要每天打一千次"经理"字吗,当"经理"明白了这一点?只是要确保人们始终如一地、明智地使用它们。


当我发现很困难的meaningful挑选到的名字,它alerts我这类太大或)真的不明白"域名"无论我coding。 P / < >

当你终于来了,好的设计addressing的财务上的大叔把鲍勃在干净的代码regarding concepts像abstraction适当的情况下,单一的责任principle和whatnot,那么"名称已经成为easier到挑选。 P / < >

采摘困难的名字几乎是永远的标志,逻辑的组织仍然需要工作。毕竟,compiler并不在乎如何好或你的poorly逻辑也组织。组织也有只帮助到manage complexity和sanity你fellpw developers。 P / < >


像kekoav一样,我谨慎地命名一些foomanager或foocontroller。它不仅表示一个遭受身份危机的对象,而且还引入了歧义。我曾经在一个项目中工作过,其中有一个"accountmanager",它是一个域对象,代表一个管理帐户的人(雇员的子类型)。当然,有人做了一个"accountmanagermanager",还有人困惑了,创建了一个account domain对象(这不是我们的应用程序要处理的对象),然后将这些内容混合到accountmanager中来管理帐户。有点乱。

是不是保管人?你是行政长官吗?

是的,我的拉丁语已经生锈了。


干净的代码可以很好的书,但不要遵循"advice在标识符的名称和评论。(看到自describing代码和长identifiers让代码unreadable) P / < >

例如,这里是鲍勃叔叔的保龄球的例子(见其他replies为鲍勃叔叔的code)做properly: P / < >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Returns total score for a game of ten-pin bowling. On entry the number of fallen pins
// for each ball is provided in fallen[] and the number of balls bowled is in balls_bowled.
public int score()
{
  int points = 0;                                            // Cumulative count of points for the game
  int ball = 0;                                               // Current shot or"ball" (1 or 2 per frame)
  int frame;                                                  // Current frame of the game


  for (frame = 0; frame < max_frames; ++frame)
  {
    if (fallen[ball] == 10)
    {
        // Strike (all pins knocked over from 1 ball) gives bonus of next two balls
        points += 10 + fallen[ball + 1] + fallen[ball + 2];
        ++ball;                                                 // Move to next frame (no 2nd ball)
    }
    else if (fallen[ball] + fallen[ball + 1] == 10)
    {
        // Spare (all pins knocked over in 2 balls) gives bonus of following ball
        assert(fallen[ball + 1] > 0);
        points += 10 + fallen[ball + 2];
        ball += 2;                                             // Move to first ball of next frame
    }
    else
    {
        // Open frame just gives score of all fallen pins of both frame's balls
        assert(fallen[ball] + fallen[ball + 1] < 10);
        points += fallen[ball] + fallen[ball + 1];
        ball += 2;                                             // Move to first shot of next frame
    }
  }
  assert(frame == max_frames && ball == balls_bowled);
  assert(points <= max_frames*30);                // Maximum possible score
  return points;
}

一个好的方法是使用设计模式,您将完成所有的工作:)


有一个你坚持的标准,就不会有任何混乱。如果您的页面名为accountmanager,那么按照我的标准,它将意味着"您管理帐户的页面"。

对类名、事件等执行类似操作。


如果您在.NET环境中,可以使用fxcop作为指导原则。至于命名一个类,我通常使用名词/目的。你说得对,经理班做什么?如果它是一个控制器类,就称它为ManagerController。