Saladpuk.com
🏆 เนื้อหาหลัก
🏆 เนื้อหาหลัก
  • 💖สลัดผัก
  • 📰มีอะไรใหม่บ้าง
    • 2020
      • 2020-11
      • 2020-10
      • 2020-09
      • 2020-08
      • 2020-03
      • 2020-02
      • 2020-01
    • 2019
      • 2019-12
      • 2019-11
      • 2019-10
      • 2019-09
      • 2019-08
  • 🤔อ่านเรื่องไรดี ?
  • มือใหม่หัดเขียนโค้ด
    • 👶เขียนโค้ดด้วยภาษา C#
      • เกิดมาไม่เคยเขียนโค้ดมาก่อนเบย
      • 👶พื้นฐาน
        • 1.โปรแกรมที่ต้องลง
        • 2.โครงสร้างของโค้ด
        • 3.ชนิดของข้อมูล
        • 4.การสร้างตัวแปร
        • 5.คำสั่งพื้นฐาน
        • 6.การแปลงข้อมูล
        • 7.การเปรียบเทียบค่า
        • 8.การตัดสินใจด้วย IF statements
        • 9.การตัดสินใจด้วย Switch statements
        • 10.การทำงานซ้ำๆด้วย While
        • 11.การทำงานซ้ำๆด้วย Do While
        • 12.การทำงานซ้ำๆด้วย For
        • 13.การแก้โจทย์จากรูป
        • 14.มารู้จักกับ Array กัน
      • 🧑ระดับกลาง
        • 15.Value type vs Reference type
        • 16.ลดงานซ้ำๆด้วย Method
        • 17.มารู้จักกับ Class & Field กัน
        • 18.มารู้จักกับ Constructor กันบ้าง
        • 19.มาเขียน Method ใน Class กัน
        • 20.มารู้จักกับ Property กัน
        • 21.ลองใช้คลาสแบบจริงจังบ้าง
        • 22.การสืบทอด Inheritance
        • 23.Polymorphism
        • 24.Abstract Class
        • 25.Interface
        • 26.Namespace
        • 27.Enum
        • 28.Exception handler
        • 29.ลงลึกกับ string
        • 30.StringBuilder เพื่อนคู่ string
      • 👨⏳ระดับสูง
        • Generic
        • Delegates
        • Action & Func
        • Lambda expression
        • LINQ
        • พระคัมภีร์การใช้คำสั่ง LINQ
      • 💡Tips
        • 💡C# version 8.0
        • 💡Boxing & Unboxing
    • 👶Algorithm
      • 👾Algorithm Big-O
      • 👽Algorithm P & NP
    • 👦OOP
      • 💖Abstraction
      • 💖Encapsulation
      • 🏆Abstraction & Encapsulation
      • 💖Inheritance
      • 💖Polymorphism
      • 🏆Inheritance & Polymorphism
      • 📝ลองเขียน OOP ดูดิ๊
      • 👑OOP + Power of Design
      • 🥰เทคนิคในการออกแบบ
    • 👶บทสรุปฐานข้อมูล
      • เก็บรูปในฐานข้อมูล
      • Database indexing
      • การลบข้อมูล
    • 👦Communication Patterns
    • 👦Design Patterns
      • 🤰Creational Patterns
        • 🏭Factory Method
        • 🏭Abstract Factory
        • ☝️ Singleton Pattern
        • 🏗️ Builder Pattern
        • 🎎Prototype Pattern
      • 🧱Structural Patterns
        • 🔌Adapter Pattern
        • 📪Proxy Pattern
  • Puzzle
    • 🧠Challenges
      • 🐴Google ม้า 25 ตัว
      • 🌉Amazon เสา 2 ต้น
      • 🥇ทองเก๊
      • 💊ยาต้านโควิด
      • 🎩CP หมวก 5 ใบ
      • 🧓Einstein's Riddle 01
  • พื้นฐานที่ควรต้องรู้
    • 🐳Docker
      • 📦Docker Containers
      • 🃏Docker Exercise 01
      • 🛠️ Docker Tools
      • 🗃️ Docker Registry
      • 🖼️ Container Image
      • 📢Docker Push
      • 🔄WSL
    • 👶Clean Code
      • 🧓Uncle Bob - Clean Code
      • 🧓Uncle Bob - Comments
      • 🧓Uncle Bob - Naming
      • 🧓Uncle Bob - Mindset
      • 🧓Uncle Bob - TDD
    • 👶Code Smells
    • 👶สิ่งที่คนเขียนโค้ดมักเข้าใจผิด
    • 👶AI พื้นฐาน
    • 👶Git พื้นฐาน
      • Git branching strategy
    • 👶Cloud พื้นฐาน
    • 👶UML พื้นฐาน
      • Activity Diagram
      • Class Diagram
      • Sequence Diagram
      • Use case Diagram
      • บทสรุปการใช้ UML
    • 👶Data Scientist
      • การเลือก Algorithms ให้ AI (1/5)
      • การเตรียมข้อมูลให้ AI (2/5)
      • หลักการตั้งคำถามให้ AI (3/5)
      • แฉความลับของ AI Model (4/5)
      • หัดเขียน AI จาก AI ของคนอื่น (5/5)
    • 👶DevOps พื้นฐาน
    • 👶Docker ขั้นพื้นฐาน
      • Image and Container
      • แชร์ Docker Image ที่สร้างไว้
    • 👶Microservices พื้นฐาน
      • Microservices ที่ดีมีลักษณะยังไง
      • Microservices Tips
      • จาก Monolith สู่ Microservices
    • 👶ความรู้พื้นฐานในการทำเว็บ
    • 👦Bottlenecks of Software
      • หัวใจที่สำคัญที่สุดของฐานข้อมูล
    • 👦Agile Methodology
      • Agile in a Nutshell
      • Software Development Life Cycle
      • Code Review
    • 👦Security พื้นฐาน
      • การเก็บรหัสผ่านที่ถูกต้อง
      • Security in actions
        • Hash function
      • Security Principles
      • 😎The Matrix 1
      • 😎The Matrix 2
      • HTTPS in a nutshell
    • 👦SOLID Design Principles
      • มารู้จักกับ SOLID กันดีกว่า
      • Single-Responsibility Principle
      • Open/Closed Principle
      • Liskov Substitution Principle
      • Interface Segregation Principle
      • Dependency-Inversion Principle
  • Cloud Computing
    • 👶Microsoft Azure 101
      • สมัคร Microsoft Azure
      • รู้จักกับ Resource Groups
      • สร้างเว็บตัวแรกกัน
      • สร้าง Virtual Machine กัน
      • ประเภทของคลาว์เซอร์วิส
      • มาสร้าง Logic App กัน
      • มาสร้าง Function App กัน
      • คลาว์คิดเงินยังไง ?
      • Cloud Native
      • Guideline for Cloud scaling
      • Auto Scaling
    • 👶Azure App Services
    • 👶App Service Plan
    • 👶Azure Storage
      • Blob storage
        • ลองสร้างที่เก็บไฟล์กันเลย
        • เข้าใจ Blob storage ให้มากขึ้น
        • ลองเขียนโค้ดอัพโหลดไฟล์กันบ้าง
        • สร้างเว็บจากที่ฝากไฟล์บนคลาว์
    • 👶Azure Bot Service
      • Bot เข้าใจเราได้ยังไงกันนะ
    • 👶Azure Cognitive Services
      • การสร้าง Cognitive Services
      • การ Login ด้วยใบหน้า
      • อ่านลายมือจากรูปเป็นตัวอักษร (OCR)
      • เขียน AI แยกของต่างๆทำยังไง?
      • เขียนแอพ ทายอายุ บอกเพศ ง่ายจิ๊ดเดียว
      • เขียนแอพให้ AI อธิบายรูปเป็นภาษาคน
    • 👶Machine Learning Studio
      • มาสร้าง AI ของแท้ตัวแรกของเรากัน
      • สร้าง AI ตัดสินใจอนุมัติบัตรเครดิต 💳
      • ลองเรียกใช้ AI ของเรากัน
    • 👶Azure Service Fabric
      • สร้าง Service Fabric กัน
    • 👶Blockchain
      • Blockchain ทำงานยังไง ?
      • Consensus Algorithm คืออะไร ?
      • สร้าง Blockchain ใช้เองกัน !
      • หัดเขียน Smart Contract กัน
    • 👶Power BI
    • 👶Azure Web App
      • เซิฟเวอร์บนคลาว์ ราคา? ต่าง?
    • 👶Azure DevOps
      • เล่น Azure DevOps กัน
      • เล่นกับ Repository
      • ลองทำ Continuous Integration (CI)
      • ลองทำ Continuous Delivery (CD)
      • เล่น Kanban Board
    • 🤠Cloud Playground
      • การป้องกันความลับหลุดตอนที่ 1
      • การป้องกันความลับหลุดตอนที่ 2
      • การป้องกันความลับหลุดตอนที่ 3
      • การป้องกันความลับหลุดตอนจบ
  • Software Testing
    • 👦Test-First Design
    • 👦Test-Driven Development
      • 1.มารู้จักกับ TDD กันดีกว่า
      • 2.Test cases เขาเขียนกันยังไงนะ
      • 3.เครื่องมือในการทดสอบ
      • 4.การใช้ Theory และ InlineData
      • 5.โค้ดที่ทดสอบได้
      • 6.Mantra of TDD
      • 7.Functional & None-Functional testing
      • 8.Manual vs Automation testing
      • 9.Automation Frameworks in .NET
      • 10.Mock Framework
      • 11.มาเรียนการใช้ Moq กันเถอะ
      • 12.สรุป
  • Web
    • 👦Web API
      • 1.Web API คืออะไร
      • 2.ติดตั้ง .NET Core SDK
      • 3.สร้าง Web API ตัวแรกกัน
      • 4.Verbs
      • 5.Swagger เพื่อคู่ API
      • 6.การใช้ Model
      • 7.เรียก Web API ผ่าน Postman
      • 8.มาจัดกลุ่ม API กัน (1/2)
      • 9.มาจัดกลุ่ม API กัน (2/2)
  • Software Design
    • 🤴Design Patterns
      • 🦈Creational patterns
        • Abstract Factory
        • Builder
        • Factory Method
        • Prototype
        • Singleton
      • 🦈Structural patterns
        • Adapter
        • Bridge
        • Decorator
        • Facade
        • Proxy
      • 🦈Behavioral patterns
        • Chain of Responsibility
        • Command
        • Iterator
        • Mediator
        • Memento
        • Observer
        • State
        • Strategy
        • Template Method
        • Visitor
Powered by GitBook
On this page
  • 🧐 โจทย์ 01
  • 🧒 แก้โจทย์
  • 🧐 โจทย์ 02
  • 🧒 แก้โจทย์
  • 🧐 โจทย์ 03
  • 🧒 แก้โจทย์
  • 🧐 โจทย์ 04
  • 🧒 แก้โจทย์
  • ตัวอย่างโค้ดทั้งหมด
  • 🎯 บทสรุป

Was this helpful?

Export as PDF
  1. มือใหม่หัดเขียนโค้ด
  2. OOP

ลองเขียน OOP ดูดิ๊

🤔 เวลาเขาเอาหลัก Object-Oriented Programming มาใช้จริงๆมันเป็นยังไงนะ ?

PreviousInheritance & PolymorphismNextOOP + Power of Design

Last updated 5 years ago

Was this helpful?

หลังจากที่เราได้เห็นหัวใจหลักของ OOP ไปเรียบร้อยแล้ว ดังนั้นในรอบนี้เราจะมาดูกันว่าเวลาที่ไปเจอโจทย์เราจะนำ OOP มาใช้แก้ปัญหาของเราได้ยังไง

แนะนำให้อ่าน หัวใจหลักของ OOP มีทั้งหมด 4 อย่างคือ , , และ ถ้าสนใจอยากทบทวนเรื่องไหรก็จิ้มไปอ่านที่ชื่อมันได้เลย หรือจะไปดูจากเมนูด้านซ้ายมือก็ได้

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

🧐 โจทย์ 01

มีบริษัทมาจ้างเขียน เกมออนไลน์ ที่มีแค่ตัวละครเดียวคือ เด็กฝึกหัด (Novice) ซึ่งมันจะมี พลังชีวิต, พลังโจมตี, ประสบการณ์ที่จะใช้ในการอัพเลเวล และสามารถ เดิน, นั่ง, โจมตี และตายได้ ประมาณนี้ เราจะเขียนออกมายังไงดี ?

🧒 แก้โจทย์

ในการคิดแบบ OOP เราจะต้องแปลง ปัญหา ให้มาอยู่ในรูปแบบของ Model เสียก่อน โดยใช้หลักการของ ดังนั้นเพื่อให้เข้าใจตรงกันผมกำลังจะสร้าง Model เพื่อใช้ในการเขียนโค้ด โดยมันจะต้องมี Properties และ Behaviors ต่างๆประมาณนี้

ดังนั้นผมก็จะสร้าง Model ออกมา ซึ่งมีหน้าตาประมาณนี้

public class Novice
{
    public int HP { get; set; }
    public int Exp { get; set; }
    public int Atk { get; set; }
    public int Level { get; set; }
    public string Status { get; set; }

    public void Walk() { }
    public void Sit() { }
    public void Attack() { }
    public void Dead() { }
}
  • ถ้าพลังชีวิตถูกลดลงมันจะต้องห้ามต่ำกว่า 0 และถ้าพลังชีวิตหมดก็แสดงว่าตัวละครจะต้องตาย

  • ถ้าได้รับค่าประสบการณ์เกิน 100 ให้ตัวละครเลเวลอัพได้เลย

  • พวก Properties ต่างๆต้องไม่ถูกแก้ได้มั่วซั่ว

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

เลยเขียนโค้ดจัดการเรื่อง พลังชีวิต ก่อน

public class Novice
{
    private int hp;
    public int HP
    {
        get => hp;
        set
        {
            var hpTemp = hp;
            hp -= value;
            if (hp <= 0)
            {
                hp = 0;
                Status = "Dead";
                Dead();
            }
            else
            {
                hp = hpTemp;
                Status = "Alive";
            }
        }
    }

    ...
}

ตามด้วยจัดการ ค่าประสบการณ์ ที่ทำให้เลเวลอัพได้ยังไง

public class Novice
{
    private int exp;
    public int Exp
    {
        get => exp;
        set
        {
            var expTemp = exp;
            expTemp += value;
            if (expTemp >= 100)
            {
                exp = 0;
                Level++;
            }
            else
            {
                exp = expTemp;
            }
        }
    }

    ...
}

สุดท้าย Properties ต่างๆก็อย่าให้คนอื่นแก้ได้มั่วซั่ว และทำการกำหนดค่าเริ่มต้นซะ

public class Novice
{    
    public int Atk { get; private set; }
    public int Level { get; private set; }
    public string Status { get; private set; } = "Alive";

    public Novice()
    {
        HP = 100;
        Atk = 3;
    }

    ...
}

จาดโค้ดด้านบนก็จะเห็นแล้วว่า ของหลายๆอย่างเราซ่อนความวุ่นวายที่ภายนอกไม่จำเป็นต้องรู้ไว้ได้หมดแล้ว โดยการใช้ Encapsulation มาช่วย ซึ่งถ้าเราออกแบบ encapsulation ออกมาได้ดีมันก็จะช่วย เติมเต็ม ความเป็น Abstraction ได้มากขึ้น

Abstraction + Encapsulation จะซ่อนความวุ่นวายทั้งหลายไว้ เหลือแค่เพียงความเรียบง่ายในการใช้งาน เพราะหลักการคือการสร้าง Component ที่มันรับผิดชอบตัวเองได้นั่นเอง

ดังนั้นพอลองวาดภาพดูจะเห็นว่า Model ที่เราสร้างขึ้นมานั้น มันเป็น Component ที่รับผิดชอบเรื่องของตัวละครได้เบ็ดเสร็จในตัวมันเองเรียบร้อยเลย คนที่เอามันไปใช้ก็แค่เรียกใช้งานได้ถูก Properties / Methods เท่านั้น มันก็จะทำงานได้ถูกต้องโดยที่คนเรียกใช้ไม่ต้องรู้การทำงานภายในของมันเลยนั่นเอง

🧐 โจทย์ 02

เกมที่เราเขียนขายดีมาก เลยทำให้เราต้องเพิ่มตัวละครแบบอื่นๆเข้าไปบ้างนั่นคือ นักดาป (Swordman) และ พระ (Acolyte) เพื่อให้เกมมีความหลากหลายขึ้น และมีเงื่อนไขเพิ่มมาอีกนิสนุงนั่นคือ

  • นักดาป - จะมีพลังโจมตีเริ่มต้นที่ 10 หน่วย และ สามารถใช้ ท่าโจมตีพิเศษ ได้อีกด้วย

  • พระ - มีพลังโจมตีเริ่มต้นที่ 5 หน่วย และ สามารถใช้ การรักษาให้กับตัวเองได้ด้วย

แล้วเราจะเขียนโปรแกรมยังไงดี เพื่อให้มีตัวละครใหม่ 2 ตัวเพิ่มขึ้นมา ในขณะที่ตัว เด็กฝึกหัด (Novice) ก็ยังต้องใช้งานได้เหมือนเดิมด้วยนะ

🧒 แก้โจทย์

จากที่ว่ามาเราก็จะกลับมาออกแบบโดยใช้หลัก Abstraction เพื่อตีโจทย์ก่อนว่า ปัญหา ที่เรากำลังเจอมันประกอบไปด้วยอะไรบ้างนั่นเอง ดังนั้นผมก็จะได้รูปประมาณนี้

นักดาป (Swordman)

พระ (Acolyte)

เมื่อเราเห็นข้อมูลตัวอย่างของ นักดาป และ พระ เรียบร้อยแล้วเราน่าจะเห็นว่ามันมี Properties และ Behaviors ต่างๆที่คล้ายกับ เด็กฝึกหัด เลยใช่ไหม ซึ่งของที่มันมีเหมือนกันนั่นคือ

// Properties
HP
Exp
Atk
Level
Status

// Behaviors
Walk()
Sit()
Attack()
Dead()

แล้วทั้ง 3 ตัวก็เป็นสิ่งที่เรียกว่า ตัวละคร ที่ให้ผู้ใช้สามารถเลือกเล่นได้ ดังนั้นมันอยู่ในรูปแบบความสัมพันธ์ IS A อยู่แล้ว ดังนั้นในจุดนี้เราเลยสามารถใช้ Inheritance ได้เลย โดยการสร้าง Model กลางที่ชื่อว่า Character ตามด้านล่างเลย

public class Character
{
    public int HP { get; set; }
    public int Exp { get; set; }
    public int Atk { get; set; }
    public int Level { get; set; }
    public string Status { get; set; }

    public void Walk() { }
    public void Sit() { }
    public void Attack() { }
    public void Dead() { }
}

อ่อ อย่าลืมนะว่าตัว Model กลางก็ต้องนำหลัก Abstraction + Encapsulation มาใช้ด้วยเช่นกัน ดังนั้น เราจะได้โค้ดจริงๆออกมาเป็นแบบนี้ (อย่าลืมเปลี่ยน private เป็น protected ด้วยนะ คลาสลูกจะได้เข้าถึงตัวแปรพวกนั้นได้)

public class Character
{
    private int hp;
    public int HP
    {
        get => hp;
        set
        {
            var hpTemp = hp;
            hp -= value;
            if (hp <= 0)
            {
                hp = 0;
                Status = "Dead";
                Dead();
            }
            else
            {
                hp = hpTemp;
                Status = "Alive";
            }
        }
    }

    public int Atk { get; protected set; } = 3;

    private int exp;
    public int Exp
    {
        get => exp;
        set
        {
            var expTemp = exp;
            expTemp += value;
            if (expTemp >= 100)
            {
                exp = 0;
                Level++;
            }
            else
            {
                exp = expTemp;
            }
        }
    }
    public int Level { get; protected set; }
    public string Status { get; protected set; } = "Alive";

    public Character()
    {
        HP = 100;
    }

    public void Walk() { }
    public void Sit() { }
    public void Attack() { }
    public void Dead() { }
}
public class Novice : Character
{
}

public class Swordman : Character
{
}

public class Acolyte : Character
{
}

สุดท้ายมันก็จะเหลือแค่ของพิเศษที่มีเฉพาะตัวของ เด็กฝึกหัด นักดาป และ พระ เท่านั้น ซึ่งก็คือ

  • นักดาป - จะมีพลังโจมตีเริ่มต้นที่ 10 หน่วย และ สามารถใช้ ท่าโจมตีพิเศษ ได้อีกด้วย

  • พระ - มีพลังโจมตีเริ่มต้นที่ 5 หน่วย และ สามารถใช้ การรักษาให้กับตัวเองได้ด้วย

ดังนั้นเราก็จะแก้ไข Model ของทั้ง 3 อาชีพให้กลายเป็นแบบนี้

public class Novice : Character
{
    public Novice()
    {
        Atk = 3;
    }
}

public class Swordman : Character
{
    public Swordman()
    {
        Atk = 10;
    }

    public void SuperAttack() { }
}

public class Acolyte : Character
{
    public Acolyte()
    {
        Atk = 5;
    }

    public void Heal() { }
}

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

ลองเขียนแผนภาพเมื่อย้อนกลับมาดูความเป็น Component ของมันอีกทีซิ

🧐 โจทย์ 03

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

  • พระ - สามารถรักษาให้กับตัวเองและผู้เล่นคนอื่นได้ด้วย

แล้วเราจะแก้ไขโค้ดเรายังไงให้รองรับความต้องการใหม่อันนี้ ?

🧒 แก้โจทย์

ในตอนนี้โจทย์ของเราอยู่ที่เมธอด Heal ของคลาส Acolyte ตามโค้ดด้านล่าง

public class Acolyte : Character
{
    public void Heal() { }
}

ปัญหาของมันคือ มันรักษาได้เฉพาะตัวเอง ดังนั้นถ้าเราอยากให้รักษาคนอื่นได้ เราก็ต้องส่ง parameter บางอย่างเข้าไปให้มันนั่นเอง แต่ว่าเราควรจะส่ง parameter อะไรเข้าไปดีล่ะ

public void Heal( ??? ) { }

จากรูปด้านบนจะเห็นว่า ถ้าเราใช้ Novice, Swordman หรือ Acolyte เข้าไปเป็น parameter นั่นหมายความว่าเราจะต้องมี method แบบนี้อย่างน้อย 3 ตัว

public void Heal(Novice target) { }
public void Heal(Swordman target) { }
public void Heal(Acolyte target) { }

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

public void Heal(Character target) { }

เมื่อโค้ดเป็นแบบด้านบนแล้วนั่นหมายความว่า ทุกตัวละคร เราสามารถส่งไปทำการรักษาได้หมดเลย และต่อให้มีตัวละครใหม่ๆเข้ามาในอนาคต ที่เป็น sub class ของ Character มันก็จะทำงานกับ method นี้ได้ทันที นี่แหละตัวอย่าง ความยืดหยุ่น ที่ทำให้การเขียนโค้ดครั้งเดียว แต่ใช้ได้ตลอดไป

🧐 โจทย์ 04

ตอนนี้ผู้เล่นเริ่มบ่นอยากแต่งตัวละครสวยๆได้ ทางบริษัทเลยคิดว่า ทุกตัวละครสามารถใส่หมวกบนหัวได้ และ หมวกแต่ละแบบก็จะเพิ่มความสามารถต่างๆให้กับผู้เล่นได้ด้วย ดังนั้นหน้าที่นี้เลยตกมาที่เรา แล้วเราจะรับมือกับเรื่องนี้ยังไงดีกันนะ ?

🧒 แก้โจทย์

จำง่ายๆเวลาที่มีงานเข้ามาให้ดูก่อนว่างานนั้นเป็นงานที่ โค้ดเดิมทำได้อยู่แล้ว หรือ มันเป็นเรื่องใหม่

  • ถ้าโค้ดเดิมทำได้อยู่แล้ว เช่น เพิ่มตัวละครตัวใหม่เข้าไป อันนี้เราก็แค่ต่อยอด Model เดิมก็จะใช้งานได้ละ

  • ถ้ามันเป็นเรื่องใหม่ เช่นกรณีนี้ ให้เรานำหลักของ Abstraction กลับมาวิเคราะห์ดูเสมอนั่นเอง

ดังนั้นในรอบนี้เราก็จะเอา Abstraction กลับมาช่วยตีโจทย์นั่นเอง โดยผมมองว่าไม่ว่าจะเป็นตัวละครตัวไหนก็ตาม ก็ควรที่จะใส่หมวกได้ และแม้แต่ตัวละครในอนาคตก็ควรจะต้องใส่หมวกได้เช่นกัน และหมวกก็ควรมีความหลากหลายด้วย ตามรูปเลย

แล้วก็อย่าลืมมองกลับมาที่ Model เดิมที่เรามีด้วย ตามรูปด้านล่างเลย

ซึ่งถ้าดูตามความเหมาะสมแล้ว เราควรจะเพิ่ม Property ของหมวกให้กับ Character นั่นเอง ดังนั้นเราลองคิดนิสนุงว่า Model หมวกที่สามารถเพิ่มสถานะให้กับผู้ใส่ได้ควรจะมีอะไรบ้าง (ผมแอบคิดมาละประมาณนี้ละกัน)

นั่นก็หมายความว่าผมควรที่จะมี Model ของมันออกมาประมาณนี้

public class Hat
{
    public string Description { get; set; }
    public int EffectOnHP { get; set; }
    public int EffectOnAttack { get; set; }
}

ดังนั้นเราก็จะเอา Model ตัวใหม่อันนี้ไปใส่ไว้ใน Character ตามรูปด้านล่าง เพื่อให้ตัวละครรองรับการใส่หมวกได้ (ซึ่งบางตัวละครอาจจะไม่ใส่หมวกก็ได้นะ)

ดังนั้นโค้ดเราก็จะออกมาราวๆนี้

public class Character
{
    public Hat headEquipment { get; protected set; }

    public void EquipHead(Hat gear) { }

    ...
}

ตัวอย่างโค้ดทั้งหมด

public class Hat
{
    public string Description { get; set; }
    public int EffectOnHP { get; set; }
    public int EffectOnAttack { get; set; }
}
public class Character
{
    private int hp;
    public int HP
    {
        get => hp;
        set
        {
            var hpTemp = hp;
            hp -= value;
            if (hp <= 0)
            {
                hp = 0;
                Status = "Dead";
                Dead();
            }
            else
            {
                hp = hpTemp;
                Status = "Alive";
            }
        }
    }

    public int Atk { get; protected set; } = 10;

    private int exp;
    public int Exp
    {
        get => exp;
        set
        {
            var expTemp = exp;
            expTemp += value;
            if (expTemp >= 100)
            {
                exp = 0;
                Level++;
            }
            else
            {
                exp = expTemp;
            }
        }
    }
    public int Level { get; protected set; }
    public string Status { get; protected set; } = "Alive";

    public Hat headEquipment { get; protected set; }

    public Character()
    {
        HP = 100;
    }

    public void Walk() { }
    public void Sit() { }
    public void Attack() { }
    public void Dead() { }
    public void EquipHead(Hat gear) { }
}
public class Novice : Character
{
    public Novice()
    {
        Atk = 3;
    }
}
public class Swordman : Character
{
    public Swordman()
    {
        Atk = 10;
    }

    public void SuperAttack() { }
}
public class Acolyte : Character
{
    public Acolyte()
    {
        Atk = 5;
    }

    public void Heal(Character target) { }
}

🎯 บทสรุป

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

คำเตือน ในการออกแบบโดยใช้แนวคิดของ OOP ซึ่งมีหัวใจหลัก 4 ตัวนั้น ไม่ได้หมายความว่าเราจะต้องพยายามเอาหัวใจมันมาใช้งานทุกตัวนะ เพราะไม่อย่างนั้นเราจะทำให้ของมันยากขึ้นโดยใช่เหตุ ซึ่งถ้าใช้แค่หลักของ Abstraction เพื่อสร้าง Model แล้วทำงานได้หมด ก็ใช้แค่นั้นก็เพียงพอแล้วครับ

มันก็ใช้งานได้แต่ใครอยากจะแก้พลังชีวิต พลังโจมตี หรืออะไรก็ตามในกลุ่มของ Properties ก็แก้ได้เลย ดังนั้นเราเลยต้องมาคิดในมุมของ ด้วย เช่น

คราวนี้เราก็เอา Model เด็กฝึกหัด, นักดาป, พระ มาทำการต่อยอดความสามารถของ Character โดยใช้ความรู้จากเรื่อง เข้ามาใช้นั่นเอง

จากตรงนี้ผมขอเขียนแผนภาพ ก่อนละกัน จะได้เข้าใจภาพรวมทั้งหมดในตอนนี้ ซึ่งจะได้ออกมาตามรูปด้านล่าง

ดังนั้นในจุดนี้เราจะใช้ความรู้เรื่อง เข้าช่วยแก้ปัญหา โดยการส่งคลาส Character เข้าไปเป็น parameter นั่นเอง เพราะ Base class สามารถทำงานกับ Sub class ได้ทุกตัวยังไงล่ะ ดังนั้นโค้ดเราก็จะออกมาเป็นแบบนี้

คำเตือน ตามที่บอกไปว่าตัวอย่างทั้งหมดยังไม่ใช่การออกแบบที่ดีนัก ดังนั้นถ้าอยากรู้ว่าการออกแบบที่ดีเป็นยังไงแล้วล่ะก็ลองไปดูบทความถัดไป หรือกดจากลิงค์นี้ได้เลยครัช ****

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

👦
📝
Encapsulation
Inheritance
UML
Polymorphism
👑 OOP + Power of Design
👶 UML พื้นฐาน
Abstraction
Encapsulation
Inheritance
Polymorphism
Abstraction