ข้อดีของภาษา C# เมื่อเทียบกับภาษาอื่น ๆ ตอนที่ 14

ข้อดีของภาษา C# เมื่อเทียบกับภาษาอื่น ๆ ตอนที่ 14

การสร้างสมาชิกของอินเตอร์เฟสสองตัวแบบเจาะจง
ในตอนที่แล้วผู้เขียนพูดถึงเรื่องการเจาะจงอินเตอร์เฟส ซึ่งมีไว้เพื่อแก้ปัญหาในกรณีที่คลาสหนึ่งคลาสสืบคุณสมบัติจากอินเตอร์เฟสสองตัว และอินเตอร์เฟสสองตัวนั้นมีเมธอดชื่อเดียวกันและมีซิกเนเจอร์ตรงกัน จะมีผลให้อินเตอร์เฟสทั้งสองเรียกไปยังเมธอดเดียวกัน ในตอนนี้หัวข้อนี้จะพูดถึงการสร้างสมาชิกของอินเตอร์เฟสสองตัวแบบเจาะจงบ้าง
คนโค้ดสามารถใช้การสร้างอินเตอร์เฟสแบบเจาะจงเพื่อสร้างอินเตอร์เฟสสองตัวที่มีสมาชิกซึ่งซ้ำกันแต่มีโค้ดภายในต่างกันได้ โค้ดตัวอย่างในรูปที่ 1, 2 และ 3 แสดงมิติของกล่องในมาตราเมตริกและมาตราอังกฤษ โดยมีอินเตอร์เฟส IEnglishDimensions (บรรทัดที่ 10-14) ทำหน้าที่กำหนดมิติของกล่องในมาตราอังกฤษ และมีอินเตอร์เฟส IMetricDimensions (บรรทัดที่ 17-21) ทำหน้าที่กำหนดมิติของกล่องในมาตราเมตริก

รูปที่ 2 คือนิยามคลาส Box ซึ่งนำอินเตอร์เฟสสองตัวที่มีสมาชิกซึ่งซ้ำกันแต่มีโค้ดภายในต่างกัน อินเตอร์เฟส IEnglishDimensions ทำหน้าที่กำหนดมิติของกล่องในมาตราอังกฤษ และอินเตอร์เฟส IMetricDimensions ทำหน้าที่กำหนดมิติของกล่องในมาตราเมตริกไปใช้ สมาชิกที่ทั้งสองอินเตอร์เฟสมีซ้ำกันคือเมธอด Length และ Width
เมื่อนำอินเตอร์เฟสไปสืบคุณสมบัติ คลาสลูกต้องกำหนดโค้ดส่วนไส้ให้แก่สมาชิกแบบเมธอดของอินเตอร์เฟส เรียกว่าการทำ “อิมพลีเมนท์” (implement) ในกรณีนี้เราต้องอิมพลีเมนท์เมธอด Length และ Width ของอินเตอร์เฟส IEnglishDimensions ซึ่งทำได้อย่างที่เห็นในบรรทัดที่ 37, 39 เริ่มจากกำหนดชนิดข้อมูลให้ตรงกับสมาชิกเมธอดของอินเตอร์เฟส ซึ่งในกรณีนี้คือ float ตามด้วยชื่อของอินเตอร์เฟส ซึ่งในกรณีนี้คือ IEnglishDimensions ตามด้วยการใช้ตัวกระทำแลมบ์ดาแล้วตามด้วยนิพจน์ที่ต้องการให้เป็นค่าส่งกลับ ซึ่งในกรณีเมธอด Length ให้เป็นค่าจากตัวแปร lengthInches และเมธอด Width ให้เป็นค่าจากตัวแปร widthInches
การอิมพลีเมนท์สมาชิกเมธอด Length และ Width ของอินเตอร์เฟส IEnglishDimensions ก็ทำด้วยวิธีคล้ายกันอย่างที่เห็นในบรรทัดที่ 42, 44 เริ่มจากกำหนดชนิดข้อมูลให้ตรงกับสมาชิกเมธด ของอินเตอร์เฟส ซึ่งในกรณีนี้คือ float ตามด้วยชื่อของอินเตอร์เฟส ซึ่งในกรณีนี้คือ IEnglishDimensions ตามด้วยการใช้ตัวกระทำแลมบ์ดาแล้วตามด้วยนิพจน์ที่ต้องการให้เป็นค่าส่งกลับ ซึ่งในกรณีเมธอด Length ให้เป็นค่าจากตัวแปร lengthInches * 2.54f และเมธอด Width ให้เป็นค่าจากตัวแปร widthInches * 2.54f
นี่เป็นการเขียนไส้ของเมธอดอย่างย่นย่อโดยใช้นิพจน์แลมบ์ดา แม้ว่าโค้ดในตัวอย่างนี้จะกำหนดไส้ให้แก่สมาชิกเมธอด Length และ Width โดยการใช้แลมบ์ดา แต่ก็ไม่จำเป็นต้องทำเช่นนั้น เราสามารถกำหนดค่าด้วยการเขียนนิยามเมธอดเต็มรูปแบบตามปรกติได้

รูปที่ 3 คือโค้ดของเมธอด Main ที่แสดงวิธีใช้งานคลาส Box โดยขั้นแรกประกาศตัวแปรสร้างออพเจ็กต์จากคลาส Box (บรรทัดที่ 49) จากนั้นประกาศตัวแปรสร้างออพเจ็กต์ eDimensions ที่มีไทป์เป็นอินเตอร์เฟส IEnglishDimensions (บรรทัดที่ 52) และประกาศตัวแปรสร้างออพเจ็กต์ eDimensions ที่มีไทป์เป็นอินเตอร์เฟส IEnglishDimensions (บรรทัดที่ 55) โปรดสังเกตว่าทั้ง ออพเจ็กต์ eDimensions และออพเจ็กต์ eDimensions แม้จะมีไทป์ต่างกันแต่ก็อ้างไปยังออพเจ็กต์ box1 ด้วยกันทั้งคู่ การทำงานของโค้ดจึงมีภาวะเป็นโพลิมอร์ฟิสซึม
บรรทัด 58,59 แสดงค่ามิติของกล่องในมาตราอังกฤษ ส่วนบรรทัด 62,63 แสดงค่ามิติของกล่องในมาตราเมตริก ผลลัพธ์การทำงานของโปรแกรมจะเป็นอย่างที่เห็นในบรรทัดที่ 67-70

ในกรณีที่ต้องการกำหนดให้ใช้มาตราอังกฤษเป็นค่าโดยปริยายสามารทำได้โดยใส่โค้ดเมธอด Length และ Width ของอินเตอร์เฟส IEnglishDimensions โดยการกำหนดแบบธรรมดา อย่างที่เห็นในบรรทัด 33,34 ของรูปที่ 4 ส่วนเมธอด Length และ Width ของอินเตอร์เฟส IMetricDimensions ให้คงการกำหนดโค้ดส่วนไส้ในแบบเดิม

การทำเช่นนี้จะมีผลให้ท่านสามารถเข้าถึงหน่วยอังกฤษได้โดยการอ้างถึงออพเจ็กต์ box1 และเข้าถึงหน่วยในมาตราเมตริกได้โดยอ้างถึงออพเจ็กต์ mDimensions ตามเดิม

การใช้อินเตอร์เฟสกับสองคลาส
เมื่อนิยามอินเตอร์เฟสแล้ว เราจะนำมันไปใช้กับหนึ่ง หรือสองคลาสหรือมากกว่าเท่าใดก็ได้ตามใจชอบ รูปที่ 6 คือตัวอย่างโค้ดแสดงการนำอินเตอร์เฟสหนึ่งอันไปใช้กับคลาสสองคลาส บรรทัดที่ 5-10 คือนิยามอินเตอร์เฟส IVehicle บรรทัด 12 และ 35 คือนิยามคลาส Bicycle และ Bike อันเป็นคลาสที่จะนำอินเตอร์เฟส IVehicle ไปใช้ และบรรทัด 58 คือคลาส test ที่จะนำคลาส Bicycle และ Bike ไปสร้างเป็นออพเจ็กต์เพื่อใช้งาน
แนวคิดที่อยู่เบื้องหลังการนิยามอินเตอร์เฟส IVehicle คือเราวางแผนที่จะสร้างนิยามคลาสซึ่งจำลองหรือห่อหุ้มการทำงานของยานภาหนะแบบใดก็ได้ที่มีความสามารถสามประการคือ สามารถเปลี่ยนเกียร์ได้ เร่งความเร็วได้ และห้ามล้อได้ ซึ่งในตัวอย่างนี้คือจักรยาน (คลาส Bicycle) และจักรยานยนต์ (คลาส Bike) เมื่อคลาส Bicycle และ Bike สืบคุณสมบัติจากอินเตอร์เฟส IVehicle เราจะแน่ใจได้ว่าออพเจ็กต์ที่เกิดจากสองคลาสนี้จะมีความสามารถสามประการดังกล่าว และโค้ดที่เรียกใช้ออพเจ็กต์จะสามารถเรียกใช้ความสามารถสามประการนั้นได้ในลักษณะเดียวกัน โดยไม่ต้องสนใจว่าออพเจ็กต์นั้นเกิดจากคลาส Bicycle หรือเกิดจากคลาส Bike
ความสามารถในการเปลี่ยนเกียร์ได้จะถูกกำหนดโดยเมธอด void ChangeGear(int a) ความสามารถในการเร่งความเร็วได้จะถูกกำหนดโดยเมธอด void SpeedUp(int a) ความสามารถในการห้ามล้อได้ ได้จะถูกกำหนดโดยเมธอด void ApplyBrakes(int a) นิยามของอินเตอร์เฟส IVehicle มีแต่ซิกเนเจอร์ของสามเมธอดนี้ คลาสที่นำอินเตอร์เฟส IVehicle ไปใช้สืบคุณสมบัติ จำเป็นจะต้องใส่โค้ดไส้เอาเอง เพราะวิธีทำงานของออพเจ็กต์คนละตัวกันจะต่างกัน ยกตัวอย่างเช่นออพเจ็กต์ที่เป็นจักรยาน ย่อมจะมีวิธีเปลี่ยนเกียร์ต่างไปจากออเจ็กต์ที่เป็นจักรยานยนต์ แต่โค้ดที่เรียกออพเจ็กต์ใช้งาน จะมีโค้ดแบบเดียวกันไม่ว่าจะทำงานกับจักรยานหรือจักรยานยนต์ คือเรียกเมธอด ChangeGear ส่งพารามิเตอร์เป็นตัวเลขจำนวนเต็มให้หนึ่งตัว และไม่ต้องรับค่าส่งกลับ


รูปที่ 7 และรูปที่ 8 คือนิยามคลาส Bicycle และคลาส Bike ตามลำดับ ทั้งสองคลาสนี้คือคลาสเพื่อจำลองหรือห่อหุ้มการทำงานของจักรยานและจักรยานยนต์ เรากำหนดให้ทั้งสองคลาสนี้สืบคุณสมบัติจากอินเตอร์เฟส IVehicle ดังนั้นเราจึงจำเป็นจะต้องอิมพลีเมนต์หรือใส่นิยามเมธอดและโค้ดการทำงานภายในเมธอดไว้ในคลาสทั้งสองนี้
เมธอดที่เราจำเป็นต้องใส่ (ไม่ใส่ไม่ได้ จะเกิดเออเรอร์ตอนคอมไพล์) ไว้ภายในคลาส Bicycle และคลาส Bike คือสามเมธอดที่เราประกาศไว้ภายในอินเตอร์เฟส ได้แก่เมธอด void ChangeGear(int a) เมธอด void SpeedUp(int a) และเมธอด void ApplyBrakes(int a)

รูปที่ 9 คือตัวอย่างโค้ดแสดงการเรียกใช้งานคลาส Bicycle และคลาส Bike มาสร้างเป็นออพเจ็กต์เพื่อใช้งาน
- บรรทัดที่ 62: สร้างออพเจ็กต์หรืออินสแตนซ์ของคลาส Bicycle
- บรรทัดที่ 63: เรียกใช้งานเมธอด void ChangeGear(int a) ที่เป็นเมธอดตามอินเตอร์เฟส
- บรรทัดที่ 64: เรียกใช้งานเมธอด void SpeedUp(int a) ที่เป็นเมธอดตามอินเตอร์เฟส
- บรรทัดที่ 65: เรียกใช้งานเมธอด void ApplyBrakes(int a) ที่เป็นเมธอดตามอินเตอร์เฟส
- บรรทัดที่ 67: แสดงข้อความ "Bicycle present state :"
- บรรทัดที่ 68: เรียกใช้งานเมธอด printStates() ที่ไม่เกี่ยวอะไรกับอินเตอร์เฟส

อินเตอร์เฟสแบบเจนเนอริก
ข้อดีอย่างหนึ่งของภาษาซีชาร์ปคือการมีกลไกที่เรียกว่าเจนเนอริก ที่ช่วยให้โค้ดเดียวกันสามารถทำงานได้กับไทป์ต่าง ๆ ทุกชนิด เราสามารถใช้เจนเนอริกร่วมกับคลาสและเมธอดได้ และเราสามารถใช้เจนเนอริกร่วมกับอินเตอร์เฟสได้ เรียกว่าอินเตอร์เฟสแบบเจนเนอริก
บรรทัดที่ 3-8 คือนิยามอินเตอร์เฟส IPerson<T> ซึ่งเป็นอินเตอร์เฟสแบบเจนเนอริก บรรทัด 5 นิยามเมธอดซิกเนเจอร์ add(T) ที่รับพารามิเตอร์แบบเจนเนอริกหนึ่งตัว บรรทัด 6 นิยามเมธอดซิกเนเจอร์ delete() ที่ไม่ได้ระบุว่าต้องทำอะไรกับเจอนเนอริก บรรทัด 7 นิยามเมธอดซิกเนเจอร์ get() ที่มีค่าส่งกลับเป็นเจนเนอริก
บรรทัด 9-24 คือนิยามคลาส Employee<T> ที่เป็นคลาสแบบเจนเนอริกและสืบคุณสมบัติจากอินเตอร์เฟส IPerson<T> บรรทัด 11 ประกาศตัวแปร enroll ซึ่งเป็นฟิลด์ที่มีดาต้าไทป์เป็นแบบเจนเนอริก บรรทัด 12-15 คืออิมพลีเมนต์ของเมธอด add(T) ทำหน้าที่นำค่าที่ผู้เรียกส่งมาใส่ในตัวแปร enroll บรรทัด 16-19 คืออิมพลีเมนต์ของเมธอด delete() ทำหน้าที่นำค่าโดยปริยายของไทป์ใส่ในตัวแปร enroll ดังนี้ถ้าเจนเนอริกทำงานกับตัวเลข ค่าของ enroll จะเป็นศูนย์ แต่ถ้าเจนเนอริกทำงานกับสตริงก์ ค่าของ enroll จะเป็นสตริงก์ว่าง บรรทัด 20-23 คืออิมพลีเมนต์ของเมธอด get() ทำหน้าที่นำค่าของตัวแปร enroll ส่งกลับไปให้ผู้เรียก
โค้ดบรรทัด 29-31 แสดงตัวอย่างการนำคลาส Employee<T> ไปสร้างออพเจ็กต์แล้วเรียกใช้งาน บรรทัด 29 สร้างอินสแตนซ์ของคลาส Employee<T> โดยกำหนดตัวแปรอ้างอิงชื่อ myEmployee และกำหนดชนิดข้อมูลให้แก่เจนเนอริกเป็นสตริงก์
โค้ดบรรทัด 30 ทดลองเรียกเมธอด add(T) โดยส่งข้อความตัวอักษรไปซึ่งทำได้เพราะเราได้กำหนดชนิดข้อมูลให้แก่เจนเนอริกเป็นสตริงก์ไว้ที่บรรทัดก่อนหน้านี้แล้ว บรรทัด 31 ทดลองเรียกเมธอด get() ซึ่งจะได้ค่าจากตัวแปร enroll ที่อยู่ภายในคลาส Employee<T>
บทความตอนนี้แสดงข้อได้เปรียบหรือจุดเด่นของภาษาซีชาร์ปที่ไม่พบในภาษาอื่น ๆ บางภาษา โดยอธิบายการทำงานของอินเตอร์เฟสและวิธีใช้งานแบบต่าง ๆ เพิ่มเติมจากตอนที่แล้ว ได้แก่ การสร้างสมาชิกของอินเตอร์เฟสสองตัวแบบเจาะจง การใช้อินเตอร์เฟสกับสองคลาส และอินเตอร์เฟสแบบเจนเนอริก ในบทความตอนต่อไปจะพูดเรื่องรายละเอียดอื่น ๆ ที่น่าสนใจซึ่งทำให้ภาษาซีชาร์ปโดดเด่นและเหมาะแก่การใช้พัฒนาซอฟต์แวร์