Principle ของ Encapsulation

บทนำ: ปัญหาของการเปิดเผยข้อมูล

จนถึงตอนนี้ เราเขียนโปรแกรมโดย ให้ใครก็ได้เข้าถึง attributes ได้

java// ❌ ปัญหา: Attributes เปิดเผย
public class BankAccount {
    public double balance;  // ใครก็ถ้อนเงินให้เชื่อมแบบไม่ถูกต้อง!
}

// ใช้งาน
BankAccount account = new BankAccount();
account.balance = 50000;
account.balance = -10000;  // !!!! ยอดเงินติดลบ - ผิด!
account.balance = 999999999;  // !!!! เงินเยอะขึ้นโดยไม่ฝาก - ผิด!

ปัญหา:

  1. ❌ ใครก็เปลี่ยนแปลงข้อมูล โดยไม่ตรวจสอบ
  2. ❌ ไม่ได้บ้านหมั้น (ไม่มีเงื่อนไข)
  3. ❌ ข้อมูลสามารถ ทำให้ inconsistent (ไม่สอดคล้องกัน)
  4. ❌ ไม่มีการบันทึก (audit trail) ว่าใครเปลี่ยนข้อมูล

Encapsulation คืออะไร?

Encapsulation (หลีกเหลี่ยม) คือ หลักการซ่อน:

  1. ซ่อนข้อมูลภายใน (hide internal data)
  2. เปิด interface ทีละส่วน (expose only necessary operations)
  3. ควบคุมการเข้าถึง (validate access)

คำนำ: เหมือนกล่องดำของเครื่องบิน

  • ภายในมี electronic ที่ซับซ้อน (ซ่อนไว้)
  • แต่ภายนอกมี switch/button (interface) ที่ใช้งาน

ตัวอย่าง: Encapsulation ในชีวิตจริง

ตัวอย่าง 1: ATM

text❌ ไม่ Encapsulation:
─────────────────────────
ATM: "ที่นี่ที่เลขบัญชี, ที่นี่ account, เปลี่ยนเงินได้เอง!"
User: เข้าใจได้ ฉันเปลี่ยน balance = 999999 ได้!

✓ Encapsulation:
──────────────────────────
ATM: "บอกฉัน: เบอร์บัตร, PIN, จำนวนเงิน"
     "ฉันจะทำการตรวจสอบและถอนเงินให้"
User: "OK, ถอน 5000 บาท"
ATM: "✓ ตรวจสอบ PIN ได้"
     "✓ ตรวจสอบ balance พอ"
     "✓ ถอนเงินสำเร็จ"
     "✓ บันทึก transaction"

ตัวอย่าง 2: Car Engine

text❌ ไม่ Encapsulation:
──────────────────────
ผู้ขับขี่: "ฉันเปิด carburetor, ปรับ timing valve, ..."
         (ต้องรู้ลายละเอียด)

✓ Encapsulation:
──────────────────
ผู้ขับขี่: "ฉันเหยียบ accelerator, ปั่นกุญแจ"
Engine: "ฉันจะทำทุกอย่าง"
         "✓ ปรับ fuel injection"
         "✓ ปรับ ignition timing"
         "✓ ปรับ air intake"

ประโยชน์ของ Encapsulation

1. Data Integrity (ความถูกต้องของข้อมูล)

java// ✓ ด้วย Encapsulation
public class Student {
    private double gpa;  // ซ่อน
    
    public void setGPA(double newGPA) {
        if (newGPA >= 0 && newGPA <= 4.0) {
            this.gpa = newGPA;  // ✓ ตรวจสอบแล้ว
        } else {
            System.out.println("Error: GPA must be between 0-4.0");
        }
    }
    
    public double getGPA() {
        return this.gpa;
    }
}

// ใช้งาน
Student student = new Student();
student.setGPA(3.75);   // ✓ OK
student.setGPA(5.0);    // ❌ Error: GPA must be between 0-4.0
student.setGPA(-1);     // ❌ Error: GPA must be between 0-4.0

ประโยชน์: ข้อมูล ไม่เคยผิด เพราะมี validation


2. Flexibility (ความยืดหยุ่น)

java// ❌ ไม่ Encapsulation: เปลี่ยนใจยาก
public class Person {
    public String fullName;  // เก็บแบบเต็มชื่อ
}

// ต่อมา: ต้องแยก firstName, lastName
// → ต้องแก้ไขทุก code ที่ใช้ fullName

// ✓ Encapsulation: เปลี่ยนใจได้
public class Person {
    private String firstName;
    private String lastName;
    
    public void setFullName(String first, String last) {
        this.firstName = first;
        this.lastName = last;
    }
    
    public String getFullName() {
        return firstName + " " + lastName;
    }
}

// ใช้งาน: เหมือนเดิม (method interface เดียวกัน)
person.setFullName("John", "Doe");
String name = person.getFullName();

ประโยชน์: เปลี่ยน internal implementation แต่ interface ไม่เปลี่ยน


3. Security (ความปลอดภัย)

java// ❌ ไม่ Encapsulation: ข้อมูลสำคัญเปิดเผย
public class Account {
    public String password;
}

// ใครก็ได้อ่าน password ได้!
Account account = new Account();
System.out.println(account.password);  // ❌ Hacked!

// ✓ Encapsulation: ซ่อนข้อมูลสำคัญ
public class Account {
    private String password;  // ซ่อน
    
    public boolean verifyPassword(String input) {
        return password.equals(input);  // ✓ ตรวจสอบเท่านั้น
    }
    
    public void changePassword(String oldPwd, String newPwd) {
        if (verifyPassword(oldPwd)) {
            this.password = newPwd;  // ✓ มี verification
        }
    }
}

// ใช้งาน: ไม่ได้อ่านข้อมูลจริง
if (account.verifyPassword("myPassword")) {
    System.out.println("Correct!");
}

ประโยชน์: ข้อมูลสำคัญ ไม่สามารถอ่านได้ โดยตรง


4. Maintainability (ง่ายต่อการดูแล)

java// ❌ ไม่ Encapsulation: เปลี่ยนได้ยาก
public class Temperature {
    public double celsius;  // เก็บอุณหภูมิเป็น Celsius
}

// Code ที่ใช้: เก็บหลายที่ๆ
double temp = tempObject.celsius;
tempObject.celsius = 25;

// ต่อมา: ต้องเปลี่ยนเป็น Kelvin
// → ต้องค้นหาทุกที่ที่เข้าถึง celsius และแก้ไข

// ✓ Encapsulation: เปลี่ยนได้ง่าย
public class Temperature {
    private double kelvin;  // เปลี่ยนเป็น Kelvin ภายใน
    
    public double getCelsius() {
        return kelvin - 273.15;
    }
    
    public void setCelsius(double celsius) {
        this.kelvin = celsius + 273.15;
    }
}

// ใช้งาน: เหมือนเดิม
double temp = tempObject.getCelsius();
tempObject.setCelsius(25);

ประโยชน์: เปลี่ยน internal representation โดย ผู้ใช้ไม่รู้


Analogy: Encapsulation เหมือนบ้าน

text❌ บ้านไม่มี Encapsulation:
────────────────────────────
┌─────────────────────────────┐
│  Open House!                │
│  ไม่มีผนัง, ไม่มีประตู      │
│  ใครก็เข้าได้ ทำอะไรก็ได้    │
│  - สวิช light ปิด/เปิด      │
│  - เปลี่ยน furniture        │
│  - กัดขนมใน fridge         │
│  - เข้าห้องนอนได้          │
│  - อ่านเอกสารส่วนตัว       │
│ ⚠️ ยุ่ม! ความเป็นส่วนตัวไม่มี!
└─────────────────────────────┘

✓ บ้านมี Encapsulation:
──────────────────────────
┌─────────────────────────────┐
│  Private House!             │
│  ┌─ ประตูด้านนอก            │
│  │  (gate - ต้องใช้ keycard) │
│  │                          │
│  ├─ ประตูห้องรับแขก         │
│  │  (controlled access)     │
│  │                          │
│  ├─ ประตูห้องอื่น           │
│  │  (locked - เฉพาะครอบครัว)│
│  │                          │
│  ├─ ประตูห้องนอน            │
│  │  (ส่วนตัว - ปิดสนิท)     │
│  │                          │
│  └─ Safe ในห้องนอน          │
│     (ข้อมูลสำคัญซ่อน)       │
│                             │
│ ✓ ส่วนตัวได้ + ปลอดภัย!     │
└─────────────────────────────┘

หลักการ 3 ประการของ Encapsulation

1. Hide (ซ่อน)

java// ❌ เปิดเผย
public class Student {
    public String name;
    public int age;
    public double gpa;
}

// ✓ ซ่อน
public class Student {
    private String name;   // ✓ ซ่อน
    private int age;       // ✓ ซ่อน
    private double gpa;    // ✓ ซ่อน
}

วิธี: ใช้ private


2. Restrict (จำกัด)

java// ✓ จำกัดวิธีการเข้าถึง
public class Student {
    private double gpa;
    
    // ✓ เข้าถึงได้ผ่าน setter เท่านั้น
    public void setGPA(double newGPA) {
        if (newGPA >= 0 && newGPA <= 4.0) {
            this.gpa = newGPA;
        }
    }
    
    // ✓ อ่านค่าได้ผ่าน getter เท่านั้น
    public double getGPA() {
        return this.gpa;
    }
}

// ใช้งาน: เข้าถึงผ่าน methods เท่านั้น
student.setGPA(3.75);
double gpa = student.getGPA();

// ❌ ไม่ได้:
// student.gpa = 5.0;  // ERROR! private

วิธี: ให้ getter/setter จำกัด


3. Validate (ตรวจสอบ)

java// ✓ ตรวจสอบเมื่อเปลี่ยนแปลง
public class BankAccount {
    private double balance;
    
    public void setBalance(double newBalance) {
        if (newBalance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        this.balance = newBalance;
    }
    
    public void withdraw(double amount) {
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance");
        }
        this.balance -= amount;
    }
    
    public double getBalance() {
        return this.balance;
    }
}

// ใช้งาน
BankAccount account = new BankAccount();
account.setBalance(50000);

account.withdraw(30000);  // ✓ OK
account.withdraw(25000);  // ❌ Error: Insufficient balance

วิธี: เพิ่ม validation logic ใน setter


ตัวอย่างที่ 1: Student Class ที่ไม่ Encapsulated

java// ❌ ไม่ Encapsulation
public class StudentBad {
    public String studentID;
    public String name;
    public double gpa;
    public int year;
    
    public void study() {
        gpa += 0.1;
    }
}

// ปัญหา:
public class Main {
    public static void main(String[] args) {
        StudentBad student = new StudentBad();
        
        // ❌ ก็ได้ set ค่า invalid
        student.studentID = null;      // null!
        student.name = "";             // string ว่าง!
        student.gpa = 10.5;            // GPA > 4.0!
        student.year = 0;              // ปี 0?
        
        // ❌ ก็ได้ใช้ method ไม่ถูกต้อง
        for (int i = 0; i < 1000; i++) {
            student.study();  // GPA = 100 + !!!
        }
    }
}

ตัวอย่างที่ 2: Student Class ที่ Encapsulated (ดี)

java// ✓ Encapsulation
public class StudentGood {
    private String studentID;
    private String name;
    private double gpa;
    private int year;
    
    // Constructor ที่มี validation
    public StudentGood(String studentID, String name, double gpa, int year) {
        setStudentID(studentID);
        setName(name);
        setGPA(gpa);
        setYear(year);
    }
    
    // ===== Getters =====
    public String getStudentID() {
        return studentID;
    }
    
    public String getName() {
        return name;
    }
    
    public double getGPA() {
        return gpa;
    }
    
    public int getYear() {
        return year;
    }
    
    // ===== Setters (มี Validation) =====
    public void setStudentID(String id) {
        if (id == null || id.isEmpty()) {
            throw new IllegalArgumentException("Student ID cannot be null or empty");
        }
        this.studentID = id;
    }
    
    public void setName(String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Name cannot be null or empty");
        }
        this.name = name;
    }
    
    public void setGPA(double gpa) {
        if (gpa < 0 || gpa > 4.0) {
            throw new IllegalArgumentException("GPA must be between 0 and 4.0");
        }
        this.gpa = gpa;
    }
    
    public void setYear(int year) {
        if (year < 1 || year > 4) {
            throw new IllegalArgumentException("Year must be between 1 and 4");
        }
        this.year = year;
    }
    
    // ===== Business Methods =====
    public void study() {
        double newGPA = gpa + 0.1;
        if (newGPA > 4.0) {
            newGPA = 4.0;  // Cap at 4.0
        }
        this.gpa = newGPA;
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s, Name: %s, GPA: %.2f, Year: %d\n",
                         studentID, name, gpa, year);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // ✓ การสร้าง valid object
        StudentGood student = new StudentGood("6501001", "John", 3.75, 2);
        student.displayInfo();  // ID: 6501001, Name: John, GPA: 3.75, Year: 2
        
        // ✓ ใช้ getter
        System.out.println("GPA: " + student.getGPA());  // 3.75
        
        // ✓ ใช้ setter ที่ safe
        student.study();
        System.out.println("After study: " + student.getGPA());  // 3.85
        
        // ❌ ไม่ได้ set ค่า invalid
        try {
            student.setGPA(10.5);  // ❌ Error!
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        // ❌ ไม่ได้เข้าถึง private attributes
        // student.gpa = 5.0;  // ERROR! private
        
        // ❌ ไม่ได้สร้าง invalid object
        try {
            StudentGood bad = new StudentGood("", "John", 3.75, 2);  // ❌ Error!
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

ตัวอย่างที่ 3: BankAccount ที่ Encapsulated

javapublic class BankAccount {
    private String accountNumber;
    private String accountHolder;
    private double balance;
    private int transactions = 0;  // นับจำนวน transaction
    
    public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
        if (accountNumber == null || accountNumber.isEmpty()) {
            throw new IllegalArgumentException("Account number cannot be empty");
        }
        if (accountHolder == null || accountHolder.isEmpty()) {
            throw new IllegalArgumentException("Account holder cannot be empty");
        }
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Initial balance cannot be negative");
        }
        
        this.accountNumber = accountNumber;
        this.accountHolder = accountHolder;
        this.balance = initialBalance;
    }
    
    // ===== Getters =====
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public String getAccountHolder() {
        return accountHolder;
    }
    
    public double getBalance() {
        return balance;
    }
    
    public int getTransactionCount() {
        return transactions;
    }
    
    // ===== Business Methods (ไม่มี direct setBalance) =====
    
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Deposit amount must be positive");
        }
        
        this.balance += amount;
        this.transactions++;
        
        System.out.printf("[DEPOSIT] Amount: %.2f, New Balance: %.2f\n", amount, balance);
    }
    
    public void withdraw(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Withdraw amount must be positive");
        }
        
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance. Current: " + balance);
        }
        
        this.balance -= amount;
        this.transactions++;
        
        System.out.printf("[WITHDRAW] Amount: %.2f, New Balance: %.2f\n", amount, balance);
    }
    
    public void transfer(BankAccount recipient, double amount) {
        if (recipient == null) {
            throw new IllegalArgumentException("Recipient account cannot be null");
        }
        
        if (amount <= 0) {
            throw new IllegalArgumentException("Transfer amount must be positive");
        }
        
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance for transfer");
        }
        
        this.withdraw(amount);  // ลด balance ของ this
        recipient.deposit(amount);  // เพิ่ม balance ของ recipient
        
        System.out.printf("[TRANSFER] From %s to %s: %.2f\n", 
                         this.accountNumber, recipient.accountNumber, amount);
    }
    
    public void displayInfo() {
        System.out.printf("Account: %s | Holder: %s | Balance: %.2f | Transactions: %d\n",
                         accountNumber, accountHolder, balance, transactions);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        BankAccount account1 = new BankAccount("ACC001", "John", 50000);
        BankAccount account2 = new BankAccount("ACC002", "Jane", 30000);
        
        System.out.println("=== Initial State ===");
        account1.displayInfo();  // Balance: 50000, Transactions: 0
        account2.displayInfo();  // Balance: 30000, Transactions: 0
        
        System.out.println("\n=== Deposit ===");
        account1.deposit(10000);
        account1.displayInfo();  // Balance: 60000, Transactions: 1
        
        System.out.println("\n=== Withdraw ===");
        account1.withdraw(5000);
        account1.displayInfo();  // Balance: 55000, Transactions: 2
        
        System.out.println("\n=== Transfer ===");
        account1.transfer(account2, 20000);
        
        System.out.println("\n=== Final State ===");
        account1.displayInfo();  // Balance: 35000, Transactions: 3
        account2.displayInfo();  // Balance: 50000, Transactions: 1
        
        System.out.println("\n=== Error Handling ===");
        try {
            account1.withdraw(50000);  // ❌ Insufficient
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        try {
            account1.setBalance(-1000);  // ❌ ERROR! ไม่มี setBalance
        } catch (Exception e) {
            System.out.println("Error: Cannot set balance directly!");
        }
    }
}

Output:

text=== Initial State ===
Account: ACC001 | Holder: John | Balance: 50000.00 | Transactions: 0
Account: ACC002 | Holder: Jane | Balance: 30000.00 | Transactions: 0

=== Deposit ===
[DEPOSIT] Amount: 10000.00, New Balance: 60000.00
Account: ACC001 | Holder: John | Balance: 60000.00 | Transactions: 1

=== Withdraw ===
[WITHDRAW] Amount: 5000.00, New Balance: 55000.00
Account: ACC001 | Holder: John | Balance: 55000.00 | Transactions: 2

=== Transfer ===
[WITHDRAW] Amount: 20000.00, New Balance: 35000.00
[DEPOSIT] Amount: 20000.00, New Balance: 50000.00
[TRANSFER] From ACC001 to ACC002: 20000.00

=== Final State ===
Account: ACC001 | Holder: John | Balance: 35000.00 | Transactions: 3
Account: ACC002 | Holder: Jane | Balance: 50000.00 | Transactions: 1

=== Error Handling ===
Error: Insufficient balance for transfer. Current: 35000.0
Error: Cannot set balance directly!

ตัวอย่างที่ 4: Product Inventory System

javapublic class Product {
    private String productID;
    private String productName;
    private double price;
    private int quantity;
    private double totalSold = 0;  // ติดตาม
    
    public Product(String id, String name, double price, int quantity) {
        setProductID(id);
        setProductName(name);
        setPrice(price);
        setQuantity(quantity);
    }
    
    // ===== Getters =====
    public String getProductID() {
        return productID;
    }
    
    public String getProductName() {
        return productName;
    }
    
    public double getPrice() {
        return price;
    }
    
    public int getQuantity() {
        return quantity;
    }
    
    public double getTotalSold() {
        return totalSold;
    }
    
    public boolean isInStock() {
        return quantity > 0;
    }
    
    // ===== Setters (มี Validation) =====
    public void setProductID(String id) {
        if (id == null || id.isEmpty()) {
            throw new IllegalArgumentException("Product ID cannot be empty");
        }
        this.productID = id;
    }
    
    public void setProductName(String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Product name cannot be empty");
        }
        this.productName = name;
    }
    
    public void setPrice(double price) {
        if (price < 0) {
            throw new IllegalArgumentException("Price cannot be negative");
        }
        this.price = price;
    }
    
    public void setQuantity(int quantity) {
        if (quantity < 0) {
            throw new IllegalArgumentException("Quantity cannot be negative");
        }
        this.quantity = quantity;
    }
    
    // ===== Business Methods =====
    public void addStock(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        this.quantity += amount;
        System.out.printf("[STOCK ADD] %s: Added %d units (Total: %d)\n",
                         productName, amount, quantity);
    }
    
    public void sell(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        if (amount > this.quantity) {
            throw new IllegalArgumentException("Insufficient stock. Available: " + quantity);
        }
        
        this.quantity -= amount;
        double revenue = amount * price;
        this.totalSold += revenue;
        
        System.out.printf("[SALE] %s: Sold %d units for %.2f (Total sold: %.2f)\n",
                         productName, amount, revenue, totalSold);
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s | Name: %s | Price: %.2f | Stock: %d | Total Sold: %.2f\n",
                         productID, productName, price, quantity, totalSold);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Product laptop = new Product("P001", "Laptop", 25000, 10);
        Product mouse = new Product("P002", "Mouse", 500, 50);
        
        System.out.println("=== Initial State ===");
        laptop.displayInfo();
        mouse.displayInfo();
        
        System.out.println("\n=== Add Stock ===");
        laptop.addStock(5);
        mouse.addStock(30);
        
        System.out.println("\n=== Sell ===");
        laptop.sell(2);  // ขาย laptop 2 เครื่อง
        mouse.sell(20);  // ขาย mouse 20 ตัว
        
        System.out.println("\n=== State After Sales ===");
        laptop.displayInfo();
        mouse.displayInfo();
        
        System.out.println("\n=== Stock Status ===");
        System.out.println("Laptop in stock: " + laptop.isInStock());
        System.out.println("Mouse in stock: " + mouse.isInStock());
        
        System.out.println("\n=== Error Handling ===");
        try {
            laptop.sell(100);  // ❌ Insufficient stock
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        try {
            mouse.setPrice(-1000);  // ❌ Negative price
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

หลักการของ Encapsulation Summary

text┌─────────────────────────────────────────────┐
│  Encapsulation = "Black Box" Principle     │
├─────────────────────────────────────────────┤
│                                             │
│  HIDE (ซ่อน)                               │
│  ├─ Private attributes                     │
│  └─ Internal implementation                │
│                                             │
│  RESTRICT (จำกัด)                          │
│  ├─ Public getters                         │
│  └─ Public setters (with validation)      │
│                                             │
│  VALIDATE (ตรวจสอบ)                        │
│  ├─ Input validation                       │
│  ├─ Business rule enforcement              │
│  └─ Data consistency                       │
│                                             │
│  RESULT (ผลลัพธ์)                          │
│  ├─ Data integrity                         │
│  ├─ Security                               │
│  ├─ Flexibility                            │
│  └─ Maintainability                        │
│                                             │
└─────────────────────────────────────────────┘

ตัวอย่างที่ 5: Temperature Class ที่ซ่อน Internal Representation

javapublic class Temperature {
    // ✓ เก็บเป็น Kelvin (ภายใน)
    private double kelvin;
    
    public Temperature(double celsius) {
        setCelsius(celsius);  // ใช้ setter ที่มี validation
    }
    
    // ===== Expose ผ่าน Celsius =====
    public double getCelsius() {
        return kelvin - 273.15;
    }
    
    public void setCelsius(double celsius) {
        if (celsius < -273.15) {
            throw new IllegalArgumentException("Temperature cannot be below absolute zero");
        }
        this.kelvin = celsius + 273.15;
    }
    
    // ===== Expose ผ่าน Fahrenheit =====
    public double getFahrenheit() {
        return (kelvin - 273.15) * 9 / 5 + 32;
    }
    
    public void setFahrenheit(double fahrenheit) {
        double celsius = (fahrenheit - 32) * 5 / 9;
        setCelsius(celsius);
    }
    
    // ===== Expose ผ่าน Kelvin =====
    public double getKelvin() {
        return kelvin;
    }
    
    public void setKelvin(double kelvin) {
        if (kelvin < 0) {
            throw new IllegalArgumentException("Kelvin temperature cannot be negative");
        }
        this.kelvin = kelvin;
    }
    
    public void displayInfo() {
        System.out.printf("C: %.2f | F: %.2f | K: %.2f\n",
                         getCelsius(), getFahrenheit(), getKelvin());
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Temperature temp = new Temperature(25);  // 25°C
        
        System.out.println("=== Setting via Celsius ===");
        temp.displayInfo();  // C: 25.00 | F: 77.00 | K: 298.15
        
        System.out.println("\n=== Setting via Fahrenheit ===");
        temp.setFahrenheit(100);  // 100°F
        temp.displayInfo();  // C: 37.78 | F: 100.00 | K: 310.93
        
        System.out.println("\n=== Setting via Kelvin ===");
        temp.setKelvin(273.15);  // 0°C (freezing point of water)
        temp.displayInfo();  // C: 0.00 | F: 32.00 | K: 273.15
        
        System.out.println("\n=== Error Handling ===");
        try {
            temp.setCelsius(-500);  // ❌ Below absolute zero
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

ประเด็นสำคัญ:

  • Internal: เก็บเป็น Kelvin เท่านั้น
  • Interface: expose เป็น Celsius, Fahrenheit, Kelvin
  • ผู้ใช้ไม่รู้: ว่าภายในเก็บเป็น Kelvin
  • Flexibility: สามารถเปลี่ยน internal representation (เช่นเป็น Celsius) โดย interface ไม่เปลี่ยน

ข้อสรุป: ทำไมต้อง Encapsulation

text❌ ไม่ Encapsulation:
────────────────────
- ข้อมูลผิด ✗
- ไม่ปลอดภัย ✗
- ยากต่อการแก้ไข ✗
- Code ซ้ำซ้อน ✗

✓ Encapsulation:
─────────────────
- ข้อมูลถูกต้อง ✓
- ปลอดภัย ✓
- ง่ายต่อการแก้ไข ✓
- Code สะอาด ✓

คำแนะนำ: เขียน Encapsulation ที่ดี

text1. ทำให้ attributes เป็น private
   ✓ private String name;
   ✗ public String name;

2. เพิ่ม getter/setter
   ✓ public String getName() { }
   ✓ public void setName(String name) { }

3. เพิ่ม validation
   ✓ if (name == null || name.isEmpty()) throw ...
   ✓ if (age < 0 || age > 150) throw ...

4. ยกเว้น constants
   ✓ public static final double PI = 3.14159;
   ✓ public static final int MAX_STUDENTS = 100;

5. ซ่อน implementation details
   ✓ เก็บ internal data ที่ best
   ✓ expose ผ่าน interface ที่ clean