[EP1] บันทึกการลองออกแบบ Software Architecture ด้วย Python

Phachara Kamthong
4 min readMay 1, 2021

--

ในบทความนี้ผมจะมาเล่าประสบการณ์ที่ผมต้องลองวาง Software Architecture ที่มีเป้าหมายคือสามารถเพิ่มโค้ดสำหรับเชื่อมต่อระบบอื่น ๆ ได้ง่ายขึ้น

ผมคิดว่า Programming paradigms ที่เหมาะสมสำหรับนำมาวาง Structure ของตัวโค้ดจากโจทย์ที่ได้รับควรจะใช้ Object-oriented

ใน EP1 นี้ผมจะพูดถึงการแนวทางการเขียน OOP ในภาษา Python ก่อน หากสนใจอยากอ่านเกี่ยวกับประสบการณ์การออกแบบจริง ๆ จะไปอยู่ที่ EP2 นะครับ เนื่องจากอยากให้เข้าใจวิธีการเขียน OOP จากตัวโค้ดที่ผมจะนำมายกตัวอย่างก่อน

ความท้าทายแรกคือ…มันเขียนยังไงอะ?

ผมได้เรียน OOP มาสมัยมหาลัยด้วยภาษาจาวา ในความสัจจริงคือผมค่อนข้างรู้สึกอึดอัดกับความ Strict ของตัวภาษานี้มาก แต่ก็กลับมาเห็นข้อดีตอนเรียน Design pattern ว่าด้วยความ Strict ของมัน มันค่อนข้างที่จะทำให้โค้ดออกมาตรงตัวและ Explicit ออกมาแทบจะชัดเจน

แต่เนื่องด้วยผมต้องนำมาเขียนใน Python ที่ตอนนี้ผมได้ทำโปรเจคที่ใช้งาน django อยู่ ซึ่งตัว django ก็จะมีแนวทางการเขียนของมันและ paradigm ที่ใช้จะเน้นไปทาง functional มากกว่า

Photo by Anders Jildén on Unsplash

OOP Concept & Principle

หลังจากที่เวิ่นเว้อกันไปแล้วเรามาดู concept ของตัว OOP กันดีกว่าว่ามีอะไรบ้าง OOP เป็นการเขียนโปรแกรมเชิงวัตถุ ซึ่งวัตถุก็จะมีคุณสมบัติเช่น สุนัข ก็จะมีขา ตา หู และหาง ภาษาไทยอาจจะงงมันก็คือ attribute นี่แหละครับ ส่วนกริยาเช่น การเห่า การเดิน ก็คือ method

และตัว OOP จะประกอบด้วย 4 principle ด้วยกันคือ

1. Encapsulation

เป็นการห่อหุ้มไม่ให้เข้าถึงส่วนที่ไม่ได้รับอนุญาต ในคอนเซปของ encapsulation จะมีศัพท์ที่ได้ยินบ่อย ๆ คือ public และ private โดยสิ่งที่เราอนุญาตให้เข้าถึงจะเรียกว่า public และส่วนที่จะไม่ให้เข้าถึงเรียกว่า private สิ่งนี้ก็จะหมายถึงทั้ง public attribute, private attribute, public method, private method

** หมายเหตุ ** ใครที่มาจาก Java หรือภาษาอื่นที่ strict อาจจะสบถอะไรในใจก็ได้ สำหรับแนวทางการเขียนที่ใน python นี้

Public attribute

attribute ของคลาสจะสามารถประกาศตรงที่ตัวคลาสอย่างบรรทัดที่ 2 ก็ได้ หรือลบบรรทัดที่ 2 และทำแบบใน def __init__(self) ก็ได้ หากนำโค้ดไปลองพิมพ์ตามจะเห็นได้ว่าตัว Editor ที่เราใช้ จะทำการ Suggestion ตัว attribute ขึ้นมา ในกรณีนี้เราจะสามารถเข้าถึงได้เลย ซึ่งถือว่าเป็น public attribute

*เพิ่มเติม* สำหรับ def __init__(self) นี้คือ Constructor function ที่จะเรียกเป็นอันดับแรกเมื่อคลาสนี้ถูกสร้าง ถ้าเคยเขียน Java มาก่อน จะมีลักษณะแบบนี้ครับ

public class Dog {
private String name;
public Dog(String name){
this.name = name
}
}

ส่วน self.name ในที่นี้จะอ้างถึง attribute ที่ชื่อ name ในคลาสครับ

Private attribute

สำหรับ Private attribute นั้น ใน python จะใช้ __ (double underscore/dunder) นำหน้าชื่อตัวแปร จะทำให้ตัวแปรนั้นไม่สามารถ Access ได้ตรง ๆ แต่ถ้าหากไปเห็นการนำ _ มานำหน้าชื่อตัวแปร ให้เข้าใจไว้ว่า มันคือ Private ทิพย์ สำหรับให้ Python developer นั้นรู้กัน อ่านต่อเพิ่มเติมได้ที่ python — What is the meaning of single and double underscore before an object name? — Stack Overflow

*เพิ่มเติม* วิธีนี้จะเรียกว่า Name Mangling นะครับ

เมื่อเราเรียกใช้งานก็จะพบ Error ดังตัวอย่าง ในตัวอย่าง name อาจจะไม่ใช่สิ่งที่ควรจะ private จริง ๆ แต่ถ้ายกตัวอย่างเป็นสิ่งอื่นที่เราอยากให้การแก้ไขค่า attribute นี้ ต้องผ่านการประมวณผลก่อน เราจึงจำเป็นต้องเตรียม public method ไว้ให้

สิ่งที่ต้องเตรียมคือ Getter, Setter method ไว้ให้คนที่เรียกใช้สามารถเข้ามาแก้ไขค่าของตัว attribute ได้

def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
ใจเย็น ที่นี่เขาไม่ทำกันแบบนั้น

ใน Python เรามีสิ่งที่เรียกว่า @property สำหรับทำ Getter, Setter ให้โค้ดดูพรีเมี่ยม!

สิ่งที่ต้องทำคือเราจะใช้ property decorator บนตัว Getter function และสำหรับ Setter function เราจะอ้างอิงไปที่ตัว Getter function และเพิ่ม .setter หากดูที่การรันตัวโค้ด จะเห็นได้ว่าบรรทัดที่ 22 ตอนที่เราต้องการจะกำหนดค่าใหม่ให้ name มันจะเรียกไปที่บรรทัดที่ 13 จึงถูก print ตัว Setting name ออกมา

สำหรับรายละเอียดความเป็นมาและตัวอย่างเพิ่มเติมของ property decorator สามารถอ่านเพิ่มเติมได้ที่ Python @property: How to Use it and Why? — Programiz

2. Inheritance

เรียกได้ว่าเป็นการสืบทอดคุณสมบัติ เรียกได้ว่าพ่อมีอะไร ลูกจะมีตามพ่อ

จากตัวโค้ดจะเห็นว่า BraveDog ซึ่งเป็นคลาสลูกที่สืบทอดคุณสมบัติจากคลาส Dog จะมี name และ getter, setter ของ name โดยที่ไม่ต้องเขียนตัวโค้ดเลย นี่เรียกว่าการสืบทอดคุณสมบัติ (Inheraitance)

ไม่จำเป็นว่าลูกจะเป็นต้องมีทุกอย่างเหมือนพ่อ จะเห็นว่าเวลาเรา print ค่าของตัวลูกและพ่อออกมา (to string method) จะมีตัวลักษณะ text ที่ต่าง format กัน ในที่นี้เราจะเรียกว่าการเขียนทับ (method overriding) หากไปดูที่ตัวโค้ดจะเห็นว่าบรรทัดที่ 30–33 เราได้ประกาศชื่อ method ที่เหมือนกับที่พ่อมี สิ่งนี้คือ method overriding

3. Abstraction

ใช้ในการทำ Modeling หรือก็คือเป็นโครงสร้างให้กับคลาสอื่น ๆ โดยคนที่ทำการ Inheritance ตัว abstraction นี้ไป จำเป็นจะต้องสร้างโครงสร้างให้เหมือนกับตัว abstraction พูดง่าย ๆ ก็คือ ต้องมีชื่อ method ตามในเรื่องนี้เราจะได้ยินศัพท์ต่าง ๆ เช่น interface, abstract class และ concrete class

concrete class ก็คือคลาสปกตินี่แหละครับ ที่สามารถนำไปรัน หรือนำไปใช้งานได้โดยไม่เกิดปัญหา

abstract class คือคลาสที่ยังไม่สมบูรณ์ ยังไม่สามารถนำไปใช้ได้จนกว่าจะ Implement method ที่ยังเป็น abstract อยู่

interface คือคลาสที่เป็นโครงจริง ๆ ทุก method ในนี้จะประกอบด้วย abstract method ทั้งหมด

จากโค้ดจะมี 2 Version นะครับ Version ที่ 1 จะเป็นคลาสปกติที่เราเขียนเหมือนให้เป็นโครงสร้างให้คนนำไป แต่สำหรับ Version 1 นั้น หากคนที่นำไป Implement ไม่ได้สร้าง method bark ก็จะไม่เกิด Error อะไร อีกทั้งยังไม่ตรงตามวัตถุประสงค์ของ Abstraction ด้วย

โค้ดจึงเป็นไปในหน้าตาของ Version 2 เราจำเป็นจะต้อง เรียกใช้ abc module ย่อมาจาก Abstract Base Classes จากนั้นกำหนด metaclass=abc.ABCMeta ดังบรรทัดที่ 15 และสำหรับ method ที่ต้องการให้เป็น abstractmethod ต้องทำการประกาศ @abc.abstractmethod ด้านบนชื่อ method

จะเห็นได้ว่าหากนำไปรันโดยที่ BraveDog ไม่ได้ Implement method bark จะพบเออเร่อ ดังที่เห็นในรูป

4. Polymorphism

นิยามของมันคือ having many forms คือการทำให้มีหลายรูปร่าง หลักการนี้อาจจะนำตัว abstaction มาประยุกต์รวมกับ inheritance ก็ได้ หรือจะเป็น inheritance เดี่ยว ๆ ก็ได้ เช่น หากผมยกตัวอย่างเป็นเกม ผมบอกว่าตัวละคนในเกม จะประกอบไปด้วย HP, MP และสามารถเดินได้ อาชีพต่าง ๆ ก็จะมี HP, MP และเดินได้เช่นกัน อีกทั้งจะมีอาชีพประจำตัว พร้อมสกิลประจำตัว จะออกแบบได้ประมาณนี้

สำหรับ 4 Principle ของ OOP ในรูปแบบของ Python ก็ออกมาประมาณนี้ หากผิดพลาดประการใด ขออภัยมา ณ ที่นี้ด้วยนะครับ หากมีสิ่งใดอยากเสริมสามารถมาบอกผมได้ทุกเมื่อเลยนะครับ

สำหรับ Source code ทั้งหมด ผมอัพไว้ที่ Github เรียบร้อยแล้ว สามารถจิ้มได้ที่ลิ้งนี้เลยครับ

บทความนี้จะเกิดขึ้นไม่ได้ หากไม่ได้รับการสนับสนุนจาก depa และ คณะเทคโนโลยีสารสนเทศ มจธ. ที่มอบทุนการศึกษาเพชรพระจอมเกล้าเพื่อพัฒนาเทคโนโลยีและนวัตกรรมดิจิทัล ที่เตรียมความรู้สำหรับพร้อมทำงานให้นะครับ

Ref

--

--

Phachara Kamthong
Phachara Kamthong

No responses yet