The Deception of Indirection

The Deception of Indirection
Photo by Ernest Brillo / Unsplash
We can solve any problem by introducing an extra level of indirection

Developer หลายๆ คนน่าจะคุ้นเคยกับ Theorem ข้างบนมาบ้างจาก Fundamental theorem of Software Engineering (FTSE) ซึ่งหลายๆ คนก็น่าจะเห็นด้วย เพราะมันฟังดูเข้าใจง่ายมาก แค่เพิ่ม Interface หรือ Wrapper ขึ้นมาแค่นั้น แต่จริงๆ แล้วนี่เป็นความเข้าใจคลาดเคลื่อนไปมาก เพราะส่วนใหญ่ Developer เราจะสับสนระหว่าง Abstraction กับ Indirection ถ้าไม่ระวังให้ดี และไอความสับสนนี่แหละที่ทำให้ code เราเปลี่ยนจาก clean ๆ กลายเป็น Spaghetti ได้เลย

Abstraction vs Indirection

ความเข้าใจผิดมากที่สุดของเรื่องนี้คือ การที่คิดว่าสองสิ่งนี้มันเป็นสิ่งเดียวกัน เรามาลองดูนิยามมันคร่าวๆ ของสองสิ่งนี้กันครับ

  • Abstraction is about "What" ตัว Abstraction ทำหน้าที่เป็นสัญญา ที่ซ่อนความซับซ้อน (complexity) ของการทำงานหนึ่งๆ ทุกครั้งที่เราใช้ Interface เป็นการบอกว่า "เราไม่สนนะว่าข้างในมันจะทำงานยังไง ตราบใดที่มันยังทำงานได้ตามกรอบที่เราวางไว้" เรียกว่าเป็นมุมมองแบบ Passive ก็ได้
  • Indirection is about "Where" ในส่วนของ Indirection สามารถอธิบายได้ง่ายๆ ว่าเป็นป้ายบอกทางมากกว่า โดยทำหน้าที่เป็นตัวกลาง (middleman) คอยบอกว่าให้เลี้ยวซ้าย เลี้ยวขวานะ ตาม Action ที่ได้รับมา หรือมองในอีกมุมของเป็นการตอบรับแบบ Active ครับ
    อีกจุดหนึ่งที่ทำให้ Indirection แตกต่างจาก Abstraction อย่างชัดเจนคือการต้องมี Registry ครับ ยกตัวอย่างกลับไปที่ Analogy ของป้ายบอกทางก็ได้ ถ้าเราอยู่ที่ 3 แยก มันต้องมีคนมาเขียนป้ายบอกว่า ถ้าเลี้ยวซ้ายไปไหน ถ้าเลี้ยวขวา แล้วจะไปถึงไหน นั่นแหละครับการ Registry ซึ่งนี่เป็นหลักการพื้นฐานไม่ต่างจากการที่เรามี DNS Resolver (เชื่อมระหว่าง Domain -> IP) หรือ built-in Python method อย่าง getattr() (เชื่อม String -> class method)

ทุกอย่างมีราคา

ย้อนกลับไปที่ FTSE ข้างบนครับ จริงๆ แล้วมันไม่จบแค่นั้น มันยังมีครึ่งหลังที่คนไม่ค่อยรู้อยู่ กล่าวไว้โดย David Wheeler ว่า

...except for the problem of too many levels of indirection

ลองจินตนาการภาพตามนะครับ ทุกครั้งที่เราเพิ่มของเข้ามาใน Indirection ตัว Register ที่ทำงานอยู่จะทำงานหนักขึ้นเรื่อยๆ ครับ และเราจะเริ่ม Debug ยากขึ้นเรื่อยๆ เพราะมันไม่ใช่การเรียก function ตรงไปตรงมา แต่เราต้องไปที่ Register ก่อนว่า action นี้หมายถึง function ไหน แล้วค่อยไล่ไปจากตรงนั้น แค่คิดก็ปวดหัวแล้วใช่มั้ยครับ กลับมาที่ Analogy ป้ายบอกทางครับ ตัวอย่างที่ชัดเจนมากก็คือ ห้าแยกลาดพร้าวนี่แหละครับ
เพราะฉะนั้นแล้วเนี่ย กฏข้อสำคัญที่เราควรจะพึงระลึกไว้เสมอคือ เราควรพึ่ง Indirection ก็ต่อเมื่อ Complexity ที่เกิดขึ้นนั้นน้อยกว่า การเรียก Action มันตรงๆ ครับ

เราจะเป็นคนควบคุมมันหรือจะเป็นคนที่ถูกมันควบคุม

ครั้งหน้าถ้าเราเขียนโค้ด ลองถามตัวเองสั้นๆ ครับว่า Logic ที่เรากำลัง Implement อยู่เนี่ย มันจะถูกใช้เฉพาะงานส่วนนี้โดยเฉพาะ หรือว่ามันสามารถยกระดับไปเป็นระบบที่ใช้กับงานที่หลากหลายได้ ซึ่งการเลือกใช้แนวคิด Indirection คือการเลือกที่จะ ไม่ตอบคำถามนี้ แต่เป็นการเลือกที่จะหาทางที่จะหาคำตอบให้กับคำถามนี้ได้ในอนาคต ครับ