จาก Cocoa Style for Objective-C ตอนแรก ได้พูดการตั้งชื่อคลาส เมธอด และตัวแปรไปแล้วนั้น ตอนนี้ก็จะพูดถึงรายละเอียดที่มากขึ้นไปอีกในการตั้งชื่อที่มากไปกว่าครั้งก่อน เพราะด้วยไวยกรณ์และลักษณะของภาษา Objective-C ที่แปลกออกไปจากหลายๆ ภาษาอื่น เช่น C/C++, Java หรือ PHP และด้วยวิถีของ Apple ที่วางเอาไว้ว่าการกำหนดชื่อส่วนประกอบต้องอ่านแล้วเข้าใจได้ง่าย อธิบายได้ด้วยตัวของมันเอง
บล็อกนี้เรียบเรียงเนื้อหามาจาก Cocoa Dev Central : Cocoa Style for Objective-C Part I, Part II
ชื่อเมธอด: ออบเจ็คที่คืนกลับมา
การเข้าถึงข้อมูลภายในออบเจ็คผ่าน accessor, class method (ภาษาทั่วไปคือ static method), หรือเมธอด ออบเจ็คที่คืนกลับมานั้นก็จะขึ้นอยู่กับเงื่อนไขต่างๆ ภายในเมธอดหรือข้อมูลที่ใสเข้าไป ซึ่งจะมีรูปแบบคือ
// Pattern [object/class thing+condition]; [object/class thing+input:input]; [object/class thing+identifier:input];
thing ในที่นี้นั่นก็คือรูปแบบข้อมูลที่จะคืนออกมา ซึ่งค่าที่คืนออกมานั้นผ่านการคิดด้วย เงื่อนไข (condition), ข้อมูลที่ส่งให้ (input, identifier) ที่ระบุถัดมา ยกตัวอย่าง เช่น
// 'thing' = string, and condition is 'DeletingLastPahtComponent' pathToFile = [fullPathOfFile stringByDeletingLastPathComponent]; // 'thing' = string, input = 'Encoding' = NSUnicodeStringEncoding readablePath = [pathInURLEncoded stringByReplacingPercentEscapesUsingEncoding:NSUnicodeStringEncoding]; object = [array objectAtIndex:2]; // class methods newString = [NSString stringWithFormat:@"%@/newfile.tex", pathToFile]; newArray = [NSArray arrayWithObjects:yesterdayMessage, todayMessage, tomorrowMessage, nil];
แต่กฎด้านบนก็ใช่ว่าจะใช้ได้ทั้งหมด เพราะในบางกรณีก็จะมีคำเพิ่มรอบข้างเพื่ออธิบายของผลลัพธ์นั้น โดยจะใช้รูปแบบ
// Pattern [object adjective+thing]; [object adjective+thing+condition]; [object adjective+thing+input:input];
ยกตัวอย่างเช่น
// Usage capitalizedUsername = [username capitalizedString]; newString = [string decomposedStringWithCanonicalMapping]; subarray = [array subarrayWithRange:segment];
หลีกเลี่ยงการใช้คำที่ให้ความหมายไม่ชัดเจน
ข้อผิดพลาดในโปรแกรม (Bug) ที่เกิดขึ้นส่วนหนึ่งก็เพราะการใช้คำพูดที่กำกวนในชื่อคลาสหรือเมธอดที่ตั้ง ตัวอย่างเช่น
// Ambiguous Messages -sortInfo -refreshTimer -update -fetchInfo:
ถ้ายืดตามการตั้งชื่อของ Cocoa แล้วก็จะได้ว่า
- sortInfo จะคืนข้อมูลที่เป็น sort info (อาจมองได้ว่าเป็น getter method) หรือเปล่า หรือคืนการจัดเรียง (sort) บางอย่างที่เรียกว่า “info”
- refreshTimer คืน “Timer” สำหรับ refresh หรือจะ refresh ตัว Timer
- update อัพเดตอะไร ?
- fetchInfo: จะ fetch ตัวแปร Info หรือเป็นเพียงการส่ง info ของการ fetch
ทางแก้
- currentSortInfo ให้ความหมายได้ดีกว่าชื่อเดิม
- refreshDefaultTimer คำว่า refresh ในชื่อใหม่นี่จะอธิบายความได้ชัดกว่าชื่อเดิม นอกจากนั้นยังไม่โดนมองเป็น getter method ด้วย
- updateMenuItem บ่งชี้ไปว่าจะอัพเดตอะไร
- infoForFetch: เมธอดนี้คืน Info สำหรับ fetch ที่ส่งให้
Global C Functions
สำหรับ Global C Function นั้นจะใช้รูปแบบทั่วไปดังนี้
// Global C Functions Pattern Prefix + Value () Prefix + Value + With/From/For + Input () Prefix + Action () Prefix + Action + Type ()
จำเห็นได้ว่าการหลักการตั้งชื่อจะเหมือนกับชื่อคลาสที่ต้องเริ่มต้นด้วย Prefix เพื่อหลีกเลี่ยงชื่อที่ซ้ำซ้อนกันตัวอย่างเช่น
// Global C Functions Example NSHomeDirectory() NSHomeDirectoryForUser() NSClassFromString() NSBeginAlertSheet() NSDrawGrayBezel()
Global Symbols อื่นๆ
นอกจากฟังก์ชั่นด้านบนแล้ว ก็ยังมี Symbol อื่นๆ มากกว่านั้น ตัวอย่างเช่น
- Constants
- Typedef’d structs
- Typedef’d enums
- Individual enum values
- Objective-C Protocols
ซึ่งทั้งหมดนี้ก็มีวิธีการตั้งชื่อเช่นเดียวกันกับคลาสหรือ C functions
// Structs NSPoint point; // struct NSRange range; // struct NSRectArray *rects; // c-style array of structs
สำหรับ Cocoa นั้น enums มักจะใช้ “mode” สำหรับ methods
range = [string rangeOfString:@"find me" options:NSLiteralSearch];
โดยทั้ง constants และ enums นั้นจะมี suffix ที่บ่งชี้ประเภทของตัวเอง เช่น
// Constants & Enums // search modes (enums) NSLiteralSearch NSCaseInsensitiveSearch // exception names (constants) NSMallocException NSInvalidArgumentException // notification names (constants) NSTaskDidTerminateNotification NSWindowWillMoveNotification
สำหรับ notification นั้น จะมีรูปแบบกันตั้งชื่อที่ต่างไปคือ
Class of Affected Object + Did/Will + Action + "Notification"
Dynamic Typing
Objective-C เป็นภาษาที่ยืดหยุ่น (dynamically-typed) กล่าวคือไม่ต้องบอกว่าคอมไพล์เลอร์ว่าตอนนี้เรากำลังใช้ออบเจคนี้ด้วยชนิด (type) อะไรขณะคอมไพล์ การประกาศตัวแปรแบบนี้เป็นเพียงแค่การสัญญาว่าจะมีการเปลี่ยนแปลงชนิดนี้ในส่วนใดส่วนหนึ่งของโค้ดเท่านั้น สำหรับใน Objective-C จะใช้ id เพื่อแทนออบเจคของ Objective-C
// Dynamically-Typed Variables id hostName; id ipAddress; id keyedAccountNames; id theObject; // the compiler is fine with this theObject = [NSString string]; theObject = [NSNumber numberWithInt:1]; theObject = [NSDictionary dictionary];
มี 3 เหตุผลที่ต้องระบุชนิดของตัวแปรทั้งหมด คือ
- เพื่อความชัดเจน เพื่อทำให้เกิดความชัดเจนในสิ่งที่จะทำกับตัวแปรนั้น
- หลีกเลี่ยงคำเตือนต่างๆ ที่ไม่จำเป็นเท่าไหร่ เช่น การเตือนให้ส่งเมสเซจที่ไม่จำเป็นไปยังออบเจคนั้น
- ได้คำเตือนที่เป็นประโยชน์จากคอมไพล์เลอร์ ในกรณีที่ใช้เมสเซจผิด คอมไพล์เลอร์ก็จะเตือนเรา เช่น ใช้ -
stringในNSArray
สำหรับการใช้งาน id สำหรับ dynamic type เหมาะสำหรับการใช้งานประเภท
- delegate หรือ datasource
- notification ของออบเจค
- content ใน container
- ออบเจคที่ทำหน้าที่เกี่ยวกับ target หรือ action
การตั้งชื่อพารามิเตอร์
การตั้งชื่อพารามิเตอร์นั่นมักจะนำหน้าด้วย 'the', 'an' หรือ 'new'
- (void) setTitle: (NSString *) aTitle; - (void) setName: (NSString *) newName; - (id) keyForOption: (CDCOption *) anOption; - (NSArray *) emailsForMailbox: (CDCMailbox *) theMailbox; - (CDCEmail *) emailForRecipients: (NSArray *) theRecipients;
นอกจากนั้นแล้วก็สำหรับการตั้งชื่อตัวแปรใน loop นั้น มักจากนำหน้าตัวแปรที่อยู่ใน loop นั้นด้วย 'one' หรือ 'a/an' เพื่ออ้างถึงตัวแปรในที่อยู่ในลักษณะเดี่ยวๆ เช่น
for ( i = 0; i < count; i++ ) {
oneObject = [allObjects objectAtIndex: i ];
NSLog( @"oneObject: %@", oneObject );
}
NSEnumerator *e = [allObjects objectEnumerator];
id item;
while( item = [e nextObject] )
NSLog( @"item: %@", item );
Odds and Ends
ถ้าต้องส่งเมสเซจที่มีความยาวมากๆ (ทั้งที่ยาวเพราะชื่อ และยาวเพราะจำนวนพารามิเตอร์ที่ต้องใส่) มักจะแยกออกเป็นหลายบรรทัด (Xcode จะเรียงให้โคล่อนตรงกันอัตโนมัติเมื่อขึ้นบรรทัดใหม่)
// NSColor
color = [NSColor colorWithCalibratedHue: 0.10
saturation: 0.82
brightness: 0.89
alpha: 1.00];
สำหรับคลาสที่มีการใช้งานแบบ Singleton ชื่อของเมธอดจำนำหน้าด้วย shared
// Singleton
@implementation SLManagedSemesterContext
+ (id) sharedSemesterContext
{
static SLManagedSemesterContext *shared = nil;
if ( ! shared )
shared = [[self alloc] init];
return shared;
}