第三章 接口与API设计—第19条:使用清晰而协调的命名方式

2017-04-16  本文已影响0人  CoderCurtis

类、方法及变量的命名是Objective-C编程的重要环节。新手通常会觉得这门语言很繁琐,因为其语法结构使得代码读起来和句子一样。名称中一般都带有"in"、"for"、"with"等介词,其他编程语言则很少使用这些它们认为多余的字眼。以下面这段代码为例:

NSString *text = @"The quick brown fox jumped over the lazy dog";
NSString *newText = [text stringByReplacingOccurrencesOfString:@"fox" withString:@"cat"];

此代码用了比较啰嗦的方式来描述一个看上去如此简单的表达式。对于执行替换操作的那个方法,其名字居然有48个字符长。不过这样做的好处是,代码读起来像日常语言里的句子:
"将文本中出现的"fox"字符串替换为"cat",并返回替换后的新字符串。"(Take text and give me a new string by replacing the occurrences of the string 'fox' with the string 'cat')
这个句子准确描述了开发者想做的事。在命名不像Objective-C这般复杂的语言中,类似的程序可能会写成这样:

string text = "The quick brown fox jumped over the lazy dog";
string newText = text.replace("fox", "cat");

这样写有个问题,就是text.replace()的两个参数到底按何种顺序解读。是"fox"为"cat"所替换,还是"cat"为"fox"所替换呢?还有个疑问:replace函数是把所有出现的字符串都替换掉呢,还是只替换掉第一次找见的那个?其名称没能清除地表达出这两个意思。而Objective-C的命名方式虽然长一点,但是却非常清晰。
读者也会注意到,方法与变量名使用了"驼峰式大小写命名法"(camel casing)--以小写字母开头,其后每个单词首字母大写。类名也用驼峰命名法,不过其首字母要大写,而且前面通常还有两三个前缀字母(参见第15条)。在编写Objective-C代码时,大家一般都使用这种命名方式。如果你愿意,也可使用自己的风格来命名,不过按照驼峰命名法写出来的代码更容易为其他Objective-C开发者所接受。

方法命名
你要是写过C++或Java代码的话,应该会习惯那种较为简省的函数名,在那种命名方式下,若想知道每个参数的用途,就得查看函数原型。这会令代码难于读懂: 为了明白函数用法,你必须经常回过头来参照其原型。比方说,要写一个表示矩形的类。用C++代码可以这样定义此类:

class Rectangle {
public:
    Rectangle(float width, float height);
    float getWidth();
    float getHeight();
private:
    float width;
    float height;
};

不熟悉C++也没关系,你只要知道这个类包含名为width及height的两个实例变量就好。若想创建该类的实例,只有一种办法,就是以矩形尺寸为参数,调用其"构造器"(constructor),宽度与高度都有对应的存取方法。可以用下面这行代码来创建该类的实例:

Rectangle *aRectangle = new Rectangle(5.0f, 10.0f);

回顾这行代码时,并不能一下子看出5.0f和10.0f表示什么。你可能觉得这两个参数是矩形尺寸,可是到底宽度在先还是高度在先呢?要想确定这一点,还得去查函数定义才行。
Objective-C语言就不会有这个问题了,因为其方法名可以起得更长一些。熟悉C++的人可能会像下面这样把Rectangle改写为等价的Objective-C代码:

#import <Foundation/Foundation.h>

@interface EOCRectangle : NSObject

@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;

- (id)initWithSize:(float)width :(float)height;

@end

写这个类的人显然知道,在Objective-C语言中,和C++构造器等效的东西是init-系列方法,于是将其命名为"initWithSize:"。这看上去很奇怪,你也许觉得语法有误,第二个冒号前面怎么没有字呢?实际上语法完全没错,之所以会觉得写错了,是因为它和改写前的C++构造器有着想同的问题: 使用此类的开发者还是不清楚每个变量的含义:

EOCRectangle *aRectangle = 
    [[EOCRectangle alloc] initWithSize:5.0f :10.0f];

下面这种命名方式就要好很多:

- (id)initWithWidth:(float)width andHeight:(float)height;

这么写是很长,然而这次绝对不会混淆每个变量的含义了:

EOCRectangle *aRectangle = 
    [[EOCRectangle alloc] initWithWidth:5.0f andHeight:10.0f];

虽说使用长名字可令代码更为易读,但是Objective-C新手还是难于习惯这种详尽的方法命名风格。不要吝于使用长方法名。把方法名起的稍微长一点,可以保证其能准确传达出方法所执行的任务。然而方法名也不能长得太过分了,应尽量言简意赅。
以EOCRectangle类为例。好的方法名应该像这样:

- (EOCRectangle*)unionRectangle:(EOCRectangle*)rectangle
- (float)area

而下面这种命名方式咋不好:

- (EOCRectangle*)union:(EOCRectangle*)rectangle // Unclear
- (float)calculateTheArea // Too verbose

清晰的方法名从左至右读起来好似一段文章。并不是说非得按照那些命名规则来给方法起名,不过这样做可以令代码变得更好维护,而且也能使其他人更易读懂。
NSString这类就展示了一套良好的命名习惯。下面列出几个方法及其命名缘由:

[@"Effective Objective-C" hasPrefix:@"Effective"] == YES

要是把方法名直接写成"prefix",读起来就不这么顺了。反之,若将其叫成"isPrefixWith:"则听上去冗长而别扭。

给方法命名时的注意事项可总结成下面几条规则。

类与协议的命名
应该为类与协议的名称加上前缀,以避免命名空间冲突(参见第15条),而且应该像给方法起名时那样把词句组织好,使其从左至右读起来较为通顺。例如,在NSArray的子类中,有一个用于表示可变数组的类,叫做NSMutableArray,mutable这个词放在array前面,用以表明这是一种特殊的array(数组)。
下面以iOS的UI库UIKit为例,演示类与协议的命名惯例:

说了这么多,其中最重要的一点就是,命名方式应该协调一致。而且,如果要从其他框架中继承子类,那么务必遵循其命名惯例。比方说,要从UIView类中继承自定义的类,那么类名末尾的词必须是View。同理,若要创建自定义的委托协议,则其名称中应该包含委托发起方的名称,后面再跟上Delegate一词。如果能坚持这种命名习惯,那么在稍后回顾自己的代码或他人使用你所写的代码时,很容易就能理解其含义。

要点

上一篇 下一篇

猜你喜欢

热点阅读