Cocoa Style for Objective-C: Part II

จาก 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 เหตุผลที่ต้องระบุชนิดของตัวแปรทั้งหมด คือ

  1. เพื่อความชัดเจน เพื่อทำให้เกิดความชัดเจนในสิ่งที่จะทำกับตัวแปรนั้น
  2. หลีกเลี่ยงคำเตือนต่างๆ ที่ไม่จำเป็นเท่าไหร่ เช่น การเตือนให้ส่งเมสเซจที่ไม่จำเป็นไปยังออบเจคนั้น
  3. ได้คำเตือนที่เป็นประโยชน์จากคอมไพล์เลอร์ ในกรณีที่ใช้เมสเซจผิด คอมไพล์เลอร์ก็จะเตือนเรา เช่น ใช้ -string ใน NSArray

สำหรับการใช้งาน id สำหรับ dynamic type เหมาะสำหรับการใช้งานประเภท

  1. delegate หรือ datasource
  2. notification ของออบเจค
  3. content ใน container
  4. ออบเจคที่ทำหน้าที่เกี่ยวกับ 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;
}

Slide: Introduction to WordPress Theme Development

อาจจะต้องสอนน้องที่ทำงานเรื่องการเขียนธีมให้เวิร์ดเพรส เลยทำเป็นสไลด์ไว้สอนเลยดีกว่า ตอนนี้รีบๆ ขอเป็นแบบ Intro. ไปก่อน โอกาสหน้าค่อยเจาะลึก

Core Data in Action: Why I Did It

จากที่เคยเขียนบล็อกเรื่อง Core Data ทั้ง 2 ตอน (Application Layout, Development Phase) เท่าที่ลองอ่านแล้วตั้งคำถามกับตัวเองแบบคนไม่รู้ก็ได้คำถามที่ว่าทำไมต้องใช้อันนั้น ไม่ใช้ได้ไหม หรือแล้วไอ้นี่มันมีประโยชน์อะไร มีหลายข้อเหมือนกัน สุดท้ายก็เลยคิดว่าคนที่อ่านก็น่าจะคิดและเจอปัญหาแบบเดียวกัน จากเดิมที่เปลี่ยนให้บล็อกมีแค่ 2 ตอนจบ เลยกลับมาเป็นแบบเดิมที่ตั้งใจไว้ว่าน่าจะมีตอนสรุปจบตอนที่ 3 ด้วย ซึ่งตอนนี้ก็อย่างที่ได้เกริ่นไปแล้วว่าคงจะเป็นเหมือน faq ของวิธีการพัฒนาที่ผมเขียนไปแล้วครับ

Continue reading

Google Maps + Geocoder Services: Part II

จากตอนแรก ได้อธิบายถึงการทำงานของโค้ดและได้สร้างรายการการแก้ไขไปแล้วบางส่วน โดยมีรายการของงานที่ต้องทำคือ

  • ประกาศตัวแปร maps, marker และ latlng เป็นตัวแปรแบบ global
  • ลบการประกาศตัวแปร info และการแมพเหตุการณ์ ‘click‘ ในฟังก์ชั่น initWithMapStart และลบฟังก์ชั่น errorOccur
  • แก้ไข callback function ของ navigator.geolocation.getCurrentPosition
  • แก้ไขฟังก์ชั่น panToCurrentLocation
  • เพิ่มฟังก์ชั่น resolveGeocoding, displayGeocodingResult

โดยที่ 3 ข้อแรกนั้นได้แก้ไขไปแล้วเรียบร้อยใน Part I ในตอนนี้จะเป็นตอนสุดท้ายที่ถือได้ว่าเป็นหัวใจของเรื่องนี้ นั่นก็คือการส่งค่าพิกัดให้กับ Google และนำค่าที่ได้มาแสดงผลผ่าน InfoWindow จึงถือเป็นการสิ้นสุดการทำงานที่ได้วางไว้ข้างต้น

เริ่มส่วนที่เหลือกันเลยดีกว่า
Continue reading

Google Maps + Geocoder Services: Part I

จากบล็อกทั้ง 2 ตอนก่อนหน้านี้ (แนะนำการใช้งาน Google Maps V3 และ การใช้ Google Maps ร่วมกับความสามารถของ HTML5) ที่แนะนำการนำเอาแผนที่ของ Google Maps เข้ามาใช้ในเว็บไซต์และการหาตำแหน่งผู้ใช้โดยอาศัยความสามารถใหม่ที่เพิ่มเข้ามาใน HTML5 ซึ่งเว็บไซต์ส่วนใหญ่รองรับความสามารถนี้แล้ว

หลังจากพบตำแหน่งผู้ใช้เรียบร้อยแล้ว นักพัฒนาคงได้แค่เพียงแต่แหน่งพิกัดเท่านั้นซึ่งนั่นค่อนข้างยากหากต้องการพัฒนาแอพพลิเคชั่นที่จะส่งข้อมูลที่เกี่ยวข้องกับผู้ใช้ (หรือคิดว่าน่าจะมีประโยชน์กับผู้ใช้) โดยอาศัยตำแหน่งของผู้ใช้เป็นสำคัญ ดังนั้นที่เราต้องการหลังจากได้พิกัดตำแหน่งของผู้ใช้มาเรียบร้อยแล้ว ก็คงต้องสร้างฐานข้อมูลขนาดใหญ่ที่เก็บพิกัดตำแหน่งต่างๆ แล้วเชื่อมโยงไม่ยังชื่อสถานที่ที่ต้องการ ในกรุงเทพหรือต่างจังหวัดและขยายไปทั้งประเทศ
Continue reading

Cocoa Style for Objective-C: Part I

สำหรับการเริ่มเขียนภาษา Objective-C หรือภาษาคอมพิวเตอร์อื่นๆ ไม่ว่าจะเป็น Java, C หรือ PHP สิ่งสำคัญอย่างหนึ่งนอกจากจะต้องเข้างใจกลไกการทำงานของภาษานั้นๆ แล้ว ก็จำเป็นต้องเข้าใจธรรมชาติของลักษณะการกำหนดชื่อคลาส เมธอด และตัวแปร นั่นนอกจากจะทำให้โค้ดที่เราเขียนขึ้นเองนั้นเป็นที่เข้าใจง่ายแล้ว ก็ยังทำให้เราเข้าใจโค้ดอื่นๆ ที่เราต้องการได้ เช่น เฟรมเวิร์คภายนอก สำหรับภาษา Objective-C เอง ทาง Apple ก็ได้วางแนวทางการสำหรับวิธีการเขียนโค้ดให้กับนักพัฒนาเพื่อให้โค้ดเข้าใจได้ง่ายมากที่สุด ซึ่งนอกจากที่เขียนไว้เป็นลายลักษณะอักษรแล้วก็ยังมีบางส่วนที่ไม่ได้กำหนดไว้ (แต่กลายเป็น de facto) บล็อกต่อไปในี้ก็เลยตั้งใจว่าจะนำเอาเรื่องพวกนี้มาอธิบายไว้ก่อน ก่อนที่เริ่มกับ Objective-C มากกว่านี้ โดยบล็อกจะแบ่งออกเป็น 2 ตอนด้วยกันซึ่งเนื้อหานี้จะเรียบเรียงมาจากบทความของ CocoaDevCentral 2 บทความ ได้แก่ Cocoa Style for Objective-C: Part I และ Cocoa Style for Objective-C: Part II โดยที่จะแบ่งเป็น 2 ส่วนคือ

      Part I: การตั้งชื่อพื้นฐานของ Class, Variable, Accessor หรือ Method
      Part II: รายละเอียดอื่นๆ สำหรับชื่อ Method, Global Symbols, Parameter และอื่นๆ

Continue reading

Core Data in Action: Development Phase

ตอนนี้จะพัฒนาแอพพลิเคชั่นต่อจาก Core Data in Action – Application Layout สำหรับผู้ที่เริ่มต้นอ่านที่นี่ ผมแนะนำและขอร้องให้อ่านตอนแรกก่อนครับเพราะนั่นจะทำให้เข้าใจการทำงานและเหตุผลต่างๆ ในการพัฒนาแอพพลิเคชั่นในตอนนี้ได้ดีมากยิ่งขึ้น ซึ่งตอนนี้จะเป็นการพัฒนาแอพพลเคชั่นเพื่อเรียกใช้งาน Core Data โดยที่บล็อกตอนนี้จะเน้นไปในส่วการอธิบายโค้ดเป็นหลัก ส่วนวิธีการหรืออัลกอริทึมต่างๆ จะขอยกไปอธิบายในตอนถัดไป

เร่ิมต้นด้วยการเลือกที่ไฟล์ FruitsAppDelegate.m แล้วเพิ่มบรรทัดด้านล่างเข้าไปเพื่อเอาไฟล์ FruitsViewController.h เข้าใช้งาน

// FruitsAppDelegate.m
#import "FruitsAppDelegate.h"
#import "FruitsViewController.h"
...

Continue reading