Class Diagram

🤔 ออกแบบคลาสต่างๆร่วมกันกับคนอื่นโดยไม่เขียนโค้ดเขาทำกันยังไง?

😢 ปัญหา

เวลาที่เราทำงานกับโปรเจคที่มีโครงสร้างขนาดใหญ่ หรือ ทำงานร่วมกับคนอื่นนั้นก็จะมีบ่อยครั้งที่เราจะต้องเขียนคลาสเพื่อทำงานร่วมกัน และบ่อยครั้งก็จะพบว่าเรากับเพื่อนตกลงกันไว้อย่างนึง แต่ทำออกมาได้อีกอย่างนึงทั้งๆที่คุยกันแล้วว่าจะต้องสร้างอะไรบ้าง แต่ที่ทำไม่ตรงกับที่คุยก็เพราะความเข้าใจในหัวแต่ละคนไม่เหมือนกันยังไงล่ะ แล้วเราจะแก้ปัญหาเหล่านี้ยังไงดี?

😄 วิธีแก้ปัญหา

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

แนะนำให้อ่าน บทความนี้เป็นส่วนหนึ่งของคอร์ส 👶 UML พื้นฐาน หากเพื่อนๆสนใจอยากดูรายละเอียดของ UML แต่ละตัวว่ามันมีอะไรบ้างก็สามารถกดลิงค์ที่ชื่อคอร์สเข้าไปดูได้เลยนะ หรือจะดูหมวดอื่นๆจาก side menu ก็ได้เน่อ

🤔 Class Diagram ใช้ยังไง?

สมมุติว่าเราต้องเขียนโปรแกรม Login ละกัน แล้วเราจะต้องออกแบบยังไงดีนะ? ดังนั้นเราจะลองให้ ดช.แมวน้ำ 🧔 เป็นคนไล่โครงสร้างแบบเร็วๆดูละกันนะ

😄 ลองเขียน Class Diagram กัน

โดยปรกติเวลาที่เราใช้ UML เราจะไม่เขียนโค้ดแต่เราจะวาดรูปเล่นกันครับ ดังนั้นมาลองไล่ตามสิ่งที่ ดช.แมวน้ำ จะวาดรูปให้ดูทีละขั้นตอน เพื่อที่จะมีโครงสร้างของระบบ Login ดูละกันนะ

🔥 Class & Field

🧔 สิ่งแรกที่ตัวระบบ Login จะต้องมีเลยก็คือคลาส LoginRequest เอาไว้เก็บข้อมูลที่ผู้ใช้จะป้อนเข้ามา เช่น Username กับ Password ดังนั้นเราก็จะวาดรูปแรกกัน

ด้านบนถ้าเอาไปเขียนโค้ดก็จะเป็นแบบนี้

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

🔥 Method & Parameter

🧔 ถัดไปเราก็จะมีคลาสที่เอาไว้ตรวจสอบว่าผลการ Login นั้นผ่านหรือเปล่าซึ่งจะตั้งชื่อคลาสนั้นว่า LoginHandler ละกัน และมันจะมี method 1 ตัวชื่อว่า CheckLogin ที่จะตรวจว่า ข้อมูลที่ผู้ใช้ส่งมาสามารถ login ได้หรือเปล่า ตามรูปด้านล่างเลย

ด้านบนถ้าเอาไปเขียนโค้ดก็จะเป็นแบบนี้

public class LoginHandler
{
    public bool CheckLogin(LoginRequest req)
    {
        // โค้ดตรวจสอบผลการเข้าสู่ระบบ
    }
}

🔥 Visibility

🧔 อ๋อเกือบลืมไป มันจะเก็บจำนวนครั้งที่มีคน Login แล้วไม่สำเร็จเอาไว้ด้วยนะ ซึ่งข้อมูลตัวนี้คนอื่นจะดูได้อย่างเดียว ดังนั้นวาดรูปเพิ่มอีกนิสสส

🔥 Composition (ความสัมพันธ์)

🧔 คราวนี้ในการ Login แต่ละครั้งเราจะต้องมีการเก็บบันทึกการเข้าใช้งานผ่านคลาส LoginLogger ด้วยนะ วาดๆๆ

ด้านบนถ้าเอาไปเขียนโค้ดก็จะเป็นแบบนี้ (ขอเขียนแค่ตัวที่ถูกเพิ่มเข้าไปนะ)

public class LoginHandler
{
    protected LoginLogger log;
    ...
}

🔥 Aggregation (ความสัมพันธ์)

🧔 อย่าลืมนะว่าในการเขียน Log นั้นมันจะต้องบันทึกลงฐานข้อมูลด้วย ดังนั้นเราก็จะมีคลาสที่ชื่อว่า SqlDatabase เอาไว้เชื่อมต่อจัดการกับ Sql database นั่นเอง วาดต่อๆ

Composition vs Aggregation ความสัมพันธ์ 2 แบบนี้ถ้าดูแล้วจะคล้ายกันมัน มันแค่ใช้อธิบายถึงความละเอียดอ่อนใน life cycle ของ object พวกนั้นกับตัวคลาสที่เป็นเจ้าของมันหรือที่เราเรียกว่า container ครับ

🔥 Generalization (ความสัมพันธ์)

🧔 อ๋อเกือบลืมไป เรามีการ Login แบบใช้ความปลอดภัยขั้นสูงด้วย โดยนอกจามันจะตรวจการ login แบบปรกติละมันจะยังตรวจเรื่องอื่นๆด้วย ดังนั้นเจ้าตัวนี้มันจะสืบทอดมาจาก LoginHandler เพื่อตรวจสอบตามปรกติและทำการตรวจสอบขั้นสูงต่อ ดังนั้นเราก็จะสร้างคลาส AdvancedLoginHandler เพิ่มเข้ามาตามมรูปด้านล่างเลย

ด้านบนถ้าเอาไปเขียนโค้ดก็จะเป็นแบบนี้

โค้ดนี้เป็นตัวอย่างที่ไม่ดีนะ เพราะไม่ได้ทำ virtual & override กับให้กับ CheckLogin เอาไว้ เพราะมันไม่ใช่ส่วนที่เราจะสอน

public class AdvancedLoginHandler : LoginHandler
{
    ...
    private bool checkFraud(string username)
    {
        // โค้ดตรวจสอบขั้นสูง
    } 
}

🔥 Interface class

🧔 ในคราวตัวโปรแกรมของเหลามันอาจจะมีการต่อ database หลายตัวนะ ดังนั้นเราจะต้องเขียน interface class ที่ชื่อว่า IDatabase เอาไว้บ้าง ตามรูป

🧔 ซึ่งเจ้า IDatabase ตัวนี้เราก็จะให้มันสามารถเขียน log ได้ละกันเลยขอเพิ่ม method เข้าไปให้มันนิดหน่อย

🔥 Realization (ความสัมพันธ์)

🧔 ถัดไปเราก็จะให้เจ้าคลาส SqlDatabase มาทำการ implement เจ้า IDatabase ไปซะ ซึ่งสิ่งนี้เราเรียกมันว่า Realization นั่นเอง ตามรูปด้านล่างเลย

หรือเราจะเขียนย่อๆแบบนี้ก็ได้เหมือนกัน ต่างกันแค่เราต้องกลับไปไล่ดูว่ามันมี members อะไรบ้างใน interface เท่านั้น

🔥 Abstract class & Method

🧔 ไหนเราลองเปลี่ยนเจ้า AdvancedLoginHandler ให้กลายเป็น abstract คลาสหน่อยละกันเพื่อรองรับการ login หลายๆแบบในอนาคต เราก็จะได้ภาพออกมาเป็นประมาณนี้

จากรูปถ้าแปลงเป็นโค้ดจะได้ประมาณนี้

public abstract class AdvancedLoginHandler : LoginHandler
{
    public abstract bool ValidateSpecification(string username);
}

Abstract class & method ของที่เป็น abstract เราจะใส่ <<abstract>> ระบุเอาไว้ก็ได้ และเราจะเขียนให้มันเป็นตัวเอียง

🤔 มีอย่างอื่นอีกไหม ?

ก่อนที่การออกแบบมันจะลงไปทะเลมากกว่าไปนี้ เพราะผมต้องยำความสามารถทุกอย่างลงไป ดังนั้นขอแยกส่วนที่เหลืออกมาไว้ตรงนี้ละกันนะ และขอบอกเลยว่ามันเขียนออกมาเป็นบทความเฉพาะเรื่องนี้ได้อีกเลยล่ะ ดังนั้นผมจะขออธิบายเฉพาะของที่ได้ใช้บ่อยๆก่อนละกันตามด้านล่างเลย

🔥 Static member

🧔 ถ้าภายในคลาส์ของเรามีอะไรที่เป็น static member ไม่ว่าจะเป็น field หรือ method ก็ตามก็สามารถใช้แผนภาพอธิบายมันได้เหมือนกันนะ โดยการขีดเส้นใต้มันลงไปนั่นเอง

จากภาพเมื่อแปลงเป็นโค้ดจะเขียนว่า

public class Awesome
{
    public static int GlobalMember;
}

🔥 Association (ความสัมพันธ์)

🧔 ในบางทีเราก็ขี้เกียจอธิบายความสัมพันธ์ว่าเป็นแบบ Aggregation หรือ Composition เราก็สามารถลากเส้นความสัมพันธ์ระหว่างคลาสลงไปได้ดื้อๆเลย เช่น อาจารย์ จะต้องไปสอนที่ ห้องเรียน เราก็สามารถเขียนความสัมพันธ์ของคลาสทั้งสองได้ว่า

🧔 และในบางครั้งเราก็จะเขียนทิศทางเพื่อระบุว่าใครสามารถ navigate ไปหาใครได้บ้างลงไปด้วย เช่น

Teacher สามารถ navigate ไป ClassRoom ได้ ส่วน ClassRoom อาจจะทำได้ก็ได้แค่เราไม่ระบุลงไป ตามรูปด้านล่าง

ClassRoom ไม่สามารถ navigate ไป Teacher ได้ ส่วน Teacher อาจจะทำได้ก็ได้แค่เราไม่ระบุลงไป ตามรูปด้านล่าง

Teacher สามารถ navigate ไป ClassRoom ได้ และ ClassRoom ไม่สามารถ navigate ไป Teacher ได้ ตามรูปด้านล่าง

ทั้ง Teacher และ ClassRoom สามารถ navigate หากันได้

ทั้ง Teacher และ ClassRoom ไม่สามารถ navigate หากันได้

Multiplicity

🧔 บนเส้นความสัมพันธ์เรายังสามารถระบุจำนวนของความสัมพันธ์ลงไปได้ด้วย เช่น อาจารย์จะสอนหรือไม่สอนก็ได้แต่ถ้าสอนต้องห้ามสอนเกิน 4 วิชา ส่วนห้องเรียนต้องมีอาจารย์สอนอย่างน้อย 1 คนเสมอ ก็จะเขียนเป็นแผนภาพได้เป็น

🔥 Dependency (ความสัมพันธ์)

🧔 ในบางทีคลาสแต่ละคลาสมันอาจเกิดความสัมธ์แบบชั่วคราวเกิดขึ้นก็ได้ เช่น ในตัวอย่างเราจะเห็นว่าคลาส LoginRequest มันถูกส่งเข้ามาเป็น parameter ให้กับ method ของคลาส LoginHandler เพื่อใช้ตรวจสอบว่า login ได้หรือไม่เท่านั้นเองแล้วก็จบไปไม่ได้ยุ่งเกี่ยวกับอีก ดังนั้นเราจะเขียนเป็นแผนภาพได้ว่า

😄 แผนภาพและโค้ดทั้งหมด

หากไม่ชัดให้กดที่รูปเพื่อดูเต็มๆได้นะ
public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

🎯 บทสรุป

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

Last updated

Was this helpful?