/ OOD

[365 วันแห่งโปรแกรม #day64] Strategy pattern

วันที่หกสิบสี่ของ ‪#‎365วันแห่งโปรแกรม และแล้วก็มาถึงแพทเทิร์นสุดท้ายในกลุ่ม Behavioral pattern ที่ผมจะพูดถึง ซึ่งก็คือ Strategy pattern


Strategy pattern

Strategy pattern คือแพทเทิร์นที่ยอมให้มีการเปลี่ยนแปลงหรือเลือก algorithm ได้ในขณะรันไทม์ โดย client จะเป็นผู้เลือก เช่น เลือกวิธีเปรียบเทียบ object ให้เหมาะสมกับชนิดของ item ใน list เลือกวิธี validate ข้อมูลประเภทต่างๆ เป็นต้น

การที่จะทำอย่างที่กล่าวมาได้ เราจำเป็นต้องแยกส่วนของ algorithm ออกไปจาก object หลัก แล้วค่อย inject เข้ามาใช้ในขณะที่ต้องการใช้ ในภาษาที่เป็น OO เรามักจะห่อ algorithm นั้นๆ ไว้ด้วย class (มีการกำหนด interface สำหรับการใช้งานไว้) เว้นแต่ในกรณีที่ภาษานั้นๆ รองรับ function pointer หรือมีพฤติกรรมแบบ first-class function (เหมือน Functional language) ก็อาจจะใช้วิธีสร้างเป็น function แทน

Implementation

Strategy pattern class diagram

จาก diagram ข้างต้น มีส่วนประกอบดังนี้

  • Strategy - เป็น interface ที่กำหนดวิธีการเรียกใช้ algorithm

  • ConcreteStrategy - เป็น class ที่ห่อหุ้ม algorithm ต่างๆ เอาไว้ โดยจะต้อง derived มาจาก Strategy

  • Context - เป็น class อื่นที่ถือ reference ของ strategy เอาไว้ เพื่อนำไปใช้

เพื่อให้เห็นภาพมากขึ้นผมขอยกตัวอย่างเป็นเรื่องของการ compare ข้อมูลใน list เพื่อใช้ในการเรียงลำดับ สมมติว่าผมมีคลาส ListSorter (Context) สำหรับใช้ในการเรียงลำดับข้อมูลใน list โดย client สามารถกำหนดวิธีการเปรียบเทียบ object เองได้โดยการสร้าง object ที่ implement จาก ICompareStrategy (Strategy)

public interface ICompareStrategy<E>
{
    int Compare(E a, E b);
}

public class ListSorter<E>
{
    public static void Sort(List<E> list, ICompareStrategy<E> strategy)
    {
        for (int i = 0; i < list.Count; i++)
        {
            for (int j = i + 1; j < list.Count; j++)
            {
                if (strategy.Compare(list[i], list[j]) > 0)
                {
                    var temp = list[i];

                    list[i] = list[j];

                    list[j] = temp;
                }
            }
        }
    }
}

ต่อมาสมมติว่าผมสร้าง class Product

public class Product
{
    public int Id { get; set; }
    public String Name { get; set; }
    public double Price { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

แล้วผมก็ต้องการสร้าง list ของ Product แล้ว sort ดังนั้นผมต้องสร้าง strategy สำหรับการเปรียบเทียบก่อน ดังนี้

public class CompareProductByIdStrategy : ICompareStrategy<Product>
{
    public int Compare(Product a, Product b)
    {
        return a.Id - b.Id;
    }
}

public class CompareProductByPriceStrategy : ICompareStrategy<Product>
{
    public int Compare(Product a, Product b)
    {
        return (int) Math.Round(a.Price - b.Price);
    }
}

สร้างไว้ทั้งหมด 2 strategy ครับ สำหรับเรียงตาม Id อันนึง และสำหรับเรียงตาม Price อีกอันนึง ต่อมาเราจะลองสร้าง list ของ Product แล้ว print ดูก่อน

Product orange = new Product { Id = 35, Name = "orange", Price = 10.50 };
Product banana = new Product { Id = 12, Name = "banana", Price = 8.75 };
Product apple = new Product { Id = 64, Name = "apple", Price = 18 };
Product mango = new Product { Id = 32, Name = "mango", Price = 11.12 };

var products = new List<Product>();
products.Add(orange);
products.Add(banana);
products.Add(apple);
products.Add(mango);

PrintList(products);

===output===

orange
banana
apple
mango

หลังจากนั้นสั่ง sort โดยใช้ CompareProductByIdStrategy แล้วสั่ง print

ListSorter<Product>.Sort(products, new CompareProductByIdStrategy());
PrintList(products);

===output===

banana
mango
orange
apple

จะเห็นว่ามีการเรียกลำดับใหม่ครับ หลังจากนั้นเราลองเปลี่ยน strategy เป็น CompareProductByPriceStrategy แล้ว print อีกครั้งครับ

ListSorter<Product>.Sort(products, new CompareProductByPriceStrategy());
PrintList(products);

===output===

banana
orange
mango
apple

จะเห็นว่าการเรียงลำดับเปลี่ยนไปจากเดิมครับ แสดงว่ามีการนำ strategy ที่เราสร้างไปใช้จริง

*ในภาษาต่างๆ นั้นมักมีการเตรียม interface สำหรับเปรียบเทียบ object ไว้ให้ใช้อยู่แล้ว ในตัวอย่างของผมเป็นแค่การแสดงให้เห็นว่ามัน implement ขึ้นมายังไง ไม่ควรนำไปใช้จริงครับ

References

Strategy pattern - Wikipedia

Strategy - OODesign

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