Decorator pattern คือรูปแบบการเขียนโปรแกรมให้สามารถเพิ่มความสามารถให้กับ object โดยไม่ต้องแก้ไข class โดยเราสามารถ instantiate new class เข้าไปยัง object ของ class เดิม เพื่อเพิ่มเติมความสามารถให้มากขึ้นโดยไม่ต้องแก้ไข code ของ class เดิมเลย
ดูตัวอย่างการเรียกใช้ Decorator pattern กันก่อน
ลองดู code ตัวอย่างการใช้งานกันก่อน โดยจะยกตัวอย่างคอมแบบประกอบ
สมมติว่าเรากำลังเขียน class ของร้านคอมที่มี คอมพิวเตอร์จัดชุดอยู่แล้วให้ลูกค้าสามารถเพิ่มอุปกรณ์ (upgrad)เข้ามาได้ ถ้าหากจะใช้ pattern การเรียกใช้งาน class / object จะเป็นดังนี้
DefaultComputerSet1 basicComputer = new DefaultComputerSet1();
ComputerUpgrade upgradeSet = new ComputerUpgrade(basicComputer);
upgradeSet = new GeforceRTX2080(upgradeSet);
upgradeSet = new CreativeSoundAE7(upgradeSet);
Console.WriteLine("Description : " + upgradeSet.GetDescription());
Console.WriteLine("Price : " + upgradeSet.GetPrice());
ผลลัพท์
Summary : DefaultComputerSet 1 + Geforce RTX 2080 + Creative Sound Blaster AE7
Price : 83900
จาก Code ด้านบนจะเห็นว่า object upgradeSet จะมีการประกาศ new Class เพิ่มเข้ามาได้เรื่อยๆ (new GeforceRTX2080, new CreativeSoundAE7…..)
แล้วสุดท้ายเราก็เรียกใช้ getDescription, getPrice เพื่อดูว่าเลือกอุปกร์อะไรไป มันก็จะรวมข้อมูล description, price จาก ทุกๆ class ที่เรา add เข้าไปใน object ได้
จุดสังเกตุของ pattern นี้จะเห็นว่าเราไม่ต้องไปแก้ class เดิมเลย (DefaultComputerSet1) แต่เราจะ เพิ่ม class เข้าไปเรื่อยๆ ถ้าหากมีอุปกรณ์ใหม่ๆ เป็นร้อยเป็นพัน มันก็จะไม่มีผลกระทบต่อ class เดิมด้วย นี่คือข้อดีของ pattern นี้
Decorator pattern code example
Code ด้านล่างประกอบด้วย Class และ interface ดังนี้
IComputer : เป็น interface ที่ใครสืบทอดไปก็ตามจะต้อง implement method : getDescription และ getPrice
DefaultComputerSet : เป็น class ที่กำหนด set ตั้งต้น และราคา เหมือนเวลาที่ร้านคอมมีคอมวางไว้หน้าร้าน แล้วถามว่าเราจะ upgrade หรือเปล่า
ComputerUpgrade : เป็น abstract class ของอุปกรณ์ upgrade ต่างๆ
GeforceRTX2080 : เป็น class ที่สืบต่อมาจาก ComputerUpgrade จะใช้ instantiate เพิ่มเข้าไปใน basicComputer object ในกรณีสมมติว่าจะซื้อการ์ดจอด้วย
CreativeSoundAE7 : เป็น class ที่สืบต่อมาจาก ComputerUpgrade จะใช้ instantiate เพิ่มเข้าไปใน basicComputer object ในกรณีสมมติว่าจะซื้อ sound card ด้วย
using System;
using AppKit;
using Foundation;
namespace test2
{
public interface IComputer
{
string GetDescription();
float GetPrice();
}
public class DefaultComputerSet1 : IComputer {
public string GetDescription()
{
return "Default computer set 1";
}
public float GetPrice()
{
return 30000f;
}
}
public abstract class ComputerUpgrade : IComputer
{
private readonly IComputer _computer;
public ComputerUpgrade(IComputer computer)
{
_computer = computer;
}
public virtual string GetDescription()
{
return _computer.GetDescription();
}
public virtual float GetPrice()
{
return _computer.GetPrice();
}
}
public class GeforceRTX2080 : ComputerUpgrade{
public GeforceRTX2080(IComputer computer) : base(computer)
{
}
public override string GetDescription()
{
return base.GetDescription() + " + Geforce RTX 2080";
}
public override float GetPrice()
{
return base.GetPrice() + 47900f;
}
}
public class CreativeSoundAE7 : ComputerUpgrade
{
public CreativeSoundAE7(IComputer computer) : base(computer)
{
}
public override string GetDescription()
{
return base.GetDescription() + " + Creative Sound Blaster AE7";
}
public override float GetPrice()
{
return base.GetPrice() + 6000f;
}
}
public partial class ViewController : NSViewController
{
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Do any additional setup after loading the view.
}
partial void ClickedButton(NSObject sender)
{
DefaultComputerSet1 basicComputer = new DefaultComputerSet1();
ComputerUpgrade upgradeSet = new GeforceRTX2080(basicComputer);
upgradeSet = new CreativeSoundAE7(upgradeSet);
Console.WriteLine("Description : " + upgradeSet.GetDescription());
Console.WriteLine("Price : " + upgradeSet.GetPrice());
}
public override NSObject RepresentedObject
{
get
{
return base.RepresentedObject;
}
set
{
base.RepresentedObject = value;
// Update the view, if already loaded.
}
}
}
}
ทิ้งท้าย
Pattern นี้มีประโยชน์ในกรณีที่อยากเพิ่มความสามารถ/ behavior แต่ไม่ไปยุ่งกับ class เดิม โดยเราจะทำการ instantiate class ใหม่เข้าไปใน object เดิม ทำให้ code ที่เขียนขึ้นมีคุณสมบัติ open/close ตรงตาม SOLID Principle