Objective-C Programming Chapter 10 (Part 1)

Category ,Protocol , Delegate

 

ในการพัฒนาโปรแกรมเราเริ่มจากการออกแบบคลาสและลงมือเขียนโค้ด จากนั้นก็เพิ่มเติมเมธอดให้กับคลาส ขยายความสามารถออกไปเรื่อยๆ ดังเช่นคลาส Product ในบทที่ผ่านมา ในช่วงแรกอาจจะมีแค่เมธอด setter/getter หลังจากนั้นเราก็ขยายความสามารถด้วยการเพิ่มเมธอดต่างๆเข้าไป เช่น เมธอดแสดงราคาที่รวมภาษีมูลค่าเพิ่มเข้าไปแล้ว เป็นต้น ในกรณีแบบนี้ การแก้ไขโค้ดเพิ่มเติมไม่ใช่ปัญหาใหญ่สำหรับเรา เพราะว่าเราเป็นคนเขียนคลาส Product ขึ้นมาเอง
สมมติว่าเราอยากจะเขียนโค้ดเพิ่มเติมให้กับคลาส NSMutableString เช่นมีเมธอดในการสลับตัวอักษรแบบกลับด้าน จะเห็นว่ากรณีแบบนี้เกิดปัญหาขึ้นมาทันที เนื่องจากเราเข้าไปแก้ไขโค้ดของ NSMutableString ไม่ได้ เพราะว่าสิ่งที่ Framework ให้เรามามีแค่ interface (header) และ source code ที่ผ่านการคอมไพล์กลายเป็นไฟล์ไบนารี่แล้วเท่านั้น ดังนั้นหากจะแก้ไขคลาส NSMutableString เราก็เพียงแค่ขอ source code จาก Apple เท่าน้ันเอง แต่วิธีนี้คงเป็นไปไม่ได้แน่นอน วิธีการแก้ปัญหาที่พอจะเป็นไปได้ก็คือสร้างคลาสใหม่โดยการ subclass จาก NSMutableString แล้วเขียนเมธอดที่ต้องการเข้าไปใหม่ก็แก้ปัญหาได้เรียบร้อยแล้ว แต่วิธีการ subclass อาจจะไม่ใช่คำตอบที่ดีที่สุด เพราะสิ่งที่เกิดขึ้นตามมาก็คือคลาสมีขนาดใหญ่ขึ้นเรื่อยๆ เพื่อให้เห็นภาพชัดเจนขอยกตัวอย่างง่ายๆเช่นเป็นต้นว่าคลาส Product ที่เราเคยได้เขียนไป เราสร้างโปรเจคที่สองขึ้นมาใหม่และเขียนเมธอดเกี่ยวกับการจัดการราคาสินค้าเพิ่มเข้าไปทั้งสิ้น 20 เมธอด หลังจากนั้นคลาสนี้ถูกใช้ต่อในโปรเจคที่ 3,4,5 แต่ทั้งสามโปรเจคนี้ไม่ได้ใช้เมธอดที่เพิ่มเติมเข้ามาเลย แต่กลับต้องมีโค้ดทั้ง 20 เมธอดเพิ่มเข้า เพียงเพราะโปรเจคที่สองจำเป็นต้องใช้ ไม่เพียงแค่โปรแกรมมีขนาดใหญ่ขึ้น แต่คลาส Product เองก็มีความซับซ้อนเพิ่มมากขึ้น

Category

นับว่าเป็นข้อได้เปรียบของภาษา Objective-C ที่มีวิธีการเพิ่มความสามารถให้กับคลาสโดยไม่ต้อง sub class ด้วยวิธีการที่เรียกว่าแคทิกอรี่ (Category) วิธีนี้เป็นหนทางที่ช่วยให้เราแก้ไขและเพิ่มเติมเมธอดของคลาสโดยไม่ต้องไปยุ่งเกี่ยวกับโค้ดต้นฉบับเลย การประกาศแคทิกอรี่มีรูปแบบการประกาศดังนี้

ch10_cat_for

เราจะสร้างโปรเจคขึ้นมาใหม่เพื่อทำความเข้าใจกับแคทิกอรี่โดยโปรเจคนี้จะยังใช้คลาส Product จากโปรแกรม 8.7 เมื่อสร้างโปรเจคใหม่และเพิ่มคลาส Product เข้ามาเรียบร้อยแล้ว ต่อไปเราจะสร้าง category โดยการเลือกที่เมนู  New > File หรือจะกดเมาส์ขวาแล้วเลือก New File ก็ได้เช่นกัน จากนั้น XCode จะแสงหน้าต่างให้เลือก template สำหรับการสร้างไฟล์ใหม่ให้เลือก Objective-C category ดังรูป

ch10_cat

เมื่อเลือกเสร็จแล้วจะเจอหน้าต่างถามชื่อของ cateory ให้ตั้งชื่อตามต้องการ (โค้ดตัวอย่างของโปรเจคนี้ตั้งชื่อว่า MyExtend) และส่วน category on ให้เลือกคลาส Product

ch10_my

เมื่อผ่านขั้นตอนนี้จะได้ไฟล์เพิ่มขึ้นมาอีก 2 ไฟล์คือ Product+MyExtend.h และ Product+MyExtend.m ดังรูป

ch10

เมื่อเปิดดูไฟล์ Product+MyExtend.h ที่ XCode สร้างให้ก็จะเจอโค้ดลักษณะดังนี้

โค้ดมีลักษณะคล้ายกับการประกาศคลาสทั่วๆไป ส่ิงที่แตกต่างก็คือโค้ดส่วนที่บอกว่า sub class มาจากคลาสอะไรได้หายไป และมีโค้ดใหม่เข้ามาแทนคือ (MyExtend) ซึ่งหมายถึงเราได้ประกาศ category ให้กับคลาส Product โดยมีชื่อ MyExtend นั่นเอง เราจะเพิ่มเติมเมธอดใหม่เข้าไปอีก 3 เมธอดดังนี้

Product+MyExtend.h

เมธอดที่เราได้ประกาศเพิ่มเติมคือแสดง ราคาสินค้า ชื่อสินค้า และ ราคาหลังจากรวมภาษีมูลค่าเพิ่ม 7% จากนั้นเราก็จะเขียนโค้ดในส่วนของ implementation ซึ่งมีโค้ดดังนี้

Product+MyExtend.m

โค้ด implementation ของแคทิกอรี่จะมีส่วนที่เพิ่มเติมเข้ามาพิเศษกว่าการเขียนคลาสปกติก็คือ มี (MyExtend) ต่อท้ายชื่อของคลาส เพื่อบอกให้รู้ว่าโค้ดในส่วนนี้เป็นแคทิกอรี่ของ MyExtend เมื่อทุกอย่างครบแล้วต่อไปก็เขียนโปรแกรมขึ้นมาทดสอบ

Program 10.1

main.m

Program 10.1 Output
 
iMac
price:40000.00

price(VAT):42800.00

เมื่อโปรแกรมผ่านการคอมไพล์ โค้ดของ category จะไม่ได้เพิ่มให้กับคลาสตอน compile-time แต่จะเพิ่มตอน run-time ดังนั้นเราจึงไม่จำเป็นต้องคอมไพล์โค้ดของคลาสที่เราได้เพิ่มแคทิกอรี่เข้าไป พูดอีกอย่างคือเราไม่จำเป็นต้องมี source code ต้นฉบับ จากโปรแกรมที่ 10.1 จะเห็นว่าเราสามารถเขียนเมธอดเพิ่มเติมให้กับคลาส Product ได้โดยไม่ได้แก้ไขโค้ดเดิมเลย ข้อดีอีกอย่างของ category คือเมธอดที่เราเขียนเพิ่มสามารถใช้งานตัวแปรต่างๆภายในคลาสได้ แต่ไม่สามารถเพิ่มได้

Objective-C Category ไม่สามารถที่จะเพิ่ม member variable ให้กับคลาสได้ ถ้าต้องการจะเพิ่มตัวแปรให้ใช้ sub class แทน

การใช้ category มีข้อควรระวังคือ ถ้าเราประกาศเมธอดซ้ำกับเมธอดเดิมที่คลาสมีอยู่แล้ว จะเป็นการเขียนทับเมธอดเดิม เช่นสมมติว่าเราเพิ่ม setName:andPrice ซึ่งซ้ำกับเมธอดของคลาส

และโค้ดส่วน category implement ได้แก้ไขให้ราคาเป็น 2 เท่า

เมื่อโปรแกรมทำงานก็จะได้ผลลัพธ์ดังนี้
iMac
price:80000.00

price(VAT):85600.00

จากผลลัพธ์ของโปรแกรมได้แสดงให้เห็นว่าคลาสได้ใช้เมธอดที่เราเขียนใหม่แทนเมธอดเดิม

การ Overring เมธอดของ sub class สามารถเรียกเมธอดเดิมผ่านทาง super ได้ แต่การเขียนทับของแคทิกอรี่นั้นจะไม่ใช้เรียกใช้เมธอดเดิมได้เลย นอกจากนี้ซับคลาสทั้งหมดจะได้รับแคทิกกอรี่ที่เพิ่มเข้าไปด้วย ฉะนั้นแล้วหากเขียน category ให้กับคลาสที่เป็น Framework เช่น NSObject ต้องระวังให้มาก เพราะจะส่งผลกระทบต่อคลาสอื่นๆ

อย่างไรก็ตามการเขียนทับเมธอดเดิมไม่ใช่ข้อเสียเสมอไป เพราะเราสามารถใช้ให้เป็นประโยชน์ได้ เช่น กรณีที่เราต้องการจะแก้ bug ของโปรแกรมก็สามารถที่จะใช้ประโยชน์โดยการเขียนทับได้ ข้อควรระวังอีกอย่างในการใช้เขียนโปรแกรมคือในกรณีที่มี 2 category และใช้ชื่อเดียวกัน โปรแกรมจะไม่สามารถกำหนด category ที่จะใช้งานได้

เราจะลองเขียนโปรแกรมอีกสักโปรแกรมเพื่อทำความเข้าใจกับ category ให้มากขึ้น โดยครั้งนี้เราจะเพิ่ม category ให้กับ NSString เพื่อใช้หาว่าสตริงนั้นเป็น palindrome หรือไม่ ( palindrom คือสตริงอ่านจากซ้ายหรือขวาก็จะเหมือนกัน เช่น ABBA , LEVEL , OHO เป็นต้น )

Program 10.2
NSString+MyExtend.h

NSString+MyExtend.m

หลักการทำงานของเมธอด isPalindrome คือจะเปรียบเทียบตัวอักษรสองตัว ตัวแรกคือตัวอักษรที่อยู่ตำแหน่งด้านหน้าของสตริง และตัวที่สองคือตำแหน่งที่อยู่ด้านท้าย หากมีค่าเท่ากันก็จะขยับตำแหน่งทั้งสอง ให้เข้าใกล้ตรงกลางของสตริง ทำเช่นนี้ไปเรื่อยๆ จนกว่าตำแหน่งทั้งเท่ากับตรงกลางของสตริง
ch10_pa

main.m

Program 10.2 Output
 
AABAA is palindrome

 

จะเห็นได้ว่าไม่ได้มี source code ของ NSString เลย แต่เราสามารถเขียนเมธอดเพื่อใช้ในตรวจสอบ palindrome เพิ่มให้กับคลาส NSString ได้

อ่าน Part 2 

Leave a Reply