Getter/Setter กับการ Validate ข้อมูล

บทนำ: ทำไมต้อง Getter/Setter?

จากตอนที่ 1 เรารู้ว่า private attributes ต้องเข้าถึงผ่าน methods

แต่ methods นั้นต้องทำอะไร?

java// ❌ ปัญหา: Setter ไม่มี validation
public class Student {
    private double gpa;
    
    // ❌ ตัวอย่างที่ผิด
    public void setGPA(double gpa) {
        this.gpa = gpa;  // ← เข้ากล่องสิ้นสุด validate!
    }
}

// ใช้งาน
Student student = new Student();
student.setGPA(3.75);   // ✓ OK
student.setGPA(10.5);   // ❌ ยัง set ได้! (ไม่ถูก)
student.setGPA(-5);     // ❌ ยัง set ได้! (ไม่ถูก)

ปัญหา:

  • ❌ ไม่มี validation
  • ❌ data ยังเป็นไปไม่ได้
  • ❌ encapsulation ไม่เสร็จสมบูรณ์
ยาวไปอยากเลือกอ่าน

ความแตกต่าง: Getter/Setter vs Direct Access

❌ ไม่มี Getter/Setter

javapublic class BankAccount {
    public double balance;  // ← public
}

// ใช้งาน
BankAccount account = new BankAccount();
account.balance = 50000;      // ✓ OK
account.balance = -10000;     // ❌ ยังได้! ผิด!
account.balance = 999999999;  // ❌ ยังได้! ผิด!

ปัญหา:

  • ใครก็เปลี่ยนได้
  • ไม่มีเงื่อนไข
  • data inconsistent

✓ มี Getter/Setter

javapublic class BankAccount {
    private double balance;  // ← private
    
    public void setBalance(double newBalance) {
        // ✓ ตรวจสอบเงื่อนไข
        if (newBalance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        this.balance = newBalance;
    }
    
    public double getBalance() {
        return this.balance;
    }
}

// ใช้งาน
BankAccount account = new BankAccount();
account.setBalance(50000);      // ✓ OK
account.setBalance(-10000);     // ❌ ERROR! "Balance cannot be negative"
account.setBalance(999999999);  // ✓ OK (ถ้าเงื่อนไขผ่าน)

ประโยชน์:

  • ✓ มี validation
  • ✓ data ถูกต้อง
  • ✓ encapsulation สมบูรณ์

Getter: อ่านค่า

Getter คืออะไร?

Getter = method ที่ อ่านค่า attribute

textNaming convention:
get + AttributeName + () + return type

ตัวอย่าง:
- getName()       // return String
- getAge()        // return int
- getBalance()    // return double
- isActive()      // return boolean (special case)

ตัวอย่าง: Simple Getters

javapublic class Student {
    private String studentID;
    private String name;
    private double gpa;
    private boolean isActive;
    
    // ===== Getters =====
    
    // Getter for studentID
    public String getStudentID() {
        return this.studentID;
    }
    
    // Getter for name
    public String getName() {
        return this.name;
    }
    
    // Getter for gpa
    public double getGPA() {
        return this.gpa;
    }
    
    // Getter for isActive (boolean uses "is" prefix)
    public boolean isActive() {
        return this.isActive;
    }
}

// ใช้งาน
Student student = new Student();
// ... initialize ...

String id = student.getStudentID();      // ✓ อ่านค่า
String name = student.getName();         // ✓ อ่านค่า
double gpa = student.getGPA();           // ✓ อ่านค่า
boolean active = student.isActive();     // ✓ อ่านค่า

ประเภท Getter

1. Simple Getter – อ่านค่าตรงๆ

javapublic class Person {
    private String name;
    
    // Simple getter - อ่านค่าตรงๆ
    public String getName() {
        return this.name;
    }
}

2. Computed Getter – คำนวณหาค่า

javapublic class Circle {
    private double radius;
    
    // Computed getter - คำนวณ
    public double getArea() {
        return Math.PI * radius * radius;
    }
    
    public double getCircumference() {
        return 2 * Math.PI * radius;
    }
}

// ใช้งาน
Circle circle = new Circle(5);
double area = circle.getArea();  // 78.54 (computed)

3. Conditional Getter – ตรวจสอบเงื่อนไข

javapublic class Student {
    private double gpa;
    
    // Conditional getter
    public String getGradeLevel() {
        if (gpa >= 3.5) {
            return "Excellent";
        } else if (gpa >= 3.0) {
            return "Good";
        } else if (gpa >= 2.0) {
            return "Pass";
        } else {
            return "Fail";
        }
    }
}

// ใช้งาน
Student student = new Student();
// ... set gpa ...
String grade = student.getGradeLevel();  // "Excellent"

Setter: เปลี่ยนค่า

Setter คืออะไร?

Setter = method ที่ เปลี่ยนค่า attribute พร้อม validation

textNaming convention:
set + AttributeName + (type value) + void

ตัวอย่าง:
- setName(String name)       // void
- setAge(int age)            // void
- setBalance(double balance) // void
- setActive(boolean active)  // void

ตัวอย่าง: Simple Setters

javapublic class Student {
    private String studentID;
    private String name;
    private double gpa;
    private boolean isActive;
    
    // ===== Setters =====
    
    // Setter for studentID
    public void setStudentID(String studentID) {
        if (studentID == null || studentID.isEmpty()) {
            throw new IllegalArgumentException("Student ID cannot be empty");
        }
        this.studentID = studentID;
    }
    
    // Setter for name
    public void setName(String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Name cannot be empty");
        }
        this.name = name;
    }
    
    // Setter for gpa (WITH VALIDATION)
    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;
    }
    
    // Setter for isActive
    public void setActive(boolean isActive) {
        this.isActive = isActive;
    }
}

// ใช้งาน
Student student = new Student();

student.setStudentID("6501001");  // ✓ OK
student.setName("John Doe");      // ✓ OK
student.setGPA(3.75);             // ✓ OK
student.setGPA(5.0);              // ❌ ERROR! "GPA must be between 0 and 4.0"
student.setActive(true);          // ✓ OK

Validation: ตรวจสอบข้อมูล

Validation คืออะไร?

Validation = ตรวจสอบ ว่าข้อมูลถูกต้องตามกฎก่อน save

java// ✓ ตัวอย่างที่ดี
public void setAge(int age) {
    // Validation: ตรวจสอบเงื่อนไข
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("Age must be between 0 and 150");
    }
    // หลังผ่าน validation → set ค่า
    this.age = age;
}

ประเภท Validation

1. Null Check – ตรวจสอบ null

javapublic void setName(String name) {
    // ตรวจสอบ null
    if (name == null) {
        throw new IllegalArgumentException("Name cannot be null");
    }
    this.name = name;
}

2. Empty Check – ตรวจสอบว่าง

javapublic void setEmail(String email) {
    // ตรวจสอบว่าง
    if (email == null || email.isEmpty()) {
        throw new IllegalArgumentException("Email cannot be empty");
    }
    this.email = email;
}

3. Range Check – ตรวจสอบช่วง

javapublic void setGPA(double gpa) {
    // ตรวจสอบช่วง (0-4.0)
    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) {
    // ตรวจสอบช่วง (1-4)
    if (year < 1 || year > 4) {
        throw new IllegalArgumentException("Year must be between 1 and 4");
    }
    this.year = year;
}

4. Format Check – ตรวจสอบรูปแบบ

javapublic void setEmail(String email) {
    // ตรวจสอบรูปแบบ email
    if (!email.contains("@")) {
        throw new IllegalArgumentException("Invalid email format");
    }
    if (!email.contains(".")) {
        throw new IllegalArgumentException("Invalid email format");
    }
    this.email = email;
}

public void setPhoneNumber(String phone) {
    // ตรวจสอบรูปแบบเบอร์โทร
    if (!phone.matches("\\d{10}")) {  // ต้องเป็นตัวเลข 10 ตัว
        throw new IllegalArgumentException("Phone number must be 10 digits");
    }
    this.phone = phone;
}

5. Business Logic Check – ตรวจสอบตรรกะธุรกิจ

javapublic class BankAccount {
    private double balance;
    
    public void withdraw(double amount) {
        // ตรวจสอบตรรกะธุรกิจ
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance");
        }
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        this.balance -= amount;
    }
}

ตัวอย่างที่ 1: User Account ที่มี Validation

javapublic class UserAccount {
    private String username;
    private String email;
    private String password;
    private int age;
    private boolean isVerified;
    
    // ===== Constructor =====
    public UserAccount(String username, String email, String password, int age) {
        setUsername(username);
        setEmail(email);
        setPassword(password);
        setAge(age);
        this.isVerified = false;
    }
    
    // ===== GETTERS =====
    public String getUsername() {
        return this.username;
    }
    
    public String getEmail() {
        return this.email;
    }
    
    // ❌ ไม่ return password (security!)
    // public String getPassword() { return password; }
    
    public int getAge() {
        return this.age;
    }
    
    public boolean isVerified() {
        return this.isVerified;
    }
    
    // ===== SETTERS (WITH VALIDATION) =====
    
    // Setter 1: Username
    public void setUsername(String username) {
        // Validation 1: Null check
        if (username == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }
        // Validation 2: Empty check
        if (username.isEmpty()) {
            throw new IllegalArgumentException("Username cannot be empty");
        }
        // Validation 3: Length check
        if (username.length() < 3) {
            throw new IllegalArgumentException("Username must be at least 3 characters");
        }
        if (username.length() > 20) {
            throw new IllegalArgumentException("Username cannot exceed 20 characters");
        }
        // Validation 4: Format check (alphanumeric only)
        if (!username.matches("^[a-zA-Z0-9_]+$")) {
            throw new IllegalArgumentException("Username can only contain letters, numbers, and underscore");
        }
        this.username = username;
    }
    
    // Setter 2: Email
    public void setEmail(String email) {
        // Validation 1: Null check
        if (email == null) {
            throw new IllegalArgumentException("Email cannot be null");
        }
        // Validation 2: Format check
        if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
            throw new IllegalArgumentException("Invalid email format");
        }
        this.email = email;
    }
    
    // Setter 3: Password
    public void setPassword(String password) {
        // Validation 1: Null check
        if (password == null) {
            throw new IllegalArgumentException("Password cannot be null");
        }
        // Validation 2: Length check
        if (password.length() < 8) {
            throw new IllegalArgumentException("Password must be at least 8 characters");
        }
        // Validation 3: Complexity check (must have number)
        if (!password.matches(".*\\d.*")) {
            throw new IllegalArgumentException("Password must contain at least one digit");
        }
        // Validation 4: Complexity check (must have uppercase)
        if (!password.matches(".*[A-Z].*")) {
            throw new IllegalArgumentException("Password must contain at least one uppercase letter");
        }
        this.password = password;
    }
    
    // Setter 4: Age
    public void setAge(int age) {
        // Validation 1: Range check
        if (age < 13) {
            throw new IllegalArgumentException("User must be at least 13 years old");
        }
        if (age > 120) {
            throw new IllegalArgumentException("Invalid age");
        }
        this.age = age;
    }
    
    // ===== BUSINESS METHODS =====
    
    public void verifyEmail() {
        this.isVerified = true;
        System.out.println("✓ Email verified!");
    }
    
    public void displayInfo() {
        System.out.printf("Username: %s | Email: %s | Age: %d | Verified: %s\n",
                         username, email, age, isVerified ? "Yes" : "No");
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Valid User ===");
        try {
            UserAccount user = new UserAccount("john_doe", "[email protected]", "SecurePass123", 25);
            user.displayInfo();
            user.verifyEmail();
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Invalid Username (too short) ===");
        try {
            UserAccount user = new UserAccount("ab", "[email protected]", "SecurePass123", 25);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Invalid Email ===");
        try {
            UserAccount user = new UserAccount("john_doe", "invalid-email", "SecurePass123", 25);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Invalid Password (no uppercase) ===");
        try {
            UserAccount user = new UserAccount("john_doe", "[email protected]", "securepass123", 25);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Invalid Age ===");
        try {
            UserAccount user = new UserAccount("john_doe", "[email protected]", "SecurePass123", 10);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Output:

text=== Valid User ===
Username: john_doe | Email: [email protected] | Age: 25 | Verified: No
✓ Email verified!

=== Invalid Username (too short) ===
Error: Username must be at least 3 characters

=== Invalid Email ===
Error: Invalid email format

=== Invalid Password (no uppercase) ===
Error: Password must contain at least one uppercase letter

=== Invalid Age ===
Error: User must be at least 13 years old

ตัวอย่างที่ 2: Product ด้วย Validation

javapublic class Product {
    private String productID;
    private String productName;
    private double price;
    private int quantity;
    private String category;
    
    // ===== Constructor =====
    public Product(String id, String name, double price, int qty, String category) {
        setProductID(id);
        setProductName(name);
        setPrice(price);
        setQuantity(qty);
        setCategory(category);
    }
    
    // ===== GETTERS =====
    public String getProductID() {
        return this.productID;
    }
    
    public String getProductName() {
        return this.productName;
    }
    
    public double getPrice() {
        return this.price;
    }
    
    public int getQuantity() {
        return this.quantity;
    }
    
    public String getCategory() {
        return this.category;
    }
    
    public boolean isInStock() {
        return this.quantity > 0;
    }
    
    public double getTotalValue() {
        return this.price * this.quantity;
    }
    
    // ===== SETTERS (WITH 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");
        }
        if (name.length() > 100) {
            throw new IllegalArgumentException("Product name is too long");
        }
        this.productName = name;
    }
    
    public void setPrice(double price) {
        // ตรวจสอบ negative
        if (price < 0) {
            throw new IllegalArgumentException("Price cannot be negative");
        }
        // ตรวจสอบ unreasonable high
        if (price > 1_000_000) {
            throw new IllegalArgumentException("Price is unreasonably high");
        }
        this.price = price;
    }
    
    public void setQuantity(int qty) {
        // ตรวจสอบ negative
        if (qty < 0) {
            throw new IllegalArgumentException("Quantity cannot be negative");
        }
        // ตรวจสอบ unreasonable high
        if (qty > 100_000) {
            throw new IllegalArgumentException("Quantity is unreasonably high");
        }
        this.quantity = qty;
    }
    
    public void setCategory(String category) {
        if (category == null || category.isEmpty()) {
            throw new IllegalArgumentException("Category cannot be empty");
        }
        // ตรวจสอบ category ที่รู้จัก
        String[] validCategories = {"Electronics", "Clothing", "Food", "Books", "Other"};
        boolean found = false;
        for (String valid : validCategories) {
            if (valid.equalsIgnoreCase(category)) {
                found = true;
                break;
            }
        }
        if (!found) {
            throw new IllegalArgumentException("Invalid category");
        }
        this.category = category;
    }
    
    // ===== BUSINESS METHODS =====
    
    public void addStock(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        int newQuantity = this.quantity + amount;
        setQuantity(newQuantity);  // ใช้ setter เพื่อ validate
        System.out.printf("✓ Added %d units. New stock: %d\n", amount, this.quantity);
    }
    
    public void removeStock(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        if (amount > this.quantity) {
            throw new IllegalArgumentException("Not enough stock");
        }
        this.quantity -= amount;
        System.out.printf("✓ Removed %d units. Remaining: %d\n", amount, this.quantity);
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s | Name: %s | Price: %.2f | Qty: %d | Category: %s | Total: %.2f\n",
                         productID, productName, price, quantity, category, getTotalValue());
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Create Product ===");
        Product laptop = new Product("P001", "Laptop", 25000, 10, "Electronics");
        laptop.displayInfo();
        
        System.out.println("\n=== Add Stock ===");
        laptop.addStock(5);
        laptop.displayInfo();
        
        System.out.println("\n=== Remove Stock ===");
        laptop.removeStock(3);
        laptop.displayInfo();
        
        System.out.println("\n=== Try Invalid Price ===");
        try {
            Product mouse = new Product("P002", "Mouse", -100, 50, "Electronics");
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Try Invalid Category ===");
        try {
            Product book = new Product("P003", "Book", 200, 100, "Magazine");
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Try Remove Too Much Stock ===");
        try {
            laptop.removeStock(20);  // มี 12 เท่านั้น
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

ตัวอย่างที่ 3: Validation ระดับสูง

javapublic class BankAccount {
    private String accountNumber;
    private String accountHolder;
    private double balance;
    private double dailyWithdrawalLimit = 50000;
    private double dailyWithdrawnAmount = 0;
    
    // ===== Constructor =====
    public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
        setAccountNumber(accountNumber);
        setAccountHolder(accountHolder);
        setBalance(initialBalance);
    }
    
    // ===== GETTERS =====
    public String getAccountNumber() {
        return this.accountNumber;
    }
    
    public String getAccountHolder() {
        return this.accountHolder;
    }
    
    public double getBalance() {
        return this.balance;
    }
    
    // ===== SETTERS =====
    
    public void setAccountNumber(String accountNumber) {
        if (accountNumber == null || accountNumber.isEmpty()) {
            throw new IllegalArgumentException("Account number cannot be empty");
        }
        if (!accountNumber.matches("^[0-9]{10,20}$")) {
            throw new IllegalArgumentException("Invalid account number format");
        }
        this.accountNumber = accountNumber;
    }
    
    public void setAccountHolder(String accountHolder) {
        if (accountHolder == null || accountHolder.trim().isEmpty()) {
            throw new IllegalArgumentException("Account holder name cannot be empty");
        }
        this.accountHolder = accountHolder;
    }
    
    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        this.balance = balance;
    }
    
    // ===== BUSINESS METHODS WITH VALIDATION =====
    
    public void deposit(double amount) {
        // Validation: ตรวจสอบ amount
        if (amount <= 0) {
            throw new IllegalArgumentException("Deposit amount must be positive");
        }
        if (amount > 1_000_000) {
            throw new IllegalArgumentException("Deposit amount is too large");
        }
        
        // Execute
        this.balance += amount;
        System.out.printf("✓ Deposited: %.2f | New balance: %.2f\n", amount, this.balance);
    }
    
    public void withdraw(double amount) {
        // Validation 1: ตรวจสอบ amount
        if (amount <= 0) {
            throw new IllegalArgumentException("Withdrawal amount must be positive");
        }
        
        // Validation 2: ตรวจสอบ balance
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance");
        }
        
        // Validation 3: ตรวจสอบ daily limit
        if (this.dailyWithdrawnAmount + amount > this.dailyWithdrawalLimit) {
            throw new IllegalArgumentException(
                "Daily withdrawal limit exceeded. Limit: " + dailyWithdrawalLimit);
        }
        
        // Execute
        this.balance -= amount;
        this.dailyWithdrawnAmount += amount;
        System.out.printf("✓ Withdrawn: %.2f | New balance: %.2f\n", amount, this.balance);
    }
    
    public void transfer(BankAccount recipient, double amount) {
        // Validation 1: recipient ไม่ null
        if (recipient == null) {
            throw new IllegalArgumentException("Recipient account cannot be null");
        }
        
        // Validation 2: ตรวจสอบ amount
        if (amount <= 0) {
            throw new IllegalArgumentException("Transfer amount must be positive");
        }
        
        // Validation 3: ตรวจสอบ balance
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance for transfer");
        }
        
        // Execute
        this.withdraw(amount);
        recipient.deposit(amount);
        System.out.printf("✓ Transferred %.2f from %s to %s\n",
                         amount, this.accountNumber, recipient.getAccountNumber());
    }
    
    public void displayInfo() {
        System.out.printf("Account: %s | Holder: %s | Balance: %.2f\n",
                         accountNumber, accountHolder, balance);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Create Accounts ===");
        BankAccount account1 = new BankAccount("1234567890", "John Doe", 100000);
        BankAccount account2 = new BankAccount("0987654321", "Jane Smith", 50000);
        
        account1.displayInfo();
        account2.displayInfo();
        
        System.out.println("\n=== Deposit ===");
        account1.deposit(20000);
        
        System.out.println("\n=== Withdraw ===");
        account1.withdraw(15000);
        
        System.out.println("\n=== Transfer ===");
        account1.transfer(account2, 10000);
        
        System.out.println("\n=== Error: Insufficient Balance ===");
        try {
            account2.withdraw(100000);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
        
        System.out.println("\n=== Error: Invalid Account ===");
        try {
            BankAccount bad = new BankAccount("123", "John", 50000);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Best Practices: Getter/Setter ที่ดี

1. Naming Convention ที่ถูก

java// ✓ ดี
public String getName() { }
public void setName(String name) { }

public int getAge() { }
public void setAge(int age) { }

public boolean isActive() { }  // boolean uses "is"
public void setActive(boolean active) { }

// ❌ ไม่ดี
public String GetName() { }        // CapitalG
public void setname(String name) { }  // lowercase n
public boolean getActive() { }  // should be isActive()
public void set_name(String name) { }  // underscore

2. Validation ที่สำคัญ

java// ✓ ดี: มี validation
public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("Age must be between 0 and 150");
    }
    this.age = age;
}

// ❌ ไม่ดี: ไม่มี validation
public void setAge(int age) {
    this.age = age;
}

3. Exception ที่ชัดเจน

java// ✓ ดี: Error message ชัดเจน
if (email == null) {
    throw new IllegalArgumentException(
        "Email cannot be null. Expected format: [email protected]");
}

// ❌ ไม่ดี: Message สั้นเกินไป
if (email == null) {
    throw new IllegalArgumentException("Invalid email");
}

4. Getter ที่ safe

java// ✓ ดี: ไม่ให้แก้ได้
public List<String> getCourses() {
    return new ArrayList<>(this.courses);  // copy
}

// ❌ ไม่ดี: ให้แก้ได้โดยตรง
public List<String> getCourses() {
    return this.courses;  // original reference
}

5. ไม่มี Setter สำหรับบางที่

java// ✓ ดี: ไม่มี setter สำหรับ ID (immutable)
public class Student {
    private String studentID;
    
    public String getStudentID() {
        return studentID;
    }
    
    // ❌ ไม่มี setStudentID()
    // ID ตั้งแต่เริ่มต้น ไม่เปลี่ยน
}

// ✓ ดี: ไม่มี setter สำหรับ password (only verify)
public class Account {
    private String password;
    
    public boolean verifyPassword(String input) {
        return password.equals(input);
    }
    
    // ❌ ไม่มี getPassword()
    // ไม่ให้อ่าน password
}

สรุป: Getter/Setter ที่ดี

text┌─────────────────────────────────────────┐
│  GETTER (อ่านค่า)                      │
├─────────────────────────────────────────┤
│ ✓ Naming: getName()                    │
│ ✓ Simple: return value                 │
│ ✓ Safe: return copy (if needed)        │
│ ✓ Computed: คำนวณหาค่าได้             │
│ ✓ No side effects                      │
│                                         │
│  SETTER (เปลี่ยนค่า)                   │
├─────────────────────────────────────────┤
│ ✓ Naming: setName(Type value)          │
│ ✓ Validation: check before assign      │
│ ✓ Clear error: throw exception         │
│ ✓ Call other setters: reuse logic      │
│ ✓ No returning value (void)            │
│                                         │
│  VALIDATION (ตรวจสอบ)                 │
├─────────────────────────────────────────┤
│ ✓ Null check: != null                  │
│ ✓ Empty check: !isEmpty()              │
│ ✓ Range check: min/max                 │
│ ✓ Format check: regex/pattern          │
│ ✓ Business logic: domain rules         │
│                                         │
└─────────────────────────────────────────┘

บทความนี้เป็นส่วนหนึ่งของรายวิชา Object-Oriented Programming with Java สำหรับนักศึกษาวิศวกรรมคอมพิวเตอร์และวิศวกรรมซอฟต์แวร์