GRASP (General Responsibility Assignment)

บทนำ: จะให้ความรับผิดชอบแก่ class ไหน?

SOLID เป็นหลักการสูง ที่บอกว่า “ควรเป็นแบบนี้” แต่ GRASP เป็นคำแนะนำที่ปฏิบัติได้จริง – มันตอบคำถาม “ในสถานการณ์นี้ ควรให้ class ไหนรับผิดชอบสิ่งนี้?”

GRASP มี 9 หลักการ แต่ในที่นี้เราจะศึกษา 5 หลักการหลัก ที่ใช้บ่อยที่สุด


GRASP: 5 หลักการหลัก

text1. Creator - ใครควรสร้าง object?
2. Information Expert - ใครควรเก็บข้อมูล?
3. Low Coupling - ลดการขึ้นต่อกัน
4. High Cohesion - เพิ่มความสัมพันธ์ภายใน
5. Controller - ใครควรควบคุมการไหลของการทำงาน?

1. Creator: ใครควรสร้าง Object?

หลักการ

Creator = ถ้า class A มี method ที่ใช้หรือสร้าง class B หลายครั้ง และ A “สำเร็จการศึกษา” ด้วยการใช้ B แล้ว A ควรจะสร้าง B

❌ ไม่ดี: สร้างผิดที่

java// ❌ ไม่ดี: Client สร้าง object
public class Order {
    private List<OrderItem> items;
    
    public Order() {
        this.items = new ArrayList<>();
    }
    
    public void addItem(String productID, int quantity) {
        items.add(new OrderItem(productID, quantity));
    }
}

public class Client {
    public static void main(String[] args) {
        // Client เป็นคนสร้าง Order
        Order order = new Order();
        order.addItem("P001", 2);
        
        // Client เองต้องรู้ว่า OrderItem เป็นอะไร
        // ถ้า OrderItem เปลี่ยน constructor Client ต้องแก้ด้วย
    }
}

✓ ดี: สร้างในที่เหมาะสม

java// ✓ ดี: Order เป็นคนสร้าง OrderItem

public class OrderItem {
    private String productID;
    private int quantity;
    
    public OrderItem(String productID, int quantity) {
        this.productID = productID;
        this.quantity = quantity;
    }
}

public class Order {
    private List<OrderItem> items;
    
    public Order() {
        this.items = new ArrayList<>();
    }
    
    // Order เป็นคนสร้าง OrderItem
    public void addItem(String productID, int quantity) {
        OrderItem item = new OrderItem(productID, quantity);
        items.add(item);
    }
    
    public int getItemCount() {
        return items.size();
    }
}

public class Client {
    public static void main(String[] args) {
        // Client เพียง "ใช้" Order ไม่ต้องรู้ OrderItem
        Order order = new Order();
        order.addItem("P001", 2);
        order.addItem("P002", 1);
        System.out.println("จำนวนสินค้า: " + order.getItemCount());
    }
}

Output:

textจำนวนสินค้า: 2

ข้อดี:

  • Client ไม่ต้องรู้รายละเอียด OrderItem
  • Order เป็นผู้เชี่ยวชาญในการสร้าง OrderItem
  • เปลี่ยน OrderItem ไม่ต้องแก้ Client

2. Information Expert: ใครควรเก็บข้อมูล?

หลักการ

Information Expert = class ที่เก็บข้อมูล (data) ควรเป็นคนทำการดำเนินการกับข้อมูลนั้นๆ

❌ ไม่ดี: ถามข้อมูล แล้วไปคำนวณที่อื่น

java// ❌ ไม่ดี: Order ให้ข้อมูลออก คนอื่นไปคำนวณ
public class OrderItem {
    private double price;
    private int quantity;
    
    public double getPrice() { return price; }
    public int getQuantity() { return quantity; }
}

public class Order {
    private List<OrderItem> items;
    
    // ให้ข้อมูลออก
    public List<OrderItem> getItems() { return items; }
}

public class InvoiceService {
    // ต้องรู้รายละเอียด OrderItem เพื่อคำนวณ
    public double calculateTotal(Order order) {
        double total = 0;
        for (OrderItem item : order.getItems()) {
            total += item.getPrice() * item.getQuantity();
        }
        return total;
    }
}

✓ ดี: ให้ class เก็บข้อมูลทำการคำนวณเอง

java// ✓ ดี: OrderItem รู้วิธีคำนวณของตัวเอง

public class OrderItem {
    private double price;
    private int quantity;
    
    public OrderItem(double price, int quantity) {
        this.price = price;
        this.quantity = quantity;
    }
    
    // OrderItem เป็นผู้เชี่ยวชาญ คำนวณราคาของตัวเอง
    public double getSubtotal() {
        return price * quantity;
    }
}

public class Order {
    private List<OrderItem> items;
    
    public Order() {
        this.items = new ArrayList<>();
    }
    
    public void addItem(OrderItem item) {
        items.add(item);
    }
    
    // Order รู้วิธีคำนวณ total ของตัวเอง
    public double getTotal() {
        double total = 0;
        for (OrderItem item : items) {
            total += item.getSubtotal();
        }
        return total;
    }
}

public class InvoiceService {
    // ง่ายมาก เพียงขออ total จาก Order
    public void printInvoice(Order order) {
        System.out.println("รวมทั้งสิ้น: " + order.getTotal() + " บาท");
    }
}

public class Main {
    public static void main(String[] args) {
        Order order = new Order();
        order.addItem(new OrderItem(100, 2));
        order.addItem(new OrderItem(50, 3));
        
        InvoiceService invoice = new InvoiceService();
        invoice.printInvoice(order);
    }
}

Output:

textรวมทั้งสิ้น: 350.0 บาท

ข้อดี:

  • OrderItem รู้ข้อมูลของตัวเอง จึงควรคำนวณเอง
  • InvoiceService ไม่ต้องรู้รายละเอียด
  • เปลี่ยนวิธีคำนวณ ทำได้ใน OrderItem เท่านั้น

3. Low Coupling: ลดการขึ้นต่อกัน

หลักการ

Low Coupling = ลดจำนวน “ความเชื่อมโยง” ระหว่าง classes ให้น้อยที่สุด

❌ ไม่ดี: High Coupling (ผูกกันแน่น)

java// ❌ ไม่ดี: PersonService ผูกตรงกับ DatabaseConnection
public class DatabaseConnection {
    public void connect() {
        System.out.println("เชื่อมต่อฐานข้อมูล");
    }
    
    public void executeSql(String sql) {
        System.out.println("รัน SQL: " + sql);
    }
}

public class PersonService {
    private DatabaseConnection db = new DatabaseConnection();  // ← ผูกตรงๆ
    
    public void savePerson(String name) {
        db.connect();
        db.executeSql("INSERT INTO persons VALUES ('" + name + "')");
    }
}

// ปัญหา: ถ้าต้องเปลี่ยนจาก DatabaseConnection เป็น OtherDatabase
// ต้องแก้ PersonService

✓ ดี: Low Coupling (ผ่าน interface)

java// ✓ ดี: PersonService ขึ้นอยู่กับ interface

public interface DataStore {
    void save(String entity, String data);
}

public class DatabaseConnection implements DataStore {
    @Override
    public void save(String entity, String data) {
        System.out.println("บันทึก " + entity + " ลงฐานข้อมูล");
    }
}

public class FileDataStore implements DataStore {
    @Override
    public void save(String entity, String data) {
        System.out.println("บันทึก " + entity + " ลงไฟล์");
    }
}

public class PersonService {
    private DataStore dataStore;  // ← ใช้ interface
    
    public PersonService(DataStore dataStore) {
        this.dataStore = dataStore;
    }
    
    public void savePerson(String name) {
        dataStore.save("Person", name);
    }
}

public class Main {
    public static void main(String[] args) {
        // ใช้ฐานข้อมูล
        PersonService service1 = new PersonService(new DatabaseConnection());
        service1.savePerson("สมชาย");
        
        // เปลี่ยนเป็นไฟล์ ไม่ต้องแก้ PersonService
        PersonService service2 = new PersonService(new FileDataStore());
        service2.savePerson("สมชาย");
    }
}

Output:

textบันทึก Person ลงฐานข้อมูล
บันทึก Person ลงไฟล์

ข้อดี:

  • PersonService ไม่ผูกกับ DatabaseConnection เพียงสิ่งเดียว
  • เปลี่ยน DataStore ได้ง่าย
  • ง่ายต่อการทดสอบ (test ด้วย mock DataStore)

4. High Cohesion: เพิ่มความสัมพันธ์ภายใน

หลักการ

High Cohesion = elements ที่อยู่ใน class ควร “เกี่ยวข้องกันมาก” ทำให้ class มี focus ชัด

❌ ไม่ดี: Low Cohesion

java// ❌ ไม่ดี: Utility class ที่ไม่มี focus
public class Utilities {
    // ความรับผิดชอบที่ไม่เกี่ยวข้องกัน
    
    public void sendEmail(String to, String message) {
        System.out.println("ส่ง email");
    }
    
    public void calculateTax(double amount) {
        System.out.println("คำนวณ tax");
    }
    
    public void generateReport(String data) {
        System.out.println("สร้าง report");
    }
    
    public void connectDatabase() {
        System.out.println("เชื่อมต่อฐานข้อมูล");
    }
}

✓ ดี: High Cohesion

java// ✓ ดี: แยกความรับผิดชอบ แต่ละ class มี focus

public class EmailService {
    public void send(String to, String message) {
        System.out.println("ส่ง email ไปยัง: " + to);
    }
}

public class TaxCalculator {
    public double calculate(double amount) {
        System.out.println("คำนวณ tax");
        return amount * 0.07;
    }
}

public class ReportGenerator {
    public void generate(String data) {
        System.out.println("สร้าง report: " + data);
    }
}

public class DatabaseConnection {
    public void connect() {
        System.out.println("เชื่อมต่อฐานข้อมูล");
    }
}

public class Main {
    public static void main(String[] args) {
        EmailService email = new EmailService();
        email.send("[email protected]", "Hello");
        
        TaxCalculator tax = new TaxCalculator();
        System.out.println("Tax: " + tax.calculate(1000));
        
        ReportGenerator report = new ReportGenerator();
        report.generate("Sales Report");
    }
}

Output:

textส่ง email ไปยัง: [email protected]
คำนวณ tax
Tax: 70.0
สร้าง report: Sales Report

ข้อดี:

  • แต่ละ class มีความรับผิดชอบชัดเจน
  • เข้าใจง่าย ดูแลรักษาง่าย
  • ง่ายต่อการเปลี่ยน/เพิ่ม

5. Controller: ใครควบคุม Flow?

หลักการ

Controller = มีคนคนหนึ่ง (หรือ class หนึ่ง) ควบคุมการไหลของการทำงานทั้งระบบ

❌ ไม่ดี: Flow ระเลาะระลวง

java// ❌ ไม่ดี: การไหลของงาน กระจายไปทั่ว
public class Main {
    public static void main(String[] args) {
        // การไหลของงานใน Main - ยุ่งยากมาก
        
        // Step 1: สร้าง order
        Order order = new Order();
        order.addItem("P001", 2);
        
        // Step 2: ตรวจสอบ inventory
        InventoryService inv = new InventoryService();
        inv.check("P001", 2);
        
        // Step 3: คำนวณ tax
        TaxService tax = new TaxService();
        double total = 1000;
        double taxAmount = tax.calculate(total);
        
        // Step 4: ประมวลผลการชำระเงิน
        PaymentService payment = new PaymentService();
        payment.process(total + taxAmount);
        
        // Step 5: บันทึกการสั่งซื้อ
        OrderRepository repo = new OrderRepository();
        repo.save(order);
        
        // ถ้า flow เปลี่ยน ต้องแก้ Main
    }
}

✓ ดี: มี Controller รวบรวม

java// ✓ ดี: มี OrderController เป็นตัวควบคุม

public class OrderController {
    private OrderService orderService;
    private InventoryService inventoryService;
    private PaymentService paymentService;
    
    public OrderController(OrderService os, InventoryService is, PaymentService ps) {
        this.orderService = os;
        this.inventoryService = is;
        this.paymentService = ps;
    }
    
    // Controller เป็นตัวควบคุม flow ทั้งหมด
    public void checkout(Order order) {
        // Step 1: ตรวจสอบ inventory
        if (!inventoryService.isAvailable(order)) {
            System.out.println("❌ สินค้าไม่เพียงพอ");
            return;
        }
        
        // Step 2: ประมวลผลการชำระเงิน
        double total = orderService.calculateTotal(order);
        if (!paymentService.process(total)) {
            System.out.println("❌ ชำระเงินล้มเหลว");
            return;
        }
        
        // Step 3: บันทึกการสั่งซื้อ
        orderService.saveOrder(order);
        System.out.println("✅ สั่งซื้อสำเร็จ");
    }
}

public class OrderService {
    public double calculateTotal(Order order) {
        return order.getTotal();
    }
    
    public void saveOrder(Order order) {
        System.out.println("บันทึกการสั่งซื้อ");
    }
}

public class InventoryService {
    public boolean isAvailable(Order order) {
        System.out.println("ตรวจสอบ inventory");
        return true;
    }
}

public class PaymentService {
    public boolean process(double amount) {
        System.out.println("ประมวลผลการชำระเงิน: " + amount);
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        Order order = new Order();
        order.addItem("P001", 2);
        
        OrderController controller = new OrderController(
            new OrderService(),
            new InventoryService(),
            new PaymentService()
        );
        
        controller.checkout(order);
    }
}

Output:

textตรวจสอบ inventory
ประมวลผลการชำระเงิน: 200.0
บันทึกการสั่งซื้อ
✅ สั่งซื้อสำเร็จ

ข้อดี:

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

ตัวอย่างรวม: Application ที่ใช้ GRASP

java// Entity
public class Product {
    private String id;
    private String name;
    private double price;
    
    public Product(String id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    
    public String getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }
}

// Creator Pattern
public class ShoppingCart {
    private List<CartItem> items = new ArrayList<>();
    
    public void addItem(Product product, int quantity) {
        // ShoppingCart เป็นคนสร้าง CartItem
        CartItem item = new CartItem(product, quantity);
        items.add(item);
    }
    
    // Information Expert
    public double getTotal() {
        double total = 0;
        for (CartItem item : items) {
            total += item.getSubtotal();
        }
        return total;
    }
}

// CartItem
public class CartItem {
    private Product product;
    private int quantity;
    
    public CartItem(Product product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }
    
    public double getSubtotal() {
        return product.getPrice() * quantity;
    }
}

// Data Store Interface - Low Coupling
public interface CartRepository {
    void save(ShoppingCart cart);
}

public class DatabaseCartRepository implements CartRepository {
    @Override
    public void save(ShoppingCart cart) {
        System.out.println("บันทึก shopping cart ลงฐานข้อมูล");
    }
}

// Controller - High Cohesion
public class CheckoutController {
    private CartRepository cartRepository;
    
    public CheckoutController(CartRepository repo) {
        this.cartRepository = repo;
    }
    
    public void processCheckout(ShoppingCart cart) {
        System.out.println("=== Checkout ===");
        System.out.println("ราคารวม: " + cart.getTotal() + " บาท");
        cartRepository.save(cart);
        System.out.println("✅ ชำระเงินสำเร็จ");
    }
}

// Client - ง่ายต่อการใช้งาน
public class Main {
    public static void main(String[] args) {
        Product p1 = new Product("P001", "Notebook", 50);
        Product p2 = new Product("P002", "Pen", 10);
        
        ShoppingCart cart = new ShoppingCart();
        cart.addItem(p1, 2);
        cart.addItem(p2, 3);
        
        CheckoutController checkout = new CheckoutController(
            new DatabaseCartRepository()
        );
        checkout.processCheckout(cart);
    }
}

Output:

text=== Checkout ===
ราคารวม: 130.0 บาท
บันทึก shopping cart ลงฐานข้อมูล
✅ ชำระเงินสำเร็จ

สรุป

GRASP เป็นชุดของ “คำแนะนำในการออกแบบ” ที่ช่วยให้เราตัดสินใจได้ว่า “ความรับผิดชอบของสิ่งนี้ควรให้ใคร?”

  • Creator: ใครควรสร้าง object → class ที่ใช้มากที่สุด
  • Information Expert: ใครควรทำการดำเนินการ → class ที่เก็บข้อมูล
  • Low Coupling: ลดการขึ้นต่อกัน → ใช้ interface แทน concrete class
  • High Cohesion: เพิ่มความสัมพันธ์ → class ควร focus กับสิ่งเดียว
  • Controller: ใครควบคุม flow → มี class เดียวควบคุม flow โหลก

เมื่อคุณปฏิบัติตาม GRASP อย่างสม่ำเสมอ code ของคุณจะเป็นระบบที่ “ทำความหน้าที่ของตัวเอง” ได้อย่างสิ้นสุด ไม่พึ่งพา class อื่น มากเกินไป และง่ายต่อการแก้ไขหรือเพิ่มเติมในอนาคต นี่คือสถาปัตยกรรมที่ดีของ object-oriented program