ข้ามไปยังเนื้อหา

Hide Delegate and Remove Middle Man

Hide Delegate ใช้เมื่อ client เอื้อมผ่าน object หนึ่งไปเรียก method บน object ที่สอง — person.department.manager — ให้ object แรกมี method ของตัวเองที่ซ่อนการกระโดดนั้นไว้ client จะได้เรียกแค่ person.manager() และเลิกผูกกับโครงสร้างที่อยู่เบื้องหลัง person

Remove Middle Man คือท่ากลับกัน เมื่อ object หนึ่งแทบไม่ทำอะไรนอกจากส่งต่อการเรียกไปยัง delegate ครั้งแล้วครั้งเล่า ก็ปล่อยให้ client คุยกับ delegate ตรง ๆ แล้วลบ method ที่ส่งต่อทิ้ง

Hide Delegate รักษา Message Chains (Fowler บทที่ 3) และเป็นหน้าตาภาคปฏิบัติของ Law of Demeter (Lieberherr, Holland และ Riel, OOPSLA 1988) คือคุยกับเพื่อนของคุณ ไม่ใช่เพื่อนของเพื่อน สายยาว ๆ อย่าง a.getB().getC().doIt() มัดให้ caller ผูกกับความสัมพันธ์สองชั้นที่ไม่มีธุระต้องรู้ ส่วน smell ฝั่งตรงข้ามคือ Middle Man คือ class ที่ method เกือบทั้งหมดเขียนว่า “return delegate.something()” ส่งต่อเยอะจนตัวห่อไม่ได้เพิ่มอะไรเลย

Person ที่ผู้จัดการของพวกเขาถูกค้นพบผ่าน Department ของพวกเขา ก่อนหน้านี้ client นำทางผ่านสาย หลังจากนั้น Person ซ่อน delegate ไว้หลัง method manager() (ใช้ Remove Middle Man เพื่อย้อนกลับไปอีกทางเมื่อตัวห่อหยุดคุ้มกับการมีอยู่)

// Before — client walks person.department.manager
class Department {
constructor(public manager: string) {}
}
class Person {
constructor(public name: string, public department: Department) {}
}
const managerName = person.department.manager;
// After — Hide Delegate
class Department {
constructor(private _manager: string) {}
get manager(): string {
return this._manager;
}
}
class Person {
constructor(public name: string, private department: Department) {}
manager(): string {
return this.department.manager;
}
}
const managerName = person.manager();
flowchart LR
  subgraph Before["Before — caller walks the chain"]
    A["client"] --> B["person.department"]
    B --> C[".manager"]
  end
  subgraph After["After — Hide Delegate"]
    D["client"] --> E["person.manager()"]
    E -.->|"internally"| F["department.manager"]
  end
  Before -.->|"Hide Delegate"| After
  After -.->|"Remove Middle Man"| Before
Hide Delegate ห่อสายไว้ ส่วน Remove Middle Man แกะออก

Hide Delegate

  1. สำหรับแต่ละ method บน delegate ที่ client เอื้อมไปเรียก ให้เพิ่ม method ส่งต่อง่าย ๆ บน server (object ที่ client ถืออยู่แล้ว)
  2. อัปเดต client ให้เรียก method server ใหม่แทนการเดินไล่ตามสาย
  3. รัน test หลังแต่ละ method ที่คุณเปลี่ยนทิศทาง
  4. เมื่อไม่มี client ไล่เข้าไปหา delegate ตรง ๆ แล้ว ก็ซ่อน accessor ที่เปิดเผย delegate ได้เลย

Remove Middle Man (สิ่งตรงข้าม)

  1. เพิ่ม accessor บน server ที่คืนค่า delegate เอง
  2. สำหรับแต่ละ method ส่งต่อที่ไม่ได้เพิ่มคุณค่า ให้แก้ caller ให้เรียกตรงผ่าน accessor นั้นแทน
  3. รัน test ขณะที่คุณทำไป
  4. ลบ method ส่งต่อทิ้งเมื่อไม่เหลือ caller แล้ว

ซ่อน delegate เมื่อ client เอื้อมผ่าน object หนึ่งไปหาเพื่อนบ้านของ object นั้น เมื่อคุณอยากมีอิสระเปลี่ยนความสัมพันธ์ที่อยู่เบื้องหลังโดยไม่ทำให้ caller พังทั้งแถบ หรือเมื่อสายเรียกยาวจนอ่านเหมือนคำสั่งนำทาง

ความเสี่ยงคือทำเลยเถิด ซ่อนทุก delegate แล้ว server จะเต็มไปด้วย method ส่งต่อจนตัวเอง กลายเป็น Middle Man เสียเอง ตรงนั้นแหละคือจังหวะหยิบ Remove Middle Man มาใช้ แล้วปล่อยให้ client คุยกับ delegate อีกครั้ง ท่าทั้งสองเป็นปุ่มหมุน หมุนไปทางห่อหุ้มเมื่อโครงสร้างยังผันผวน หมุนไปทางตรงไปตรงมาเมื่อตัวห่อเป็นแค่สัญญาณรบกวน

Hide Delegate ช่วยให้คุณเคารพหลักการใด?
Hide Delegate ทำอะไรให้กับ client?
เมื่อใดที่คุณควรหยิบ Remove Middle Man มาใช้?
อะไรคือความสัมพันธ์ระหว่าง Hide Delegate และ Remove Middle Man?