/ 365วันแห่งโปรแกรม

[365 วันแห่งโปรแกรม #day39] Generic Programming (ตอนที่ 2)

วันที่สามสิบแก้าของ ‪#‎365วันแห่งโปรแกรม วันนี้ขอนำเสนอเรื่อง Generic Programming ตอนที่ 2


ความเดิมตอนที่แล้วเราคุยกันเรื่อง Generic Programming บอกว่ามันคืออะไร แล้วก็ยกตัวอย่าง predefined generic type ก็คือ List ในวันนี้เราจะมาต่อกันที่วิธีการสร้าง class หรือ method ที่เป็น Generic type กันครับ ก่อนอื่นเราต้องเข้าใจก่อนว่า ไม่ใช่แค่ class อย่างเดียวที่ทำ Generic ได้ แต่เราสามารถใช้ Generic Programming ทั้งกับ Class, Interface, Method, Delegate หรืออื่นๆ ก็แล้วแต่ว่าภาษาโปรแกรมนั้นๆ ยอมให้ใช้กันอะัไรได้บ้าง แต่โดยทั่วไปแล้วที่ได้แน่ๆ ก็มี Class กับ Interfaceแหละครับ นอกนั้นก็น่าจะเป็น Method ครับ

Generic Class

Generic Class ก็เหมือนกับ List ที่เรายกตัวอย่างเมื่อวานครับ มันคือคลาสที่มีการ declare อะไรบางอย่างโดยยังไม่ได้ระบุ type ไว้ แล้วในตอนเอาไปใช้งานค่อยระบุครับ ส่วนใหญ่ Generic Class เนี่ย จะใช้กับ collections ต่างๆ นั่นเองครับ

วิธีการสร้าง Generic Class ก็ไม่ยากเย็นอะไครับ แค่สร้าง Class ปกติเลย แล้วก็เปลี่ยน type ของสิ่งที่ Class นั้นทำงานด้วยเป็น type parameter แทนครับ สิ่งที่ต้องพิจารณาในการทำ Generic type ก็จะมีประมาณนี้ครับ

  • ให้เลือก type ที่ทำให้เกิดความ flexible และทำให้เราสามารถเอา Class นี้ไป reuse ได้

  • parameter type นั้นต้องมี constraint อะไรไหม เช่น ต้องเป็น type ที่ derived มาจาก IComparable เท่านั้นนะ อะไรแบบนี้

  • Generic class นั้นสามารถสืบทอดได้ ก็ต้องดูตามความเหมาะสมครับว่าควรทำไหม เพราะบางทีเมื่อมีการสืบทอดแล้วมันไม่ make sense

  • พิจารณาว่าควรมี Type parameter กี่ตัว (Generic class สามารถมี type parameter กี่ตัวก็ได้ครับ)

หลังจากนั้นเราก็มาลองสร้าง Generic Class กันได้เลยครับ ผมขอยกตัวอย่าง LinkedList ละกันครับ เพราะน่าจะเข้าใจได้ง่ายดี (ตัวอย่างนี้เป็นภาษา C#)

public class Node<T> {
    public Node<T> Next;
    public Node<T> Previous;
    public T Data;
}

public class LinkedList<T> {
    private Node<T> head;
    private Node<T> tail;
    public void Add(T item){
        Node<T> node = new Node<T>();
        node.Data = item;
        if(tail!=null){
            node.Previous = tail;
            tail.Next = node;
            tail = node;
        }else{
            tail = node;
            head = node;
        }
    }
}

จากโค้ดตัวอย่าง LiskedList ของเรานั้นประกอบไปด้วยคลาส Node และ LinkedList ครับ โดย Node ก็จะใช้เก็บ item ที่เรา Add ใส่ไป แล้วก็เก็บว่า Node ถัดไปคืออันไหน Node ก่อนหน้าคืออันไหน ส่วนคลาส LinkedList ก็มีไว้สำหรับบริหารจัดการ Operation ต่างๆ ที่ทำกับสายของ Node ครับ ดังนนั้นจะมีการเก็บตัวแรกของสาน และตัวสุดท้ายเอาไว้ ในคลาสนี้เราใช้ Generic เพื่อบอกว่าเรายังไม่อยากกำหนด Type ของ item ที่จะเก็บนะ เดี๋ยวตอนเอาไปใช้แล้วจะบอกอีกที เรามาลองเอาไปใช้กันดูดีก่าครับ

LinkedList<int> intList = new LinkedList<int>();
intList.Add(1);
intList.Add(2);

จะเห็นว่าก็เหมือนกับ LinkedList ที่มีมาให้เลย แค่ LinkedList ของเรายังมีฟังก์ชันไม่ครบแค่นั้นเอง

แล้วถ้าเราอยากรับ type parameter หลายตัวล่ะ? ก็ง่ายๆ ครับก็ใส้เพิ่มไปได้เลย ตัวอย่างที่ใช้ type parameter หลายตัวก็เช่นในคลาส Dictionary เราก็สามารถใช้ type parameter ตัวหนึ่งเป็น key ส่วนอีกตัวเป็น value ครับ

class Dictionary<K ,V> where K : IComparable<K> {
    ....
}

ก็ประมาณนี้ครับ จะเห็นว่าในตัวอย่างนี้มี K และ V เป็น type parameter โดยเฉพาะ K นั้นพิเศษกว่าปกติคือ เราบังคับว่า K จะต้องเป็นคลาสที่ derived มาจาก IComparable เท่านั้น เพราะว่าใน Map นั้นเราต้องสามารถ get element ต่างๆ ด้วย key ของมันได้ ดังนั้นสิ่งที่จะเป็น key ก็ควรต้องสามารถนำไปเปรียบเทียบกันได้ครับ

บางที type parameter ของ Generic Class อาจจะเรียกว่า Class Parameter ก็ได้

Generic Method

บางทีเราก็ไม่อยากทำให้คลาสทั้งคลาสเป็น Generic ครับ ดังนั้นเราก็ทำแค่ Method ที่ใช้ก็พอ ส่วนใหญ่เราจะทำ Generic กับ Method ที่เป็น Static ครับ ก็พวก Utility ต่างๆ ที่ควรจะถูกเรียกใช้ได้เลยโดยไม่ต้องสร้าง Instance เอง

ตัวอย่างของเรื่องนี้คือ Swap() ครับ ขอยกตัวอย่างใน C# เหมือนเดิม

static void Swap<T>(ref T a, ref T b)
{
    T temp;
    temp = b;
    b = a;
    a = temp;
}

จากโค้ดด้านบน Swap รับพารามิเตอร์ 2 ตัว เป็น reference ของ type ที่ไม่ระบุ เสร็จแล้วมันก็จะสลับค่าให้ แล้วก็จบการทำงานไปเฉยๆ เดี๋ยวเราจะลองเอา เมธอดนี้ไปใช้กับ int 2 ตัวกันครับ

int a = 5;
int b = 10;
Console.WriteLine(a + " " + b);
Swap<int>(ref a, ref b);
Console.WriteLine(a + " " + b);

===output===

5 10
10 5

จะเห็นว่าเราสามารถนำมาใช้สลับ reference ของ int ได้ครับ และถ้าเอาไปใช้กับ type อื่นก็จะได้ผลแบบเดียวกันครับ

ในบางภาษานั้นบังคับว่าเราจะสามารถแทน type parameter ด้วย complex data type เท่านั้น ไม่สามารถใส่เป็น primitive type ได้

‎day39 #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...