Skip to content

Object-Oriented Programming

แนวคิดหลัก

Object-Oriented Programming (OOP) เป็นกระบวนทัศน์การเขียนโปรแกรมที่มองทุกอย่างเป็นวัตถุ (Objects) ซึ่งแต่ละวัตถุมีคุณสมบัติ (Properties) และพฤติกรรม (Methods) ของตัวเอง วัตถุเหล่านี้สามารถโต้ตอบกันผ่านการส่งข้อความ (Message Passing) หรือการเรียกใช้เมธอดของกันและกัน

OOP มีหลักการสำคัญ 4 ประการ:

1. การห่อหุ้ม (Encapsulation)

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

java
public class BankAccount {
    // ตัวแปรแบบ private ไม่สามารถเข้าถึงได้โดยตรงจากภายนอก
    private double balance;
    private String accountNumber;
    
    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }
    
    // เมธอดสาธารณะที่ควบคุมการเข้าถึงข้อมูล
    public double getBalance() {
        return balance;
    }
    
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
}
javascript
class BankAccount {
  // Private fields in JavaScript (ES2022)
  #balance;
  #accountNumber;

  constructor(accountNumber, initialBalance) {
    this.#accountNumber = accountNumber;
    this.#balance = initialBalance;
  }

  getBalance() {
    return this.#balance;
  }

  deposit(amount) {
    if (amount > 0) {
      this.#balance += amount;
    }
  }

  withdraw(amount) {
    if (amount > 0 && amount <= this.#balance) {
      this.#balance -= amount;
      return true;
    }
    return false;
  }
}
python
class BankAccount:
    def __init__(self, account_number, initial_balance):
        # Convention for private variables is using underscore
        self._account_number = account_number
        self._balance = initial_balance
    
    def get_balance(self):
        return self._balance
    
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
    
    def withdraw(self, amount):
        if amount > 0 and amount <= self._balance:
            self._balance -= amount
            return True
        return False
rust
pub struct BankAccount {
    balance: f64,
    account_number: String,
}

impl BankAccount {
    pub fn new(account_number: String, initial_balance: f64) -> Self {
        BankAccount {
            account_number,
            balance: initial_balance,
        }
    }
    
    pub fn get_balance(&self) -> f64 {
        self.balance
    }
    
    pub fn deposit(&mut self, amount: f64) {
        if amount > 0.0 {
            self.balance += amount;
        }
    }
    
    pub fn withdraw(&mut self, amount: f64) -> bool {
        if amount > 0.0 && amount <= self.balance {
            self.balance -= amount;
            return true;
        }
        false
    }
}
go
type BankAccount struct {
    balance       float64
    accountNumber string
}

func NewBankAccount(accountNumber string, initialBalance float64) *BankAccount {
    return &BankAccount{
        accountNumber: accountNumber,
        balance:       initialBalance,
    }
}

func (a *BankAccount) GetBalance() float64 {
    return a.balance
}

func (a *BankAccount) Deposit(amount float64) {
    if amount > 0 {
        a.balance += amount
    }
}

func (a *BankAccount) Withdraw(amount float64) bool {
    if amount > 0 && amount <= a.balance {
        a.balance -= amount
        return true
    }
    return false
}

2. การสืบทอด (Inheritance)

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

python
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def make_sound(self):
        pass
    
    def sleep(self):
        return f"{self.name} is sleeping"

# Dog คลาสสืบทอดจาก Animal
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # เรียกใช้ constructor ของคลาสแม่
        self.breed = breed
    
    def make_sound(self):  # override เมธอดของคลาสแม่
        return "Woof!"
    
    def fetch(self):  # เพิ่มเมธอดใหม่เฉพาะของคลาส Dog
        return f"{self.name} is fetching the ball"
javascript
class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  makeSound() {
    // Base implementation
  }

  sleep() {
    return `${this.name} is sleeping`;
  }
}

// Dog class inherits from Animal
class Dog extends Animal {
  constructor(name, age, breed) {
    super(name, age); // Call parent constructor
    this.breed = breed;
  }

  makeSound() { // Override parent method
    return "Woof!";
  }

  fetch() { // New method specific to Dog
    return `${this.name} is fetching the ball`;
  }
}
rust
trait Animal {
    fn new(name: String, age: u32) -> Self;
    fn make_sound(&self) -> String;
    fn sleep(&self) -> String;
    fn name(&self) -> &String;
}

struct Dog {
    name: String,
    age: u32,
    breed: String,
}

impl Animal for Dog {
    fn new(name: String, age: u32) -> Self {
        Dog {
            name,
            age,
            breed: String::from("Unknown"),
        }
    }
    
    fn make_sound(&self) -> String {
        String::from("Woof!")
    }
    
    fn sleep(&self) -> String {
        format!("{} is sleeping", self.name)
    }
    
    fn name(&self) -> &String {
        &self.name
    }
}

impl Dog {
    fn new_with_breed(name: String, age: u32, breed: String) -> Self {
        Dog { name, age, breed }
    }
    
    fn fetch(&self) -> String {
        format!("{} is fetching the ball", self.name)
    }
}
go
type Animal struct {
    Name string
    Age  int
}

func (a *Animal) MakeSound() string {
    return ""  // Base implementation
}

func (a *Animal) Sleep() string {
    return fmt.Sprintf("%s is sleeping", a.Name)
}

type Dog struct {
    Animal  // Embedding for inheritance
    Breed string
}

func NewDog(name string, age int, breed string) *Dog {
    return &Dog{
        Animal: Animal{Name: name, Age: age},
        Breed:  breed,
    }
}

func (d *Dog) MakeSound() string {
    return "Woof!"  // Override parent method
}

func (d *Dog) Fetch() string {
    return fmt.Sprintf("%s is fetching the ball", d.Name)
}

3. การพ้องรูป (Polymorphism)

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

typescript
interface Shape {
  calculateArea(): number;
  draw(): void;
}

class Circle implements Shape {
  constructor(private radius: number) {}

  calculateArea(): number {
    return Math.PI * this.radius * this.radius;
  }

  draw(): void {
    console.log("Drawing a circle");
  }
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  calculateArea(): number {
    return this.width * this.height;
  }

  draw(): void {
    console.log("Drawing a rectangle");
  }
}

// สามารถใช้ Shape interface กับวัตถุที่แตกต่างกัน
function renderShape(shape: Shape) {
  console.log(`Area: ${shape.calculateArea()}`);
  shape.draw();
}

renderShape(new Circle(5)); // ทำงานกับ Circle
renderShape(new Rectangle(4, 6)); // ทำงานกับ Rectangle
javascript
// JavaScript doesn't have interfaces but can use duck typing
class Circle {
  constructor(radius) {
    this.radius = radius;
  }

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }

  draw() {
    console.log("Drawing a circle");
  }
}

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  calculateArea() {
    return this.width * this.height;
  }

  draw() {
    console.log("Drawing a rectangle");
  }
}

// Works with any object with calculateArea and draw methods
function renderShape(shape) {
  console.log(`Area: ${shape.calculateArea()}`);
  shape.draw();
}

renderShape(new Circle(5));
renderShape(new Rectangle(4, 6));
python
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def calculate_area(self):
        pass
    
    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def calculate_area(self):
        return 3.14 * self.radius * self.radius
    
    def draw(self):
        print("Drawing a circle")

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def calculate_area(self):
        return self.width * self.height
    
    def draw(self):
        print("Drawing a rectangle")

def render_shape(shape):
    print(f"Area: {shape.calculate_area()}")
    shape.draw()

render_shape(Circle(5))
render_shape(Rectangle(4, 6))
go
type Shape interface {
    CalculateArea() float64
    Draw()
}

type Circle struct {
    radius float64
}

func (c Circle) CalculateArea() float64 {
    return math.Pi * c.radius * c.radius
}

func (c Circle) Draw() {
    fmt.Println("Drawing a circle")
}

type Rectangle struct {
    width, height float64
}

func (r Rectangle) CalculateArea() float64 {
    return r.width * r.height
}

func (r Rectangle) Draw() {
    fmt.Println("Drawing a rectangle")
}

func RenderShape(s Shape) {
    fmt.Printf("Area: %f\n", s.CalculateArea())
    s.Draw()
}

func main() {
    RenderShape(Circle{radius: 5})
    RenderShape(Rectangle{width: 4, height: 6})
}
rust
trait Shape {
    fn calculate_area(&self) -> f64;
    fn draw(&self);
}

struct Circle {
    radius: f64,
}

impl Shape for Circle {
    fn calculate_area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
    
    fn draw(&self) {
        println!("Drawing a circle");
    }
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Shape for Rectangle {
    fn calculate_area(&self) -> f64 {
        self.width * self.height
    }
    
    fn draw(&self) {
        println!("Drawing a rectangle");
    }
}

fn render_shape(shape: &dyn Shape) {
    println!("Area: {}", shape.calculate_area());
    shape.draw();
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rectangle = Rectangle { width: 4.0, height: 6.0 };
    
    render_shape(&circle);
    render_shape(&rectangle);
}

4. การนามธรรม (Abstraction)

การนามธรรมช่วยให้เราสามารถแสดงเฉพาะคุณลักษณะที่จำเป็นและซ่อนรายละเอียดที่ซับซ้อน ทำให้โค้ดเข้าใจง่ายขึ้น