<%@ Page Language="VB" ContentType="text/html" ResponseEncoding="windows-874" %> http://www.siam2dev.com >> ชุมชนนักพัฒนาซอฟต์แวร์ของไทยแห่งใหม่
เข้าสู่ระบบ ::    
http://www.siam2dev.com >> ชุมชนนักพัฒนาซอฟต์แวร์แห่งใหม่
Home   
News   
Articles   
Programming Zone   
DownLoad   
Contact US   
Links   
Webboard   
ฯลฯ   
 
     สวัสดีครับทุกท่าน ผมพัฒนาเว็บนี้ขึ้นมาโดยมีวัตถุประสงค์ ที่จะเห็นโปรแกรมเมอร์ของไทย ได้มีการคิดค้นและพัฒนาซอฟต์แวร์ ของคนไทย กันมากขึ้น เพื่อเป็นการช่วยชาตของเราให้เจริญยิ่งขึ้นไป (จะได้ไม่ต้องทะเลาะกันอย่างทุกวันนี้ 555 อย่าเครียดครับ ) เนื่องจากเว็บนี้ยังเพิ่งเริ่มต้นนะครับอาจมีเนื้อหาบางงส่วนที่ยังไม่สมบูรณ์จึงขออภัยมา ณ โอกาส นี้ด้วยครับ
 
   VB.NET_OOP
     Interface

     Interface
    ผู้เรียนภาษา C# (และภาษา Java ด้วย) บางคนพอได้ยินคำว่า Interface แล้วขวัญบิน เพราะคิดว่าเป็นศาสตร์การเขียนโค้ดระดับสูง คงมีแต่ศาสตราจารย์ทางคอมพิวเตอร์เท่านั้นที่จะเข้าใจได้ นั่นเป็นความเข้าใจผิด อันที่จริง interface เป็นเรื่องเรียบง่ายที่แม้แต่แม่บ้านทุกคนก็สามารถทำความเข้าใจได้
    ลองคิดเปรียบเทียบว่าคลาสเป็นเครื่องใช้ไฟฟ้าในครัวเรือน เครื่องใช้ไฟฟ้าก่อนจะทำงานได้ต้องเสียบปลั๊ก ยกตัวอย่างเช่น เครื่องซักผ้ากับเครื่องดูดฝุ่น แม้จะแตกต่างกันมาก แต่ก็เป็นเครื่องใช้ไฟฟ้าเหมือนกัน จึงต้องเสียบปลั๊กเหมือนกัน เครื่องใช้ไฟฟ้าไม่ว่าจะเป็นชนิดก็ตาม ต้องมีปลั๊กที่ตรงตามข้อกำหนด มิฉะนั้นจะเสียบกับปลั๊กที่ฝาผนังไม่ได้ ดังเช่นที่ท่านอาจพบว่าเครื่องใช้ไฟฟ้าที่ซื้อมาจากประเทศอังกฤษไม่สามารถเสียบกับปลั๊กในประเทศไทยได้
ข้อกำหนดของปลั๊กไฟนี้ เทียบได้กับ interface มันคือข้อกำหนดซึ่งทำหน้าที่ระบุเพียงว่าปลั๊กไฟต้องมีลักษณะเช่นใด (คือมีขนาดเท่าใด มีกี่ขา แต่ละขามีขนาดเท่าใดเป็นต้น) แต่ไม่ได้กำหนดว่าจะเครื่องใช้ไฟฟ้านั้นๆ นำไฟฟ้าไปใช้อย่างไร ยกตัวอย่างเช่น เครื่องซักผ้ากับเครื่องดูดฝุ่น แม้จะมีปลั๊กไฟที่ตรงตามข้อกำหนดทั้งคู่ แต่วิธีที่มันนำไฟฟ้าไปประยุกต์ใช้นั้นแตกต่างกันมาก

โปรดคิดเปรียบเทียบว่า ไฟฟ้าคือคลาสหลัก หรือตัวโปรแกรมหลัก เครื่องใช้ไฟฟ้าเปรียบเหมือนคลาสบริการ คลาสบริการจะต้องต่อเชื่อมกับคลาสหลักได้ ข้อกำหนด หรือข้อตกลงของการเชื่อมต่อระหว่างคลาสหลักกับคลาสบริการคือ interface ยกตัวอย่างเช่นเรามีคลาสหลักทำหน้าที่แสดงรายงาน และมีคลาสบริการหลายคลาส ทำหน้าที่เรียงข้อมูล คลาสบริการแต่ละคลาสใช้วิธีเรียงข้อมูลแตกต่างกัน (เช่น quick sort, bubble sort, merge sort และ radix sort) interface คือข้อกำหนดที่ทุกคลาสบริการต้องทำตาม เพื่อให้คลาสหลักสามารถเรียกคลาสบริการเพื่อการเรียงข้อมูลได้ โดยคลาสหลักไม่จำเป็นต้องสนใจว่าคลาสบริการมี method ชื่ออะไร พารามิเตอร์เป็นเช่นไร หรือแม้แต่รับ-ส่งข้อมูลเป็น type อะไร

    ภาษา C# สนับสนุนแนวคิด component based programming อย่างเต็มที่ เพราะแนวคิดนี้ช่วยให้การสร้างซอฟท์แวร์มีประสิทธิภาพ ท่านคงยังจำได้จากที่อ่านไปแล้วในบทที่ 1 ว่า แนวคิดนี้เหมือนการต่อ เลโก้ หรือการนำดิจิตอลไอซี มาต่อกัน หากออกแบบการเชื่อมต่อไว้ให้ดี จะทำให้สามารถนำชิ้นส่วนเล็กๆ หลายๆ แบบมาต่อกันให้กลายเป็นระบบใหญ่ที่มีความซับซ้อนได้โดยง่าย ในภาษา C# การเชื่อมต่อนี้คือ interface

    หลักการ OOP มีเป้าหมายให้การสร้างซอฟท์แวร์มีความยืดหยุ่น นำกลับมาใช้ใหม่ได้ และบำรุงรักษาได้ง่าย แต่กลไก OOP อื่นๆ ในภาษา C# ช่วยให้บรรลุเป้าหมายนี้ได้เพียงแค่ครึ่งเดียว และ interface เติมเต็มส่วนที่เหลือ ประโยชน์ของ interface มีสามประการดังนี้

     • จัดกลุ่มงานที่สัมพันธ์กัน
     • ความสะดวกเรื่อง type
     • multiple inheritance

    จัดกลุ่มงานที่สัมพันธ์กัน: เราใช้ interface เพื่อรวบรวมสิ่งต่างๆ ที่หลายคลาสใช้ร่วมกัน โดยคลาสเหล่านั้นไม่จำเป็นต้องมีความสัมพันธ์กัน (คือไม่สืบสันดานกัน) ยกตัวอย่างเช่นคลาสเครื่องซักผ้า และคลาสเครื่องดูดฝุ่น แม้จะมีความสามารถในการปรับความเร็วในการทำงาน ทั้งคู่ แต่การนำสองคลาสนี้มาเป็น derived class ของคลาส “ปรับความเร็ว” ย่อมไม่สมเหตุสมผล (เพราะการปรับความเร็วของเครื่องซักผ้าและคลาสเครื่องดูดฝุ่นมีความแตกต่างกันมากเกินไป) ที่ถูกคือทั้งคู่ควรเป็น derived class ของคลาส “เครื่องใช้ไฟฟ้า” แล้วจัดให้มี interface การ “ปรับความเร็ว” ที่คลาสทั้งสองใช้ร่วมกัน (แต่มีวิธีปฏิบัติเป็นของตนเอง)

    ความสะดวกเรื่อง type: นอกจากนั้น interface ยังช่วยให้เราใช้ object ได้ โดยไม่จำเป็นต้องรู้ type ของมัน ยกตัวอย่างเช่นหากเรารู้สึกว่าความเร็วในการทำงานสูงเกินไป เราสามารถสั่งให้ผู้ที่กำลังทำงานลดความเร็วลงได้ โดยเราไม่จำเป็นต้องรู้ว่าผู้ที่กำลังทำงานเป็น “เครื่องซักผ้า” หรือ “เครื่องดูดฝุ่น” ขอยกอีกตัวอย่างที่เป็นเรื่องของการเขียนโค้ดจริงๆ คืออัลกอริทึมเพื่อเรียงข้อมูล (sorting algorithm) ที่ตามปรกติจะรับ object ที่มี type เป็น interface แบบ IComparable ทำให้มันสามารถจัดเรียงข้อมูลได้ โดยไม่จำเป็นต้องสนใจว่า object ที่รับมามี type อะไร

    multiple inheritances: ภาษา C# ไม่สนับสนุนการทำ multiple inheritance (การสืบสันดานจากหลาย base class) เพราะอาจทำให้เกิดข้อผิดพลาดที่ดีบักยาก งานใดที่จำเป็นต้องอาศัย multiple inheritance สามารถทำได้โดยใช้ interface

โปรดดูตัวอย่างโค้ดแสดงการทำงานของ interface ต่อไปนี้

     interface I1
     {
          void MyFunction();
     }
     interface I2
     {
          void MyFunction();
     }

     โค้ดข้างบนคือนิยาม interface class สอง class ชื่อ I1 และ I2 ตามลำดับ โปรดสังเกตว่า interface class คล้าย abstract class ในแง่ที่ว่า ภายในคลาสมีนิยามสมาชิกเฉพาะส่วนหัวเท่านั้น ไม่มีโค้ดที่ทำงานจริง

     class Test : I1, I2
     {
          public void MyFunction()
               {
                    Console.WriteLine("This is I1");
               }
          void I2.MyFunction()
               {
                    Console.WriteLine("This is I2");
               }
     }

     โค้ดข้างบนคือนิยามคลาสธรรมดาชื่อ Test เป็นคลาสที่สืบสันดานจาก interface class สองคลาส (multiple inheritances) ภายในคลาส Test มีโค้ดทำงานของ method ที่รับมรดกมาจากการ interface คือ method MyFunction() โดยมีทั้ง MyFunction() ของ I1 และ MyFunction() ของ I2

     class Program
     {
          static void Main(string[] args)
               {
                    Test t = new Test();
                    I1 i1 = (I1)t;
                    i1.MyFunction();
                    I2 i2 = (I2)t;
                    i2.MyFunction();
              }
     }

     โค้ดข้างบนคือโค้ดที่สาทิตการใช้งาน interface class I1, I2 และคลาสที่สืบสันดานจากสองคลาสนี้ (คือ Test) ขั้นแรกเราสร้างตัวแปร object ชื่อ t ให้มี type เป็น test และสร้าง instance ของคลาส test จากนั้นสร้างตัวแปร object ชื่อ i1 มี type เป็น I1 และสร้าง instance ของคลาส I1 โดยใช้การ casting จาก type test (คำสั่ง I1 i1 = (I1)t;) บรรทัดถัดมาลองเรียกใช้ method MyFunction(); ดูท่านคิดว่าจะเห็นข้อความว่าอะไร

    คำสั่งสองบรรทัดสุดท้ายสร้างตัวแปร object ชื่อ i2 ให้มี type เป็น I2 และสร้าง instance ของคลาส I2 โดยอาศัยการ casting จาก type test (คำสั่ง I2 i2 = (I2)t;) บรรทัดถัดมาลองเรียกใช้ method MyFunction(); ดูท่านคิดว่าจะเห็นข้อความว่าอะไร

ผลลัพธ์จากการรันโค้ดตัวอย่างนี้คือ

     This is I1
     This is I2

     เมื่อใดจึงควรใช้ Interface

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

ต่อไปลองพิจารณาเหตุการณ์ต่อไปนี้ เราเขียนคลาสหลักคลาสหนึ่ง (สมมุติว่าชื่อคลาส BackUp) ทำหน้าที่เก็บสำรองข้อมูล โดยมีจุดหนึ่งในคลาส BackUp ที่เราต้องการบีบอัดข้อมูลก่อนนำไปเก็บ สมมุติว่าเราสร้างคลาสบริการชื่อ MakeZip ทำหน้าที่บีบอัดข้อมูลแบบ zip เมื่อต้องการบีบข้อมูลเราจึงใช้คำสั่งดังนี้

MakeZip myZip = new MakeZip();
myZip.Compress(data);เมื่อทำงานไปได้ระยะหนึ่งเราต้องการบีบข้อมูลโดยใช้อัลกอริทึมแบบ rar เราจึงต้องสร้างคลาสบริการอีกคลาสหนึ่งชื่อ MakeRar ทำหน้าที่บีบอัดข้อมูลแบบ rar เมื่อต้องการบีบข้อมูลเราจึงใช้คำสั่งดังนี้

MakeRar myRar = new MakeRar()
myRar.Compress(data); เมื่อทำงานไปได้ระยะหนึ่งเราต้องการบีบข้อมูลโดยใช้อัลกอริทึมใหม่ๆ เพิ่มอีกห้าแบบคือ cab, tar, arj, gz และ arc เราจึงต้องสร้างคลาสบริการอีกห้าคลาส ทำให้เรามีคลาสบริการที่ทำหน้าที่บีบข้อมูลเจ็ดคลาสที่เวลาเรียกใช้ต้องสร้าง object เจ็ด object และเรียก method ของมันที่แตกต่างกันไปเจ็ดแบบซึ่งไม่ค่อยสะดวกนัก ในกรณีที่มีนักเขียนโค้ดหลายคน แบ่งงานกันนิยามคลาสเหล่านี้ นักเขียนโค้ดแต่ละคนย่อมนิยามวิธีเรียกใช้ method แตกต่างกันไป เพราะไม่มีสิ่งใดทำหน้าที่กำหนดมาตรฐานให้ตรงกัน

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

     interface ICompress
     {
          string Compress(string data);
     }
     class MakeZip : ICompress
     {
          public string Compress(string data)
               {
                    return data + " compressed using Zip"
               }
     }
    
     class MakeRar : ICompress
     {
          public string Compress(string data)
               {
                    return data + " compressed using Rar"
               }
     }

     class Program
     {
          static void Main(string[] args)
               {
                    ICompress myCompress = new MakeZip();
                    Console.WriteLine(myCompress.Compress("data"));
                    Console.ReadLine();
                   myCompress = new MakeRar();
                    Console.WriteLine(myCompress.Compress("data"));
                    Console.ReadLine();
               }
     }

ผลลัพธ์ของการทำงานคือ

     Data compressed using Zip
     Data compressed using Rar

     จากโค้ดข้างบนจะเห็นว่าผู้เขียนนิยาม interface ชื่อ ICompress เพื่อเป็นข้อกำหนดให้คลาสบริการ (คลาสทำหน้าที่บีบข้อมูล) ทุกตัวดำเนินการตาม ในตัวอย่างนี้มีสมาชิกเพียงตัวเดียวคือ method Compress (แต่ในการใช้งานจริงปรกติแล้วจะซับซ้อนกว่านี้มาก) คลาส MakeZip และคลาส MakeRar นำ ICompress ไปใช้ราวกับว่ามันคือ base class แบบ abstract แล้วคลาสบริการทุกคลาสต้องนิยามโค้ดการทำงานให้แก่ method Compress

     คลาส Program ทำหน้าที่ทดสอบการทำงานโดยสร้าง object ชื่อ myCompress จากคลาส MakeZip โดยระบุให้มี type เป็น ICompress จากนั้นเรียก method Compress ซึ่งมีผลให้ method ชื่อเดียวกันในคลาส MakeZip ทำงาน บรรทัดต่อมาเปลี่ยน myCompress ให้อ้างอิงไปยัง object ตัวใหม่ก็สามารถทำได้ เพราะถือว่าทั้ง MakeZip และ MakeRar มี type เป็น ICompress เหมือนกัน และการเรียกใช้ method Compress ก็ยังคงมีวิธีใช้คงเดิม เพราะทั้ง MakeZip และ MakeRar ถูกสร้างขึ้นตามข้อตกลงเดียวกัน
     ตัวอย่างงานที่มีคลาสบีบข้อมูลหลายแบบ (แต่ละแบบใช้อัลกอริทึมบีบข้อมูลแตกต่างกัน) หากคลาสเหล่านี้ถูกเขียนขึ้นโดยนักเขียนโค้ดหลายคน การนิยามวิธีเรียกใช้ method และ data type ย่อมแตกต่างกันออกไปตามคตินิยมของนักเขียนโค้ดแต่ละคน หาเรานิยาม interface ไว้ก่อน แล้วให้นักเขียนโค้ดทุกคนดำเนินการตามข้อตกลงใน interface ผู้นิยามคลาสที่ต้องการบีบข้อมูลจะสามารถเรียกใช้ method บีบข้อมูลได้โดยไม่ต้องกังวลถึงวิธีเรียกใช้

     ความแตกต่างระหว่า interface กับ abstract class

    ท่านคงจำได้จากเรื่อง abstract class ที่เรียนไปแล้วในหัวข้อก่อนหน้านี้ในบทนี้ ว่า abstract class อาจมีแต่สมาชิกแบบ abstract ซึ่งไม่มีโค้ดนิยามการทำงาน สิ่งนี้ทำให้มันคล้าย interface แต่หน้าที่และประโยชน์ของ interface กับ abstract class ย่อมไม่เหมือนกัน interface และ abstract class แตกต่างกันดังนี้

    เราทำ multiple inheritance ต่อ Interface ได้ แต่ทำต่อ abstract class ไม่ได้
สมาชิกทุกตัวของ interface ต้องไม่มีนิยามการทำงาน แต่ abstract class สามารถมีได้ (คือ concrete method)
Interface มี constant ได้เฉพาะแบบ static final ส่วน abstract class มีได้ทั้งที่เป็นแบบ instance (แบบธรรมดา) และที่เป็น static
เราใช้ interface เพื่อจัดกลุ่มคุณสมบัติย่อยๆ ที่มีร่วมกันในหลายคลาส โดยคลาสเหล่านั้นไม่ได้ inherit จาก base class เดียวกัน แต่เราใช้ abstract class เพื่อสร้าง base class
Interface ทำงานช้ากว่า abstract class
หากเราเพิ่ม method ใน interface เราต้องแก้ไขคลาสที่ใช้งาน interface ทุกตัว แต่ถ้าเราเพิ่ม method แบบ virtual ใน abstract class เราไม่จำเป็นต้องแก้ derived class แต่อย่างใด

     Polymorphism โดยใช้ Interface

    ท่านคงยังจำได้จากที่เรียนไปแล้วในบทที่ 1 ว่า polymorphism เกี่ยวข้องกับการเปลี่ยนแปลง method ของ base class โดยการทำ method overriding เพื่อให้ method มีการทำงานที่แตกต่างไปใน derived class ซึ่งถือว่าเป็นการทำ polymorphism แบบที่พบเห็นได้ทั่วไป เรียกว่า “การทำ polymorphism ทาง inheritance”

    นอกจากนั้นในบทที่ 4 ท่านได้เรียนการทำ method overloading ซึ่งเป็นการนิยาม method ชื่อเดียวกันหลายๆ แบบ โดยมี signature แตกต่างกัน ทำให้ method ชื่อเดียวสามารถทำงานกับข้อมูล หรือ type ได้หลายแบบ ซึ่งถือว่าเป็นการทำ polymorphism แบบที่เรียกว่า “การทำ polymorphism ขณะ compile-time”

    ในบทนี้ จากโค้ดตัวอย่างข้างบน ท่านจะเห็นว่าตัวแปรอ้างอิง myCompress ซึ่งทำหน้าที่อ้างอิง interface type ICompress มีผลให้ตัวแปรนี้ใช้อ้างอิง object ที่ถูกสร้างจากคลาส MakeZip หรือ MakeRar ก็ได้ สลับกลับไปกลับมาก็ได้ ทำให้ method ที่ตัวแปรนี้อ้างถึงเปลี่ยนไปได้ด้วย ยกตัวอย่างเช่น ขณะที่ myCompress อ้างถึง MakeZip เมื่อเราใช้คำสั่ง myCompress.Compress() method Compress จะทำงานแตกต่างจากเมื่อ myCompress อ้างถึง MakeRar การที่ method ชื่อเดียวกัน มี signature เหมือกัน แต่เปลี่ยนแปลงการทำงานไป-มาได้เช่นนี้ ถือว่าเป็น polymorphism ด้วยเช่นกัน เรียกว่า “การทำ polymorphism ผ่านทาง interface”

     ข้อสรุปเรื่อง interface

    interface คือข้อตกลง หรือสัญญา หรือข้อกำหนดกลาง ที่หลายๆ คลาสนำไปใช้ร่วมกัน
    interface ทำให้เกิดความสม่ำเสมอในการเชื่อมต่อระหว่างคลาส
    interface คือการสนับสนุนแนวคิด component base programming
    interface รวมกลุ่มคุณสมบัติที่มีร่วมกันในหลายๆ คลาส โดยที่คลาสเหล่านั้นไม่ได้ inherit จาก     base class เดียวกัน
    interface ทำให้เราปฏิบัติต่อกลุ่มของ object ได้ราวกับว่าพวกมันมี type เดียวกัน
    ตัว interface เองก็คือคลาส
    interface คล้ายคลาสแบบ abstract
    เราใช้ interface แทน abstract เพื่อทดแทนการทำ multiple inheritance
    คลาสที่ไม่เป็น abstract ซึ่งสืบสันดานจาก interface ต้องใส่โค้ดทำงานให้สมาชิกทุกตัว
    นำ interface ไปสร้าง object โดยตรงไม่ได้
    ภายใน interface มีสมาชิกต่อไปนี้ได้ methods, properties, indexers, events
    method ใน interface มีโค้ดทำงานไม่ได้ (มีได้เพียงประกาศส่วนหัว จะมีโค้ดที่เป็นไส้ในไม่ได้)
    คลาส และ struc อาจสืบสันดานจาก interface ได้มากกว่าหนึ่งคลาส
    ตัว interface เองก็อาจสืบสันดานจาก interface ได้มากกว่าหนึ่งคลาสเช่นกัน
    การใช้งาน interface อาจทำให้เกิดภาวะ polymorphism ได้



 
 
:: http://www.siam2dev.com ::
e-mail :: xnattapong@hotmail.com , songneam@gmail.com