SOLID Principles คืออะไร ทำไมโปรแกรมเมอร์ทุกคนต้องรู้จัก

solid-principles-coding

SOLID Principles คือ 5 หลักการที่จะออกแบบ Object-Oriented Class โดยเป็นไปในแบบ Best Practice ที่โปรแกรมเมอร์ควรจะปฏิบัติตามในการวางโครงสร้างและออกแบบ Class ในการเขียนโปรแกรม

หลักการ  5 อย่างนี้จะช่วยให้เราสามารถเขียนโค้ดได้เป็นแบบมืออาชีพมากขึ้น และเข้าใจว่าทำไมเราถึงควรใช้ Software Design Pattern ต่างๆใน Project ของเรา ไม่ว่าจะเป็น MVC, MVVM, MVP หรือ Design Pattern อื่นๆ ดังนั้นใครที่ต้องทำงานกับโค้ดทุกวันก็ควรรู้จักหลักการดังกล่าวไว้

SOLID นั้นย่อมาจาก

  • The Single Responsibility Principle
  • The Open-Closed Principle
  • The Liskov Substitution Principle
  • The Interface Segregation Principle
  • The Dependency Inversion Principle

ในบทความนี้เราจะมาอธิบายว่าแต่ละหลักการของ SOLID Principles นั้นคืออะไรบ้างพร้อมทั้งยกตัวอย่างโค้ดให้เข้าใจได้ง่ายมากขึ้น โดยตัวอย่างโค้ดจะเป็นภาษา Kotlin 

Solid principles คือ

solid principles ข้อสำคัญ​, The Single Responsibility Principle

หลักการแรกกล่าวว่า “Class ใด Class หนึ่งควรจะมีหน้าที่เพียงหนึ่งอย่าง ดังนั้น ควรจะมีเพียงสาเหตุเดียวที่จะแก้ไขมัน”

ลองมายกตัวอย่างให้เข้าใจง่ายขึ้นกัน เวลามีการเปลี่ยนแปลงในโค้ดของเราเช่น Database Logic, UI Logic หรือเรื่องอื่นๆ การเปลี่ยนแปลงนั้นควรจะส่งผลกระทบต่อ Class เพียงแค่ Class เดียว หรือ ถ้าเป็น Class มี่เก็บข้อมูลวัตถุ (Model Class) เช่น Book class หรือ Student class ที่จะมี field ที่เป็นข้อมูลของวัตถุนั้นๆอยู่ Model Class นั้นก็ควรจะเปลี่ยนเมื่อข้อมูลของวัตถุนั้นมีการเปลี่ยนแปลงเท่านั้น

การปฏิบัติตามหลักการนี้มีประโยชน์ในการทำงานเป็นทีมอย่างมาก เช่น เวลาที่เราเห็น change จาก commit ใน Version Control จากเพื่อนร่วมงานเราจะรู้ทันทีว่าสาเหตุคร่าวๆของ Change ใน Class ที่อยู่ใน Commit นั้นคืออะไร เพราะแต่ละ Class จะมีเพียงหน้าที่เดียว เช่นเกี่ยวกับ UI หรือ หรือ เป็นเรื่อง Database ยิ่งไปกว่านั้นถ้าเราปฏิบัติตามหลักการนี้อย่างเข้มข้นแล้ว Merge Conflict ก็จะเกิดขึ้นน้อยลง และ เวลามี Merge Conflict เกิดขึ้นก็จากสามารถแก้ไขได้ง่ายอีกด้วย

ตัวอย่างข้อผิดพลาด

ทีนี้เราลองมาดูตัวอย่าง Class ที่ไม่ได้ปฏิบัติตาม Single Responsibility Principle กัน และดูว่าจะทำอย่างไรให้โค้ดของเราเป็นไปตจามหลักการนี้ โดยจะขอยกตัวโค้ดของระบบ Invoice ของร้านหนังสือ

นี่เป็น Class ธรรมดาที่เก็บข้อมูลของหนังสือ ที่มาดู Class ที่จะจัดการเรื่อง Invoice กัน

ก่อนจะไปต่ออยากให้ทึกคนลองอ่านโค้ดของ class นี้ดูว่ามัไม่เป็นไปตามหลัก Single Responsibility Principle อย่างไร

อย่างแรกเลย function printInvoice ที่มี Logic ของการ print invoice ดังนั้นถ้าเราต้องการที่จะแก้ Format ของการ Print Invoice เราก็ต้องมาแก้ที่ Invoice class ซึ่งไม่เป็นไปตามหลักการที่เรากล่าวไว้ข้างต้น ที่ว่า 

“Class ใด Class หนึ่งควรจะมีหน้าที่เพียงหนึ่งอย่าง ดังนั้น ควรจะมีเพียงสาเหตุเดียวที่จะแก้ไขมัน” ดังนั้นเราจึงไม่ควรมี class ซึ่งเก็บ Business logic ผสมกับ Printing logic ไว้ในที่เดียว

จาก Class ที่ยกตัวอย่างไปยังมีอีก function ที่ไม่ได้ปฏิตามหลักการ ก็คือ function saveToFile ซึ่งเก็บ Logic ของการ Persistence ไว้ ลองนึกดูในอนาคตเราอาจมีการ save ไปยัง database หรือ เรียก API เพื่อเก็บข้อมูลเหล่านี้ก็ได้

เพื่อที่จะทำให้ โครงสร้างของ Class ที่กล่าวมาเป็นไปตามหลักการ Single Responsibility Principle  เราต้องสร้าง class แยกออกมาสำหรับเก็บ Printing logic และ Persistence logic โดยเราจากสร้าง Class ชื่อว่า InvoicePrinter และ InvoicePersistence จากนั้นก็แยก function ออกมาไว้ใน class ที่เราสร้างขึ้

เท่านี้ class ของเราก็เป็นไปตามหลักการ Single Responsibility Principle แล้ว

The Open-Closed Principle

หลักการนี้กล่าวว่า classes should be open for extension and closed for modification. โดย extension หมายถึงการเพิ่ม functionality และ modification หมายการแก้ไขโค้ด ใน class

ดังนั้นการนี้ก็แปลได้ว่า เราควรจะสามารถที่จะเพิ่ม function ใหม่ได้โดยไม่แตะต้องโค้ด ใน Class นั้นๆ เพราะว่าเมื่อไรที่เรามีการแก้ไขโค้ด มันจะเสี่ยงต่อการสร้าง Bug ขึ้นมาใน Project แต่เราจะทำอย่างไรล่ะ? การทำตามหลักการ SOLID ข้อนี้สามารถทำได้โดยการใช้ interface และ abstract class โดยให้ class ของเรานั้น implement interface หรือ extend จาก abstract class การทำแบบนี้ทำให้ class อื่นที่จะติดต่อ class ดังกล่าวเห็น class นี้เป็น type ของ Interface. และเราสามารถส่ง implementation แบบไหนของ interface เข้าไปก็ได้

ทีนี้มาดูตัวอย่างกัน ขอยกตัวอย่างในเรื่อง invoice ต่อจากข้อแรก สมมุติว่าเราได้ Requirement ให้เพิ่มความสามารถในการ save invoice ลง Database ถ้าแบบไม่ได้ใช้หลักการ open-closed principle เราก็คงจะแก้ไข class InvoicePersistence ตามนี้

ถ้าเราลองออกแบบให้ class ของเราเป็นไปทำ open-closed priciple เราก็จะสามารถใช้ interface ช่วยและ refactor โค้ดออกมาได้ประมานนี้

ขั้นแรกเราเปลี่ยนให้ InvoicePersistence กลายเป็น Interface ที่มี function save และ ทุก implementation ของ interface นี้ก็สามารถ implement function save ของตัวเองได้อย่างอิสระ ทีนี้เราก็สร้าง DatabasePersistence และ FilePersistence ซึ่ง implement InvoicePersistence

เท่านี้ Persistence Logic ของเราก็จะต่อยอดได้ง่ายขึ้น ในอนาคตถ้าเราต้องการเพิ่มความสามารถในการ save ลง database แยกกัน ระหว่าง MySQL และ MongoDB เราก็สามารถทำได้ แต่บางคนอาจบอกว่าสร้าง Class แยกกันหลายๆตัว แล้วใส่ function save ไปโดยไม่ต้องมี interface ก็ได้นี่

สมมุติว่าในอนาคตเรามี Persistence class หลายแบบ เช่น มี BookPersistence class เพิ่มขึ้นมาและเราต้องสร้าง Persistence Manager เพื่อมาจัดการเหล่า Persistence class ทั้งหลาย

เมื่อเราทำแบบนี้เราสามารถส่ง implementation แบบไหนของ InvoicePersistence เข้าไปก็ได้โดย Persistence Manager ไม่ต้องสนว่าจะเป็น Database persistence, File persistence หรือ อื่นๆเลย

Liskov Substitution

หลักการนี้กล่าวว่า “subclass ต้องสามารถแทนที่ base class ของตัวมันได้”

ยกตัวอย่างให้เห็นภาพในกรณีที่ class B เป็น subclass ของ class A เราต้องสามารถส่ง class B ไปยัง method ที่ต้องการ class A ได้โดยไม่ทำให้เกิด bug หรือ output แปลกๆ

ที่กล่าวมานั้นเป็นไปตามหลักการเรื่อง inheritance ด้วยเพราะ subclass จะเพิ่มเติมความสามารถจาก Base class ของมันโดยไม่ทำให้ความสามารถของ class นั้นลดลง หลักการนี้ทำความเข้าใจไม่ยากแต่ในโค้ดจริงอาจมองเห็นความผิดพลาดได้ยาก อยากให้ดูตัวอย่างโค้ดเพื่อให้เห็นภาพกัน

ที่เห็นที่เป็น class ง่ายๆ โดยมี class Rectangle(สี่เหลี่ยม) เป็น parent class และ child class เป็น Square หรือ สี่เหลี่ยมจตุรัส เพราะสี่จัตุรัสคือ สี่เหลี่ยมที่มีด้านเท่ากัน กังเราได้ทำการแก้ตัว setter ของ width กับ height ใน class Square โดยเมื่อไรที่ตัวใดตัวหนึ่งโดน set property size ก็จะเปลี่ยนไปซึ่งทำ width และ height เปลี่ยนค่าตามไปด้วย แต่สิ่งที่เราได้ทำไปนั้นอาจ Violate หลักการข้อนี้ ทำไม?! ลองไปดูตัวอย่างต่อไปนี้กัน

สมมุติว่าทีมของเราได้มีการเขียน function getAreaTest ไว้ ถ้าอ่านโค้ดดูจะเห็นว่า Square object ของเรานั้น ทำให้ test นี้ run ไม่ผ่านเนื่องจากตอนที่ set width ให้ Sqaure object นั้นมันไปเปลี่ยนค่า height ด้วย 

Interface Segregation Principle

Segregation นั้นหมายถึง การทำให้แยกออกจากกัน และหลักการข้อนี้พูดถึงการแยก interface

หลักการนี้กล่าวว่า “การแยก Interface ออกตามการใช้งานนั้นดีกว่าการสร้าง general interface เพื่อใช้งานร่วมกันหลายๆ class” เพราะว่า  class ของเราไม่ควรจะโดนบังคับให้ Implement fucntion ที่ไม่ได้ใช้งาน

การให้ class ของเรา implement general interface และ ต้อง implement function ที่ไม่ได้ใช้งานนั้นจะไม่ดีการบำรุงรักษาโค้ดในระยะยาวถ้าอยากเห็นภาพที่ชัดเจนยิ่งขึ้นลองดูตัวอย่างโค้ดต่อไปนี้

ในที่นี้เราจะยกตัวอย่างถึงเรื่องระบบ จอดรถง่ายโดยมี function ดังที่เห็นแต่เมื่อเราต้องการ implement ระบบจอดรถฟรีล่ะ (FreeParkingLot)

อย่างที่เห็นว่า FreeParkingLot จะถูกบังคับให้ implement function ที่ไม่ได้ใช้งาน เช่น calculateFee และ doPayment ทางที่ดีคือเราควรสร้าง Interface แยกกันตามหลักการ Interface Segregation Principle โดยสร้างเป็น Interface ชื่อว่า FreeParkingLot กับ PaidParkingLot จะดีกว่า

solid principles ข้อสุดท้าย​, Dependency Inversion Principle

ข้อนี้กล่าวว่า class ของเราควรจะ ใช้ instance ที่ประกาศเป็น interface หรือ abstract class มากกว่าการระบุ class ไปเลย ซึ่งหลักการข้อนี้จะเกี่ยวข้องกับ Open-Closed Principle ที่ได้กล่าวไป ดูได้จาก class PersistenceManager ที่เราได้ implement ไป สังเกตว่าใน Constructor นั้นจะประกาศรับเป็น interface ทั้งหมดเลย

ถนอมสายตาด้วย ไฟเส้น LED

ทำไมไฟเส้น LED จึงช่วยถนอมสายตา? อ่านเพิ่มเติม ได้ที่นี่ 

ที่มา

  • โค้ดตัวอย่างและเนื้อหาส่วนใหญ่แปลมาจาก https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 
กล้องวงจรปิด-ยี่ห้อไหนดี

5 ปัจจัยสำคัญเลือกกล้องวงจรปิด กล้องวงจรปิด ยี่ห้อไหนดี 2021

ในยุคสมัยนี้มีกล้องวงจรปิดมากมายคำถามคือ เราจะเลือก กล้องวงจรปิด ยี่ห้อไหนดี ในปี 2021 นี้ บทความนี้พามาดู 5 ปัจจัยในการเลือกซื้อกล้องวงจรปิดได้อย่างโดนใจ...

Read More
โทรศัพท์มือถือ-ราคาไม่เกิน-5000

โทรศัพท์มือถือ ราคาไม่เกิน 5000 บาท ใช้เป็นเครื่องทดสอบก็ดี

โทรศัพท์มือถือ ราคาไม่เกิน 5000 สำหรับคนทำงานสายโปรแกรมเมอร์จะเอามาใช้เป็นเครื่องทดสอบก็ได้ โทรศัพท์ที่คุณต้องการ มีหลากหลายยี่ห้อให้เลือก ได้แก่ SAMSUNG, XiaoMi, Apple,...

Read More
เมาส์ไร้สาย ราคาดี

5 เมาส์ไร้สาย ราคาดี ถนอมข้อมือ สำหรับโปรแกรมเมอร์ และงาน office

เมาส์ไรสาย ราคาดี ถนอมข้อมือ สำหรับโปรแกรมเมอร์และงาน office ในการทำงานที่ต้องนั่งจับเมาส์ ทำงานทั้งวันอาจเจอปัญหาปวดหรือตึงบริเวณข้อมือหรือแขนได้ง่าย โพสนี้เราเลยจะพามาส่องรายการ Mouse ที่จะช่วยให้ตำแหน่งมือของเราอยู่ในลักษณะที่เป็นธรรมชาติมากขึ้นขณะทำงานกัน...

Read More

Leave a Comment

Your email address will not be published. Required fields are marked *