Showerthought: On FP and OOP
Tags: functional, oop
Published: 2019-12-13 11:29:25

อันนี้จะเป็น Series Showerthought ซึ่งจะเขียนแบบร่ายยาวไม่ปูทางอะไรมาก ระบายความคิดออกมาเฉยๆ

-----------

ช่วงนี้เปิดเรียน Domain Driven design แบบสมัยเก่า ที่เขายังบริหาร Complex domain ด้วย Monolith กัน (ซึ่งดีมากๆ ได้วิธีคิดหลายอย่างที่ใช้ในการเขียนโค้ดให้ดีได้) แล้วมันเห็นพลังอย่างหนึ่งของหลักการ Object-oriented คือหลักการที่ว่า ให้เอา Operation ที่ทำกับ Data ไว้ที่เดียวกันใน Object

คือเวลา Codebase ใหญ่ๆ ถ้ามันมีหลักแบบนี้ เวลาเกิดปัญหาหรือจะแก้ไขโค้ดก็ตามง่ายไง สมมติว่ามีคนบอกว่าช่วยทำให้ User มีชื่อเล่นให้หน่อย ก็รู้ละว่าให้ไปดูแถวๆ User Object หรือถ้ามีคนบอกว่า User Login ไม่ได้ เราก็ไปหาตรง User

แต่พอเราไปวิธีนี้มากๆ เข้า บางครั้งจุดเด่นของระบบมันอยู่ที่ Operation เช่น ถ้าคุณสร้างของที่คล้ายๆ Sendgrid ขึ้นมา ตัว Operation ที่ชื่อว่าส่งอีเมล์ (SendEmail) มันจะเป็นศูนย์กลางของระบบ หรือถ้าทำระบบเคลมประกัน ตัว Operation การส่งเคลม ก็เป็นศูนย์กลางของระบบ

ถามว่าถ้าเราเอาไปกระจายว่าเรามี `ProductA.claim()` หรือ `ProductB.claim()` หรือ `ProductC.claim()` ไปทั่ว มันเข้าหลัก OOP นะ แต่แล้วเนื่องจากการเคลมประกันเป็นศูนย์กลางของระบบ ที่น่าจะถูกแก้ไข พัฒนา ปรับปรุง บ่อยมากๆ แล้วถ้าเราไปยึดกฎอีกว่า 1 Class = 1 File สิ่งที่เกิดขึ้นก็คือการแก้ไขระบบส่วนมากเราต้องเปิดไฟล์หลายๆ ไฟล์ แก้หลายๆ ที่ ดูหลายๆ จอพร้อมๆ กัน ซิงค์กันตลอด มันก็ทำงานลำบากกว่าที่ควรเป็น

แต่ถ้าใน FP Language ที่มี Pattern matching ผมยกตัวอย่าง Elixir ละกัน เราสามารถ

```elixir
defmodule Claim do
  def claim(%ProductA{} = product) do
  end

  def claim(%ProductB{} = product) do
  end

  def claim(%ProductC{} = product) do
  end
end
```

ซึ่งถ้าเราใช้ OOP เราจะโมเดลแบบนี้ได้มั้ย ได้สิ เราก็แค่หาคำนามที่แทนกริยาการ Claim มาเข้าระบบ ผมเอาคำว่าใบรับประกัน (Warranty certification) เข้ามา

```csharp
public class WarrantyCertification {
   // Something
   public void Claim(Product product) {
     // Claiming
   }
}
```

ทีนี้เราก็สามารถแรเงาขับเน้น Operation การเคลมได้แล้ว

-----------------------------

ในทางกลับกัน แม้แต่ใน Functional programming หลายๆ ครั้งเราก็ไม่ได้มีระบบที่ Operation ควรจะเป็นศูนย์กลาง เช่น ถ้าเราจะหาว่าเวลาเคลม ของต้องซ่อมหรือเปลี่ยนใหม่ หรือ reimburse ซึ่งขึ้นอยู่กับปัจจัยหลายๆ อย่างมากเลยต้องคำนวณมา

```elixir
defmodule xxxx do
   def get_compensate_operation(product, warranty) do
      # Here
   end
end
```

คำถามคือเราจะตั้งชื่อ Module xxx นี้ยังไงให้หาได้ง่ายๆ ???? ถ้าเราตั้งชื่อว่า CalculateWarranty แล้วระบบเราจะไม่เต็มไปด้วยโมดูลเล็กๆ ที่มีแค่ 1 ฟังก์ชั่น แล้วกลายเป็นว่ามีไฟล์เป็นพันๆ ไฟล์ หาของกันไม่เจอหรือเปล่า??

ซึ่ง OOP มอบกฎให้ว่า ไปวางไว้ใน Data ที่เกี่ยวข้อง กรณีนี้คือไปวางไว้ในใบ Class WarrantyCertification ได้ 

พอมีกฎนี้การหาของทุกอย่างก็ง่ายขึ้นในกรณีนี้ 

ซึ่งกรณีนี้ถ้าเราทำ Functional เราก็อาจจะต้องไปวางไว้ใน Warranty เหมือนกันนะ ผมคงสร้าง Module นั้นขึ้นมา

มันทำให้ตกผลึกว่า ถ้าใช้ภาษา OOP ต้องรู้ว่าตัวภาษามันมีแนวโน้มที่จะเน้นคำนาม บางครั้งถ้าเราต้องการเน้นคำกริยา เน้น Operation บางอย่าง เราต้องหาคำนามมาใส่ให้มันถึงจะเมคเซนส์ในภาษา OOP

และถ้าใช้ภาษา Functional หลักการที่ว่าเอา Operation มาใกล้ๆ กับ Data ก็ยังเป็นอะไรที่ช่วยได้ในกรณีทั่วไปจริงๆ ยกเว้นเคสที่เราต้องการเน้น Operation ให้มันเป็นศูนย์กลางการแก้ไขปรับปรุงของระบบ

การเลือกแรเงา ลงสี ขับเน้น แต่ส่วนของระบบ สร้าง Contrast สร้างความสำคัญ สร้างความ Trivia เป็นศิลปะในการออกแบบโค้ดเบสและการบริหารโปรแกรมเมอร์ เพราะถ้าโค้ดเบสตรงไหนถูกออกแบบมาให้ Trivia ก็จะไม่มีใครไปแตะ และถ้าตรงไหนถูกขับเน้น คนก็จะแตะมากขึ้น

นี่เป็นเครื่องมือการบริหารทีมโปรแกรมเมอร์ที่ดีมากและไม่ค่อยมีใครเข้าใจหรือพูดถึง