Beyond Inheritance Part 2 : Push then Pull

Dew
Black Lens
Published in
2 min readJul 27, 2014
Newspaper

ผ่านไปอีกหนึ่งสัปดาห์หลังจากสรุป Head First Designs Patterns บทแรกไว้ในตอน Beyond Inheritance อาทิตย์นี้ก็ยังคงอ่านเรื่อยๆ จนจบบทที่สอง เลยสรุปไว้ต่อเป็น part2

บทนี้กล่าวถึงแพทเทิร์นที่ผมคิดว่าเป็นหนึ่งในแพทเทิร์นที่มีการใช้งานกันมากที่สุด แพทเทิร์นนั้นก็คือ Observer Pattern

หลักการพื้นฐานของ Observer Patterns ก็คือเมื่อเกิดเหตุการณ์ที่น่าสนใจเกิดขึ้นบนตัว Subject บรรดา Observer ทั้งหลายต้องได้รับการบอกให้ทราบถึงเหตุการณ์นั้น

ในหนังสือได้ยกตัวอย่างคลาสสิคคือเรื่องการสมัครสมาชิกรับหนังสือพิมพ์รายวัน กล่าวคือเมื่อเราต้องการรับหนังสือพิมพ์จากสำนักพิมพ์ (ให้สำนักพิมพ์เป็น Subject) เราก็ต้องไปสมัคร (register) เพื่อรับหนังสือพิมพ์ เมื่อสำนักพิมพ์ตีพิมพ์หนังสือพิมพ์ฉบับใหม่ออกมา ทางสำนักพิมพ์ก็จะจัดส่งหนังสือพิมพ์ (notify) มาให้เรา (ให้เราเป็น Observer) และเมื่อเราไม่ต้องการรับหนังสือพิมพ์นี้อีกแล้ว เราก็แค่ไปยกเลิกการรับหนังสือพิมพ์ (deregister) กับทางสำนักพิมพ์ หลังจากนี้ถึงจะมีหนังสือพิมพ์ฉบับใหม่ตีพิมพ์ออกมาทางสำนักพิมพ์ก็จะไม่จัดส่งให้กับเราอีกแล้ว

ขณะเดียวกันทางสำนักพิมพ์ก็ไม่ได้จำกัดว่าจะมีแค่เราเป็นลูกค้าของเขาเพียงรายเดียว ดังนั้นในแต่ละวันสำนักพิมพ์จะต้องทำการส่งหนังสือพิมพ์ให้กับลูกค้าหลายราย ลักษณะแบบนี้จึงเป็นความสัมพันธ์แบบ one-to-many

One To Many Relationship in Observer Pattern
One-To-Many Relationship in Observer Pattern

ในหนังสือได้อธิบาย Observer Pattern ไว้ว่า

The Observer Pattern

Defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically

ที่มีใช้อยู่ในด้านการเขียนโปรแกรมเราจะเห็นการใช้ Observer Pattern อยู่มากมาย เช่นการที่ UI element ส่งต่อ state ให้กับ listener หรือถ้าเจาะจงไปทางโมบายแอพหน่อย (ก็ผมเขียนโมบายแอพหนิ :-P) ฝั่ง Android ที่ชัดๆ ก็จะมี BroadcastReceiver ส่วน iOS ก็มี NSNotificationCenter

ถ้าเราจะนำ Observer Pattern ไปอิมพลีเมนท์เอง หลักๆ เราก็ต้องมีอินเทอร์เฟสที่ให้คลาส Observer นำไปอิมพลีเมนท์

Observer Pattern Class Diagram
Observer Pattern Class Diagram

หนังสือยก Design Principle เกี่ยวกับเรื่อง Loose Coupling มานำเสนอ เพราะการที่ Subject สามารถคุยกับ Observer แต่ละตัวได้โดยไม่ต้องสนใจว่า Observer แต่ละตัวจะเป็นคลาสอะไร ก็เข้าข่ายการอิมพลีเมนท์ตามหลัก Loose Coupling

Design Principle

Strive for loosely coupled designs between objects that interact.

คำขยายความเรื่อง Loose Coupling เพิ่มเติมจากหนังสือที่ผมชอบก็มี

  • When two objects are loosely coupled, they can interact, but have very little knowledge of each other.
  • Loosely coupled designs allow us to build flexible OO systems that can handle change because they minimise the interdependency between objects.

อีกประเด็นที่หนังสือพูดถึงก็คือเรื่อง Push และ Pull ในหนังสือบอกว่าการที่ Observers ได้รับการแจ้งเตือนจาก Subject เมื่อมีเหตุการณ์ที่น่าสนใจเกิดขึ้น ลักษณะแบบนี้ก็คือการ Push ข้อมูลมาให้ แต่ในทางกลับกันจะทำอย่างไรถ้า Observer บางตัวอยากไปดึงข้อมูลเพิ่มเติมในส่วนที่นอกเหนือจากการ push มาให้ กรณีนี้พูดอีกนัยหนึ่งก็คือจะทำอย่างไรถ้า Observer อยากเข้าไป pull ข้อมูลตรงๆ จากตัว subject เมื่อมีเหตุการณ์ที่น่าสนใจเกิดขึ้น

ประเด็นนี้ในหนังสือได้แนะนำคลาส Observable ซึ่งใช้ร่วมกันกับอินเทอร์เฟส Observer ในภาษา Java ทั้งสองตัวนี้เป็น “Built-in Observer Pattern” ของ Java ซึ่งเราสามารถใช้ได้โดยไม่ต้องมาอิมพลีเมนท์ Observer Pattern เองทั้งหมด (ดูตัวอย่างการใช้งานได้ที่นี่) และสิ่งที่สองตัวนี้มาเกี่ยวกับประเด็นเรื่อง Pull ก็ตรงที่เมธอด update ในอินเทอร์เฟส Observer มีการส่งพารามิเตอร์มาให้สองตัวคือตัว Observable และ data object จุดนี้ทำให้ Observer สามารถ pull ข้อมูลเพิ่มเติมได้ผ่านตัวแปร Observable ที่ส่งมา

จริงๆ ส่วนตัวผมคิดว่าประเด็นเรื่อง pull นี้เราสามารอิมพลีเมนท์เองก็ยังได้ โดยแค่เพิ่ม Subject มาเป็นหนึ่งในพารามิเตอร์ที่ส่งกลับมาให้ Observer เหมือนกับที่ Observable และ Observer ของ Java ทำให้ ซึ่งจริงๆ แล้วเรื่องนี้ในส่วนของ Cocoa Framework ของฝั่ง iOS ผมเห็นการเรียก callback แบบโยน Subject กลับมาให้ด้วยอยู่ทั่วไป จนผมคิดว่าน่าจะเป็น convention ของเขาด้วยซ้ำ

Observer Pattern ถึงจะมีประโยชน์และยังถูกนำไปใช้อย่างกว้างขวาง แต่อีกประเด็นที่ควรใส่ใจคือมันอาจทำให้เกิดปัญหาเรื่อง memory leak ได้ เนื่องจากการที่ obserever จะลบ reference ของตัวเองออกจาก subject ได้ ก็ต้องมีการเรียกเมธอดของ subject โดยตรง ซึ่งถ้าเกิด subject อ้างอิงถึง observer ด้วย strong reference แล้วเกิดเหตุการณ์ที่ observer ไม่สามารถมาลบตัวเองออกไปได้ ก็อาจส่งผลให้เกิด memory leak ทางแก้ที่ตรงไปตรงมาก็คือให้ subject อ้างอิงถึง observer ด้วย weak reference แทน strong reference

พบกันใหม่ใน part 3 กับ Decorator Pattern …

--

--