Dark mode
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)
การนามธรรมช่วยให้เราสามารถแสดงเฉพาะคุณลักษณะที่จำเป็นและซ่อนรายละเอียดที่ซับซ้อน ทำให้โค้ดเข้าใจง่ายขึ้น