การเปลี่ยนพฤติกรรมของ Class ขณะ Runtime

ก่อนอื่นในคราวนี้ ผู้เขียนจะขอลองเปลี่ยนไปเขียนเป็น C# แทน เนื่องจากตัว IDE ของ Visual Studio นั้น สามารถออกแบบ GUI(Graphics User Interface) ได้ง่ายกว่า IDE หลายๆตัวของ java มาก โดยผู้เขียน จะพยายามไม่ใช้ความสามารถบางชนิด ที่ภาษาอื่นๆบางภาษาไม่มี เพื่อความสะดวกในการทำความเข้าใจร่วมกัน (คนอ่าน: ไอคนเขียน จะสลับไปๆมาๆทำไมว๊ะ!!! คนเขียน: สลับไปเถอะ โค้ดเหมือนกันจะตาย = =)

เมื่อพูดถึงการเขียน Class ซึ่งเป็นหัวใจหลักของการเขียนโปรแกรมแบบ OOP แล้ว ก็คงมีหลายๆคนรู้สึกว่า การจะสร้าง Class ใดๆขึ้นมานั้น ถึงแม้ว่าเราจะสามารถออกแบบให้ Class ของเราทำงานได้อย่างดีแค่ไหนแล้ว แต่สุดท้ายก็ยังไม่สามารถทำให้ยืดหยุ่นได้

ความไม่ยืดหยุ่นในที่นี้ หมายถึงพฤติกรรมของ Class ที่ไม่อาจเปลี่ยนแปลงได้ เช่นสมมุติว่า เราออกแบบ Class ของ แมว(Cat) ขึ้นมา ซึ่งในตัวของ Class Cat นี้ ก็มีพฤติกรรมมากมาย หลาย Method ซึ่งก็รวมถึงการ ร้อง(Meowing()) ด้วย
สมมุติว่าเรากำหนดให้ Class Cat เป็นแบบนี้
Class Cat (C#)

    public void Meowing()
    {
        MessageBox.Show(“Meow”);
    }


    public Cat()
    {

    }
จะสังเกตุได้ว่า การร้องของแมวนั้น ไม่ว่าอย่างไร ก็จะร้องออกมาเหมือนเดิม(แสดง MessageBox คำว่า Meow ออกมา) ซึ่งหากจะทำการเปลี่ยนแปลงการทำงาน ก็จะต้องแก้ไขที่ตัวโค้ด แล้วทำการ Build ขึ้นมาใหม่ ซึ่งการทำงานในลักษณะนี้ อาจเป็นไปได้ยาก หากเราต้องทำโปรแกรมให้ลูกค้า และมีความต้องการในการที่จะเปลี่ยนปฤติกรรมของ Class ได้ โดยที่ไม่ต้อง Build โปรแกรมใหม่ หรือเป็นสิ่งที่ใน Design Pattern นั้น เราเรียกการทำงานลักษณะนี้กันว่า Strategy Pattern นั่นเอง

รูปแบบของ Strategy Pattern นั้น หากจะพูดง่ายๆ ก็คือการนำ Method ใดๆก็ตาม ที่เราต้องการให้สามารถเปลี่ยนแปลงการทำงานได้ มาสร้างให้อยู่ในรูปแบบของ Class โดยที่ Class ต่างๆที่สามารถนำมาใช้แทน Method นั้นๆ ต้องทำการ Implement Interface เดียวกัน เพื่อที่จะได้แน่ใจว่า Class นั้นๆ มี Method ที่เราต้องการอยู่จริง

Interface ของการร้อง จะตั้งชื่อว่า IMeowBehavior ซึ่งวิธีการร้อง จะมี 2 วิธีคือ MessageBoxMeow กับ ConsoleMeow ซึ่งทั้ง 2 Class นี้ จะต้อง Implements IMeowBehavior ทั้งคู่

public interface IMeowBehavior
{
    public void Meowing();
}
public class MessageBoxMeow : IMeowBehavior
{
    public void Meowing()
    {
        MessageBox.Show(“Meow from MessageBoxMeow”);
    }
}
public class ConsoleMeow : IMeowBehavior
{
    public void Meowing()
    {
        Console.WriteLine(“Meow from ConsoleMeow”);
    }
}
class Cat
{
    public IMeowBehavior meowBehavior; // ใช้ public จะได้เข้าใจง่าย
    public void Meowing()
    {
        meowBehavior.Meowing();
    }


    public Cat()
    {
        meowBehavior = new MessageBoxMeow();
    }
}
โดยเราออกแบบ Form ไว้แบบนี้
กำหนดการทำงานใน Form1 ไว้ดังนี้
public partial class Form1 : Form
{
    Cat c = new Cat();
    public Form1()
    {
        InitializeComponent();
    }


    private void meowBtn_Click(object sender, EventArgs e)
    {
        c.Meowing();
    }


    private void messageBoxMeowRadio_CheckedChanged(object sender, EventArgs e)
    {
        if (messageBoxMeowRadio.Checked)
            c.meowBehavior = new MessageBoxMeow();
    }


    private void consoleMeowRadio_CheckedChanged(object sender, EventArgs e)
    {
        if (consoleMeowRadio.Checked)
            c.meowBehavior = new ConsoleMeow();
    }
}

Code ข้างบนสรุปง่ายๆคือ ถ้าติ๊กอันไหน ให้เปลี่ยนการทำงานของ Cat.meowBehavior เป็นตามแต่ละแบบ และถ้ากดปุ่ม ก็จะเป็นการเรียก Cat.Meowing() ออกมา ไม่ว่าจะเป็นการร้องแบบไหนก็ตาม

ทดสอบ ก็จะได้ผลตามด้านล่าง

ถ้าใช้ MessageBoxMeow

ถ้าใช้ ConsoleMeow

ซึ่งจะเห็นได้ว่า การกระทำของ Cat นั้น สามารถเปลี่ยนแปลงไปได้ แม้ว่าจะอยู่ในช่วง Runtime ของโปรแกรมก็ตาม ซึ่งในความเป็นจริง การทำงานที่แตกต่างกันนั้น อาจไม่ใช่แค่บรรทัดเดียว แต่อาจเป็นการทำงานที่ซับซ้อนมากมายก็ได้ ซึ่งจะทำให้เราไม่ต้องแก้ไขโค้ดใหม่ แต่สามารถเปลี่ยนจากสิ่งที่ซับซ้อนสิ่งหนึ่ง ไปสู่สิ่งที่ซับซ้อนอีกสิ่ง ได้ด้วยโค้ดบรรทัดเดียว

ข้อดีอีกอย่างหนึ่งของ Strategy Pattern นั้น ก็คือช่วยเพิ่ม Maintainability เช่นสมมุติว่า เรามี Class ของแมวซัก 1000 สายพันธุ์ และ ในจำนวนนี้ มี 460 สายพันธุ์ ที่ใช้วิธีการร้องเหมือนกัน แต่คลาสแม่ ของแมว 460 นั้น บางชนิดก็มีวิธีการร้องต่างกันไป จึงไม่สามารถใช้การสืบทอดวิธีการร้องได้ เราจึงต้องโค้ดวิธีการร้องของแมวแต่ละสายพันธุ์ใหม่ทีละสายพันธุ์(อาจจะก็อปเอาก็ได้)

ปัญหาจะเกิดขึ้น เมื่อเราเกิดอยากจะแก้ไขวิธีการร้องของแมวทั้ง 460 ชนิดนั้น เราจะจำเป็นต้องเข้าไปเปิดโค้ดของแมวทั้ง 460 ชนิด แล้วค่อยๆแก้ทีละชนิด ซึ่งยากมาก แต่หากเราใช้ Strategy Pattern แล้ว หากเราต้องการเปลี่ยนการทำงานของการร้อง เราก็เพียงแค่เข้าไปแก้ไขโดยตรงที่ Class วิธีการร้องนั้นๆ แล้วทุก Class ที่เกี่ยวข้อง ก็จะเปลี่ยนไปตามวิธีที่เราต้องการ

สรุปแล้ว การที่เราจะแก้ไขการทำงานของ Class ขณะ Runtime นั้น วิธีการของ Strategy Pattern ก็เป็นหนึ่งในวิธีที่ดีมาก เพราะนอกจะช่วยทำให้เราสามารถแก้ไขการทำงานได้แล้ว วิธีการนี้ ยังคอยช่วยทำให้เราสามารถบำรุงรักษาโปรแกรมของเราได้อย่างดีอีกด้วย…

Leave a Reply

Your email address will not be published. Required fields are marked *