Wednesday, 11 July 2012

Design Pattern - Decorator Pattern

เมื่อเราต้องการแก้ไขหรือเปลี่ยนแปลงความสามารถของ Object เรามักจะใช้วิธี Subclass ซึ่ง กระบวนการดังกล่าวจะเกิดขึ้นในขณะที่เรา Compile (ซึ่งนั่นหมายความว่าเราต้องมี Source Code ถึงจะสามารถ Compile ได้) แต่ถ้าหากเราไม่มี Source Code แล้วเราจะทำอย่างไร ?

Decorator Pattern คือวิธีการออกแบบให้เราสามารถแก้ไขหรือเพิ่ม Object Functionality ในขณะ Run Time (ซึ่งหมายความว่า เราไม่จำเป็นต้องมี Source Code) เดี๋ยวเราจะลองดูว่ามีวิธีการอย่างไร ก่อนอื่นมาดู UML Diagram ของ Decorator กันก่อนแล้วกันนะครับ

Decorator Pattern UML

จากใน Diagram เราจะเห็นว่า Decorator เป็น Class ที่ inherit มาจาก Component และภายในยังมี Child Object ที่ inherit มาจาก Component อยู่ด้วย ส่วน ConcreateDecorator จะ inherit มาจาก Decorator อีกทีหนึ่ง

หลักการของ Decorator Pattern ก็คือ เราจะสร้าง Class ขึ้นมาใหม่ ซึ่ง implement interface เดียวกันกับ Class ที่เราจะทำการ Decorate (เพราะเราต้องการให้ Class ที่สร้างขึ้นมาใหม่ มีคุณสมบัติเหมือน Class เดิม)  ซึ่งเราจะทำการ Wrap Class ที่เราต้อง Decorate ไว้ภายใน ซึ่งจะทำให้เราแก้ไข method ได้ตามที่เราต้องการ ในขณะที่เรายังคงสามารถเรียกใช้งานความสามารถของ Class เดิมผ่านทาง Class ที่เราได้ทำการ Wrap เอาไว้ก่อนหน้า ถ้ายังไม่เข้าใจตอนนี้ไม่เป็นไร เดี๋ยวดู Source Code แล้วจะเข้าใจมากขึ้นครับ ตัวอย่างที่นำมาแสดงดังต่อไปนี้เป็นการแก้ไข method ที่ชื่อ getIngredients() ของ class SimpleCoffee โดยผ่าน Class ที่ทำหน้าที่เป็น Decorator ที่ชื่อว่า CoffeeDecorator

เราจะเริ่มจากการสร้าง SimpleCoffee class โดย implment Coffee interface ดังต่อไปนี้

Coffee Interface และ Simple Coffee Class

ขั้นตอนต่อไป เราจะสร้าง Abstract Class ชื่อว่า CoffeeDecorator ขึ้นมา โดย implement Coffee interface (เพราะเราต้องการให้ CoffeeDecorator มีคุณสมบัติเหมือนกับ SimpleCoffee) ส่วน Decorator ที่จะนำไปใช้งาน (Concrete Class) เราจะทำการ inherit จาก CoffeeDecorator อีกทีหนึ่ง จากตัวอย่างที่จะแสดงต่อไปนี้ เราจะสร้าง Decorator ขึ้นมา 3 ตัว คือ Milk, Whip และ Sprinkles ซึ่ง Decorator แต่ละตัวจะทำให้เกิดการเปลี่ยน ingredient และ cost


จากตัวอย่าง เราจะเห็นว่า ใน Class ที่ทำหน้าที่เป็น Decorator มีการเปลี่ยนแปลงความสามารถไปจากเดิม ในขณะเดียวกันก็ยังคงสามารถเรียกใช้ความสามารถเดิมผ่าน super.getIngredients() และ super.getCost() (ซึ่งจะไปเรียก getIngredients() และ getCost() ของ class ที่ implement Coffee Interface ที่เราได้ทำการ Wrap เอาไว้ก่อนหน้า)

ต่อไปเป็นตัวอย่างการเรียกใช้งาน Decorator จะสังเกตุว่า เราสามารถเรียกใช้งาน Decorator ในลักษณะ chain กันไปเรื่อยๆได้

ตัวอย่างการเรียกใช้งาน Decorator

สังเกตุว่าการใช้ Decorator มีความยืดหยุ่นมากกว่าการใช้ Subclass เพราะว่าเราสามารถเปลี่ยนแปลงความสามารถของ Class ในขณะ Run Time ทำให้เราสามารถเพิ่มความสามารถโดยพิจารณาจากเงื่อนไขอื่นๆได้ อีกทั้งเรายังสามารถเปลี่ยนลำดับของการเรียกใช้งาน Decorator ได้อย่างอิสระ หรือเลือกที่จะใช้หรือไม่ใช้ Decorator อันไหนก็ได้ ซึ่งการใช้ Subclass จะไม่สามารถทำได้ ถือว่าเป็น Design Pattern ตัวนึงที่ผมชอบ ยังไงก็ลองเอาไปประยุกต์ใช้งานกันดูแล้วกันนะครับ

แหล่งที่มา: http://en.wikipedia.org/wiki/Decorator_pattern

No comments:

Post a Comment