Skip to content

บทที่ 3 — Design Patterns

← บทที่ 2 | สารบัญ | บทที่ 4 →

Design Pattern = solution ที่ใช้ซ้ำสำหรับ problem ที่เจอบ่อย

หลังจบบท คุณจะ:

  • รู้จัก pattern ที่ใช้บ่อยจริง (ไม่ใช่ 23 ตัวของ GoF ทั้งหมด)
  • เห็นว่า pattern ไหนใช้เมื่อไหร่
  • เห็น pattern ใน Spring, React ที่ใช้อยู่ทุกวัน
  • ไม่ "ยัด pattern" ที่ไม่จำเป็น

ตัวอย่างก่อน: pattern คืออะไรในชีวิตจริง

ลองนึกถึงสถานการณ์: คุณต้องเลือก algorithm sort (bubble, quick, merge) — เปลี่ยนได้ตามต้องการ → นี่คือ Strategy pattern ตัวอย่างจาก code ที่เคยเขียน:

java
// เคย if/else เลือก algorithm?
if (type.equals("BUBBLE")) bubbleSort(items);
else if (type.equals("QUICK")) quickSort(items);

แค่ตั้งชื่อให้รู้จักกัน "นี่คือ Strategy" — สื่อสารทีมรู้เรื่องทันที

Pattern = vocabulary — ทำให้สื่อกันได้ ("ใช้ Strategy ที่นี่") — ไม่ใช่ goal

💡 หมายเหตุยุค 2020+: ภาษา modern (Java 8+, Kotlin, TS, Python) ลดความจำเป็นของหลาย pattern ลง — Strategy = function, Singleton = DI container (Spring, Guice), Factory = DI container, Observer = callback / RxJS / EventEmitter อ่าน pattern เป็น vocabulary ไม่ใช่ เป้าหมาย


1. ประเภท Design Patterns (GoF, 1994)

🔤 GoF = Gang of Four (แก๊งสี่คน) — ชื่อเล่นของผู้เขียน 4 คนแห่งหนังสือ Design Patterns ปี 1994 ที่จัดหมวด pattern ออกเป็น 3 ประเภทด้านล่าง

ผู้เขียน 4 คน (พิมพ์ในชื่อหนังสือเรียงตามชื่อ):

  • Erich Gamma (เอ-ริช แกม-มา)
  • Richard Helm (ริ-ชาร์ด เฮล์ม)
  • Ralph Johnson (รัล์ฟ จอห์น-สัน)
  • John Vlissides (จอห์น วลิส-ซิ-เดส)

3 ประเภทใหญ่

ประเภททำอะไรตัวอย่าง
Creational (ครี-เอ-ชั่น-แนล = สร้าง)สร้าง objectFactory, Builder, Singleton
Structural (สตรัค-เจอ-รัล = โครงสร้าง)combine object (ประกอบ object เข้าด้วยกัน)Adapter, Decorator, Facade
Behavioral (บี-เฮฟ-วิ-เออ-รัล = พฤติกรรม)interaction ของ object (การโต้ตอบกันระหว่าง object)Strategy, Observer, Command

23 GoF patterns — ตารางอ้างอิงพร้อมคำอ่าน

#ประเภทPatternคำอ่านความหมายไทย
1CreationalAbstract Factoryแอ๊บ-สแตร็คท์ แฟค-โท-รี่โรงงานของโรงงาน (สร้างตระกูล object)
2CreationalBuilderบิ้ล-เดอร์ผู้สร้าง (ทีละขั้น)
3CreationalFactory Methodแฟค-โท-รี่ เม-ธ็อดวิธีโรงงาน
4CreationalPrototypeโพร-โท-ไทป์ต้นแบบ (clone)
5CreationalSingletonซิง-เกิ้ล-ตันแบบเดี่ยว (instance เดียว)
6StructuralAdapterอะ-แด๊พ-เตอร์หัวแปลง
7StructuralBridgeบริดจ์สะพาน (แยก abstraction กับ implementation)
8StructuralCompositeคอม-โพ-สิทโครงสร้างต้นไม้
9StructuralDecoratorเด-คอ-เร-เตอร์ตัวประดับ/ตัวห่อเพิ่มของ
10StructuralFacadeฟา-ซ้าดด้านหน้า/หน้ากาก
11StructuralFlyweightฟลาย-เวทน้ำหนักเบา (แชร์ object)
12StructuralProxyพร็อก-ซี่ตัวแทน
13BehavioralChain of Responsibilityเชน ออฟ รี-สปอน-ซิ-บิ-ลิ-ตี้ลูกโซ่ความรับผิดชอบ
14BehavioralCommandคอม-แมนด์คำสั่ง (encapsulate action)
15BehavioralInterpreterอิน-เทอร์-พรี-เตอร์ตัวตีความ (parser)
16BehavioralIteratorอิ-เท-เรเตอร์ตัววนซ้ำ
17BehavioralMediatorมี-เดีย-เตอร์ผู้ประสานงานกลาง
18BehavioralMementoเม-เม็น-โต้บันทึก/snapshot
19BehavioralObserverออบ-เซิร์ฟ-เวอร์ผู้สังเกต/ผู้ติดตาม
20BehavioralStateสเตทสถานะ
21BehavioralStrategyสแตร-ทิ-จี้กลยุทธ์ (สลับ algorithm)
22BehavioralTemplate Methodเท็ม-เพลท เม-ธ็อดโครงวิธีการ
23BehavioralVisitorวิ-ซิ-เตอร์ผู้เยี่ยม (operation ข้าม class)

หนังสือนี้ — focus 10 patterns ที่ใช้บ่อย


Part 1: Creational

2. Factory — สร้าง object โดยไม่ระบุ class

🔤 Factory pattern (แฟค-โท-รี่ = รูปแบบ "โรงงาน" สร้าง object) — client ไม่ต้องรู้ว่า new class ตัวไหน บอก factory แค่ "ขอแบบนี้" พอ

ใช้เมื่อ: ต้องสร้าง object หลายแบบที่ implement interface เดียวกัน + ตัวเลือกอาจเพิ่มในอนาคต อย่าใช้เมื่อ: มีแค่ 1-2 class + ไม่เพิ่ม → new ตรง ๆ ง่ายกว่า

Factory pattern ย้ายการตัดสินใจ "สร้าง object ตัวไหน" ออกจาก client ไปไว้ที่เดียว — แทนที่จะมี if/else new ... กระจายทุกที่ client แค่บอก "ขอ payment processor แบบ STRIPE" แล้ว factory จัดการให้ ทำให้เพิ่มชนิดใหม่ได้โดยไม่ต้องแก้ client (JDBC, Spring @Bean ก็เป็น factory):

java
// ❌ Client knows concrete class
PaymentProcessor p;
if (type.equals("STRIPE")) p = new StripeProcessor(apiKey, secret);
else if (type.equals("PAYPAL")) p = new PayPalProcessor(clientId, secret);
java
// ✅ Factory
class PaymentProcessorFactory {
    public static PaymentProcessor create(String type) {
        return switch (type) {
            case "STRIPE" -> new StripeProcessor(getStripeConfig());
            case "PAYPAL" -> new PayPalProcessor(getPaypalConfig());
            default -> throw new IllegalArgumentException("Unknown type: " + type);
        };
    }
}

PaymentProcessor p = PaymentProcessorFactory.create(type);

💡 Java 14+ switch expression: switch (x) { case "A" -> ...; case "B" -> ...; } คือ syntax ใหม่ — return ค่าโดยตรง ไม่ต้องเขียน break (ผู้ใช้ Java 8/11 ใช้ switch (x) { case "A": return ...; } แทน)

Factory vs Abstract Factory — ต่างยังไง?

⚠️ คน confuse บ่อย — เป็น 2 pattern แยกกัน:

  • Factory (Factory Method) — สร้าง product เดียว (เช่น PaymentProcessor) โดยไม่ระบุ class ตรง ๆ ใช้เมื่อ "ขอ product 1 ชิ้นแบบไม่ระบุ concrete class"
  • Abstract Factory — สร้าง ตระกูล (family) ของ product หลายตัวที่เข้ากัน (เช่น Button + Input + Modal ที่ทุกตัวเป็นธีม Material) ใช้เมื่อ "ขอชุดของของที่ต้องเข้ากันทั้งชุด"

Abstract Factory — ตระกูล class

สมมุติทำ UI library — ทุกธีม (Material, Tailwind) มี Button/Input/Modal ที่ "เข้าธีมกัน" — Abstract Factory การันตีว่าทั้งชุดจะเป็นธีมเดียวกัน

java
interface UIComponentFactory {
    Button createButton();   // ปุ่ม
    Input createInput();     // ช่องกรอกข้อความ
    Modal createModal();     // หน้าต่าง popup
}

class MaterialUIFactory implements UIComponentFactory { ... }   // ทุก component สไตล์ Material
class TailwindUIFactory implements UIComponentFactory { ... }   // ทุก component สไตล์ Tailwind

Button/Input/Modal เป็น UI component ทั่ว ๆ ไปที่ library อย่าง Material UI / Bootstrap / Tailwind UI มีให้ใช้ — ที่นี่ใช้เป็นตัวอย่างเฉย ๆ ไม่ต้องเข้าใจ implementation

ใน Real World

  • JDBCConnection conn = DriverManager.getConnection(url); (Driver = factory)
  • Spring @Bean method = factory method
  • Optional.of(x), List.of(...) = factory

3. Builder — สร้าง object ที่มี option เยอะ

🔤 Builder (บิ้ล-เดอร์ = ผู้สร้าง) — สร้าง object ทีละ field แบบ chain method telescoping constructor (เทเลส-โค-ปิ้ง = แบบกล้องโทรทรรศน์/ขยายต่อกัน) — มี constructor overload หลายตัว แต่ละตัวยาวขึ้นเรื่อย ๆ

ใช้เมื่อ: object มี field เยอะ (5+) หรือมี optional field เยอะ อย่าใช้เมื่อ: มี field 2-3 ตัวที่ required ทั้งหมด → constructor ปกติพอ

object ที่มี field เยอะ (โดยเฉพาะ optional) ถ้าใช้ constructor จะกลายเป็น "telescoping constructor" ที่อ่านไม่ออก (new User("Anna", null, 25, null, ...)) — Builder แก้ด้วยการ set ทีละ field แบบมีชื่อ อ่านง่ายและบังคับ required field ได้:

java
// ❌ Telescoping constructor
new User(name, email, age, phone, address, city, country, role, isActive, ...);
new User("Anna", "a@x.com", 25);
new User("Anna", "a@x.com", 25, "0812345678");
// 8 constructor overload — readable ไหม?
java
// ✅ Builder
User user = User.builder()
    .name("Anna")
    .email("a@x.com")
    .age(25)
    .phone("0812345678")
    .role("ADMIN")
    .build();

Lombok @Builder

java
@Builder
@Data
public class User {
    private String name;
    private String email;
    private int age;
    private String phone;
}

→ Auto-generate builder

ใน Real World

  • HTTP request — Request.builder().url(...).header(...).body(...).build()
  • Spring's RestClient.builder()...
  • StringBuilder — new StringBuilder().append("a").append("b").toString()
  • SQL DSL — select("name").from("users").where(...).build()

4. Singleton — instance เดียวทั้ง app

🔤 Singleton (ซิง-เกิ้ล-ตัน = แบบเดี่ยว) — class ที่มี instance เดียวใน app ทั้งหมด

⚠️ เริ่มต้นอ่านที่นี่ก่อน — Singleton ถือเป็น anti-pattern ในโค้ดแอปทั่วไป

ในยุค DI container (Spring, Guice, Dagger) เกือบทุกกรณีให้ใช้ DI scope แทนการเขียน Singleton เอง — เพราะ:

  • Global state ที่ซ่อนอยู่ — ใครก็เรียก getInstance() ได้ทุกที่ ผ่านไม่ผ่าน method signature ก็ไม่รู้
  • Test ยาก — mock ไม่ได้ง่าย ๆ (static method) → unit test ผูกกับ instance จริง
  • Hidden coupling — class ที่เรียก Database.getInstance() ผูกกับ Database แน่น ไม่มีทาง inject ตัวอื่น
  • Thread safety ยุ่ง — ถ้าเขียนเองพลาดง่ายมาก (ดูตัวอย่าง ❌ ด้านล่าง)
  • Replace ยาก — เปลี่ยน implementation ต้องแก้ทุกที่ที่เรียก

แนวทาง modern: ให้ DI container จัดการ lifecycle (@Component + Spring สร้าง singleton ให้ — mock ได้ง่าย, inject แทน inject test double ได้) → ดูตอน ใน Spring ด้านล่าง

java
// ❌ Classic — NOT thread-safe (อย่าใช้ในโลกจริง)
class Database {
    private static Database instance;
    private Database() {}
    public static Database getInstance() {
        if (instance == null) instance = new Database();   // ❌ race condition!
        return instance;
    }
}

🐛 bug: ถ้า 2 thread เรียก getInstance() พร้อมกัน ทั้งคู่อาจเห็น instance == null แล้วต่างคน new Database() คนละตัว → instance เลย "2 ตัว" → ทำลายหลัก singleton + อาจ leak resource (DB connection คู่กัน) วิธีแก้ canonical คือ double-checked locking กับ volatile หรือใช้ Holder idiom (ดูถัดไป)

Thread-safe + lazy (Bill Pugh's idiom)

java
// ✅ Initialization-on-demand Holder idiom (Bill Pugh)
class Database {
    private static class Holder {
        static final Database INSTANCE = new Database();
    }
    
    public static Database getInstance() {
        return Holder.INSTANCE;
    }
    
    private Database() {}
}

ทำไม thread-safe: JVM การันตีว่า class จะ load ครั้งเดียว + initialize static field ครั้งเดียวเท่านั้น (Java Language Specification §12.4) — Holder class ถูก load ตอนเรียก Holder.INSTANCE ครั้งแรกเท่านั้น → lazy + thread-safe โดยไม่ต้องใช้ synchronized (ซึ่งจะช้าทุก call)

⚠️ caveat: ถ้า Database constructor throwHolder class fail to initialize → call ครั้งถัดไปจะได้ NoClassDefFoundError (cryptic — debug ลำบาก) วิธีแก้: จัดการ exception ใน constructor หรือใช้ factory method ที่ handle error ชัดเจน

ใช้ Singleton ได้เมื่อ?

✅ Logger, Configuration
✅ DB connection pool
✅ Cache instance

❌ Business service → use DI (Spring container manages "singleton" for you)

ใน Spring

java
@Service
class UserService { ... }

Spring default scope = singleton — แต่เป็น managed singleton → mock + test ง่าย

🧭 Singleton pattern vs. Spring managed singleton — ต่างกันยังไง?

  • GoF Singleton pattern — class จัดการ instance ของตัวเอง (getInstance()) + global state → anti-pattern
  • Spring managed singleton@Service class ปกติ ที่ Spring container เก็บ 1 instance ไว้แล้ว inject ให้ผู้ใช้ — class ไม่รู้ ว่าเป็น singleton (testable + replaceable) → OK

ความต่าง = ใคร own lifecycle: class เองตอนแรก, container ตอนหลัง


Part 2: Structural

5. Adapter — แปลง interface

🔤 Adapter (อะ-แด๊พ-เตอร์ = หัวแปลง) — เหมือนหัวแปลงปลั๊กไฟ (220V → USB)

ใช้เมื่อ: ต้องใช้ class/library ภายนอกที่ interface ไม่ตรงกับของเรา + แก้ class นั้นไม่ได้ อย่าใช้เมื่อ: คุมทั้งสองฝั่งได้ → แก้ interface ตรง ๆ ดีกว่า

Adapter เหมือนหัวแปลงปลั๊กไฟ — เมื่อ library ภายนอกมี interface ไม่ตรงกับที่ระบบเราใช้ Adapter ห่อมันให้เข้ากับ interface ของเรา ทำให้ใช้ของเก่า/ของนอกได้โดยไม่ต้องแก้ทั้งสองฝั่ง (เช่น Arrays.asList() แปลง array เป็น List):

java
// External lib มี class นี้
class LegacyXmlPaymentApi {
    public String processXml(String xmlRequest) { ... }
}

// แต่ระบบเราใช้ interface
interface PaymentProcessor {
    PaymentResult process(PaymentRequest req);
}

// ✅ Adapter
class LegacyXmlPaymentAdapter implements PaymentProcessor {
    private final LegacyXmlPaymentApi legacy = new LegacyXmlPaymentApi();
    
    @Override
    public PaymentResult process(PaymentRequest req) {
        String xml = toXml(req);                       // placeholder — แปลง object → XML string
        String responseXml = legacy.processXml(xml);
        return parseResponse(responseXml);             // placeholder — แปลง XML string → PaymentResult
    }
}

toXml() / parseResponse() ในตัวอย่างเป็น placeholder ที่จะใช้ XML library (เช่น JAXB, Jackson XML) implement ในของจริง — ที่นี่เน้นแสดงรูปแบบ Adapter

→ ใช้ legacy API กับ modern interface ได้

ใน Real World

  • Java Stream Arrays.asList() — adapt array → List
  • Spring's MessageSourceAccessor — wrap MessageSource
  • React useState wrapper around component class API

6. Decorator — เพิ่มความสามารถโดยไม่แก้ class เดิม

🔤 Decorator (เด-คอ-เร-เตอร์ = ตัวประดับ/ตัวห่อเพิ่มของ) — ห่อ object เดิมเพื่อเพิ่มความสามารถ + ห่อซ้อนได้

ใช้เมื่อ: ต้องการเพิ่มความสามารถ (logging, retry, caching) โดยไม่แก้ class เดิม + ผสมแบบยืดหยุ่น อย่าใช้เมื่อ: behavior มีแค่ 1-2 ตัว → inheritance ง่ายกว่า + เร็วกว่า

Decorator ห่อ object เดิมเพื่อเพิ่มความสามารถโดยไม่แก้ class เดิม — และ wrap ซ้อนกันได้ (กาแฟ + นม + น้ำตาล) เป็น composition แทน inheritance ตามหลัก OCP เจอบ่อยใน Java I/O (BufferedReader(InputStreamReader(...))) และ React HOC:

java
import java.math.BigDecimal;

interface Coffee {
    BigDecimal cost();           // ✅ BigDecimal กับเงิน (ไม่ใช่ double — บทที่ 0 mindset)
    String description();
}

class BasicCoffee implements Coffee {
    public BigDecimal cost() { return new BigDecimal("50.00"); }
    public String description() { return "Coffee"; }
}

class MilkDecorator implements Coffee {
    private final Coffee coffee;
    
    public MilkDecorator(Coffee coffee) { this.coffee = coffee; }
    
    public BigDecimal cost() { return coffee.cost().add(new BigDecimal("15.00")); }
    public String description() { return coffee.description() + " + Milk"; }
}

class SugarDecorator implements Coffee {
    private final Coffee coffee;
    public SugarDecorator(Coffee coffee) { this.coffee = coffee; }
    public BigDecimal cost() { return coffee.cost().add(new BigDecimal("5.00")); }
    public String description() { return coffee.description() + " + Sugar"; }
}

// ใช้
Coffee order = new SugarDecorator(new MilkDecorator(new BasicCoffee()));
order.cost();           // 70.00
order.description();    // "Coffee + Milk + Sugar"

ใน Real World

  • Java I/O: new BufferedReader(new InputStreamReader(new FileInputStream(...))) — แต่ละชั้นเพิ่มความสามารถ (file → bytes → chars → buffered lines) เป็นตัวอย่าง Decorator คลาสสิก
  • React HOC: withAuth(withTheme(MyComponent))
  • Spring AOP: @Transactional ครอบ method หนึ่ง — สร้าง proxy ห่อ method เดิมแล้วเพิ่ม transaction management ก่อน/หลัง → เป็น Decorator (@Cacheable, @Async ก็ใช้กลไกเดียวกัน) ตัวอื่นเช่น Middleware (Express, Koa), Servlet Filter ก็แนวคิดเดียวกัน — ไว้อ่านเจาะลึกในบท Spring

7. Facade — Interface เดียวที่ครอบหลาย sub-system

🔤 Facade (ฟา-ซ้าด — รากฝรั่งเศส = ด้านหน้า/หน้ากาก) — เหมือนพนักงานต้อนรับโรงแรม: client คุยกับคนเดียว เบื้องหลังมีหลายแผนกทำงาน

ใช้เมื่อ: ระบบมี sub-system ซับซ้อนหลายตัว + ต้องการ API เรียบง่ายให้ใช้ อย่าใช้เมื่อ: มี sub-system ไม่กี่ตัว → Facade เพิ่ม layer โดยไม่จำเป็น

💡 หลักการที่หนุน Facade คือ Law of Demeter ("Don't Talk to Strangers" — อย่าคุยกับคนแปลกหน้า) เสนอโดย Ian Holland และทีม Demeter project (Northeastern University, 1987) — กฎคือ: object ควรคุยกับ "เพื่อนใกล้ตัว" เท่านั้น (field, parameter, ตัวเอง) ไม่ใช่ chain ลึก ๆ เช่น order.getCustomer().getAddress().getCity() — Facade ลดการที่ client ต้องคุยข้าม sub-system ตรง ๆ

เมื่อการทำงานหนึ่งต้องประสานหลาย sub-system (checkout = inventory + payment + shipping + notification) client ที่เรียกตรงทุกตัวจะซับซ้อนและผูกแน่น — Facade ให้ interface เดียวที่ครอบทั้งหมด client เรียก checkout() ทีเดียว ส่วนความซับซ้อนซ่อนอยู่หลัง facade:

java
// ❌ Client deal with 5 sub-systems
class CheckoutFlow {
    public void checkout(Order order) {
        InventoryService inv = new InventoryService();
        if (!inv.reserve(order)) throw new ...;
        
        PaymentGateway gw = new PaymentGateway();
        PaymentResult result = gw.charge(order.total(), order.card());
        if (!result.success()) {
            inv.release(order);
            throw new ...;
        }
        
        ShippingService ship = new ShippingService();
        ship.createShipment(order);
        
        NotificationService notif = new NotificationService();
        notif.sendOrderConfirmation(order);
    }
}
java
// ✅ Facade
class CheckoutFacade {
    private final InventoryService inventory;
    private final PaymentGateway payment;
    private final ShippingService shipping;
    private final NotificationService notification;
    
    public OrderConfirmation checkout(Order order) {
        try {
            inventory.reserve(order);
            payment.charge(order.total(), order.card());
            shipping.createShipment(order);
            notification.sendOrderConfirmation(order);
            return new OrderConfirmation(order.getId());
        } catch (Exception e) {
            // rollback logic
            throw e;
        }
    }
}

// Client
checkoutFacade.checkout(order);    // ⭐ simple

inventory, payment, shipping, notification ในตัวอย่างเป็น service ของเราเอง (@Service class ใน Spring) ไม่ใช่ library ภายนอก — เขียนเป็น placeholder name เพื่อสื่อภาพรวม

→ Client ใช้ 1 interface — ไม่ต้องรู้ inventory + payment + shipping

ใน Real World

  • Spring JdbcTemplate = facade over JDBC API
  • jQuery = facade over DOM API
  • AWS SDK = facade over AWS REST API

Part 3: Behavioral

8. Strategy — ⭐ ใช้บ่อยที่สุด

🔤 Strategy (สแตร-ทิ-จี้ = กลยุทธ์) — สลับ algorithm ได้โดยไม่แก้ code เดิม

ใช้เมื่อ: มีหลาย algorithm/พฤติกรรมที่สลับกันได้ + จะเพิ่มในอนาคต อย่าใช้เมื่อ: มีแค่ 1 algorithm + ไม่เปลี่ยน — Strategy เพิ่ม abstraction โดยไม่จำเป็น

💡 ในภาษา modern: Strategy = function ที่ส่งเข้าไป (lambda, function value) ไม่ต้องมี class ก็ได้ → ใช้ higher-order function แทน class hierarchy เพราะง่ายและตรงกว่า

(เห็นใน OCP บทที่ 2)

java
interface SortStrategy<T> {
    List<T> sort(List<T> items);
}

class BubbleSort<T extends Comparable<T>> implements SortStrategy<T> { ... }
class QuickSort<T extends Comparable<T>> implements SortStrategy<T> { ... }
class MergeSort<T extends Comparable<T>> implements SortStrategy<T> { ... }

class Sorter<T> {
    private final SortStrategy<T> strategy;
    
    public Sorter(SortStrategy<T> strategy) { this.strategy = strategy; }
    
    public List<T> sort(List<T> items) {
        return strategy.sort(items);
    }
}

// ใช้
Sorter<Integer> s = new Sorter<>(new QuickSort<>());

Modern Strategy = Lambda

java
class Sorter<T> {
    public List<T> sort(List<T> items, Comparator<T> comparator) {
        // ...
    }
}

sorter.sort(items, (a, b) -> a.compareTo(b));
sorter.sort(items, Comparator.comparing(User::getName));   // ดูบท Java functional ภายหลัง

Comparator คือ functional interface ใน Java (ดูในบท Java/Functional ภายหลัง) — รับ lambda หรือ method reference (User::getName) ได้

→ Strategy pattern ใน 1 line

เทียบ 3 ภาษา — ในภาษาที่ function เป็น first-class, Strategy "หายไป" กลายเป็นแค่ฟังก์ชัน

insight สำคัญ: GoF ออกแบบ Strategy ด้วย class + interface เพราะ Java ยุคเก่าส่งฟังก์ชันไปมาไม่ได้ — พอภาษารองรับ function เป็นค่าได้ (Java ยุคใหม่, JS/TS, Python) "strategy" ก็คือตัวฟังก์ชันที่ส่งเข้าไปตรง ๆ

typescript
// TypeScript — strategy = ฟังก์ชันที่ส่งเข้าไป ไม่ต้องมี class/interface
type SortStrategy<T> = (a: T, b: T) => number;

function sortBy<T>(items: T[], strategy: SortStrategy<T>): T[] {
  return [...items].sort(strategy);
}

sortBy(users, (a, b) => a.name.localeCompare(b.name));   // เปลี่ยน strategy = เปลี่ยนฟังก์ชัน
python
# Python — ส่งฟังก์ชัน (หรือ key) เข้าไปตรง ๆ
def sort_by(items, key):
    return sorted(items, key=key)

sort_by(users, key=lambda u: u.name)     # strategy = lambda

💡 จับหลัก: Strategy = "สลับพฤติกรรมได้โดยไม่แก้โค้ดเดิม" — Java เก่าทำผ่าน class, ภาษาใหม่ทำผ่านการส่งฟังก์ชัน เป้าหมายเดียวกัน อย่าติดที่รูปแบบ class

ใน Real World

  • Spring @Component ที่ implement common interface — Spring inject all → Strategy
  • React onSubmit prop — ส่งฟังก์ชันจัดการเข้าไป = strategy
  • Validator chain — notNull().minLength(5).matches(/regex/)

9. Observer (Pub/Sub) — แจ้ง event

🔤 Observer (ออบ-เซิร์ฟ-เวอร์ = ผู้สังเกต) | Pub/Sub (พับ/ซับ = Publish/Subscribe = ประกาศ/สมัครรับ) — รูปแบบเดียวกันแต่เรียกต่างชื่อในบริบทต่างกัน (Pub/Sub มักหมายถึงผ่าน broker เช่น Kafka, Redis)

ใช้เมื่อ: 1 event ต้องการให้หลายฝั่งรับรู้และทำงานของตัวเอง + ไม่อยากให้ subject ผูกกับ observer อย่าใช้เมื่อ: มีฝั่งเดียวที่สนใจ → callback ตรง ๆ ง่ายกว่า

💡 Modern alternative: ในเคสง่าย — ใช้ functional callback (lambda) แทน Observer class hierarchy → ไม่ต้องมี interface Observer, แค่รับ Consumer<Event> พอ

Observer ให้ object หนึ่ง "แจ้งเตือน" หลาย object เมื่อมีเหตุการณ์ — เช่น order ถูกสร้างแล้ว email/inventory/analytics ต่างรับรู้และทำงานของตัวเอง โดย order ไม่ต้องรู้จักพวกนั้นโดยตรง เป็นรากฐานของ event-driven, DOM events, RxJS และ Spring @EventListener:

java
interface OrderObserver {
    void onOrderCreated(Order order);
}

class Order {
    private final List<OrderObserver> observers = new ArrayList<>();
    
    public void addObserver(OrderObserver obs) {
        observers.add(obs);
    }
    
    public void confirm() {
        // ...
        observers.forEach(o -> o.onOrderCreated(this));
    }
}

// Observers
class EmailNotifier implements OrderObserver {
    public void onOrderCreated(Order order) {
        emailService.send(...);
    }
}

class InventoryReducer implements OrderObserver { ... }
class AnalyticsTracker implements OrderObserver { ... }

⚠️ ระวัง memory leak: observers.add(obs) ใน Java/JS ถือ strong reference ไป observer — ถ้า observer ไม่ unsubscribe (removeObserver) ตอนเลิกใช้ → GC เก็บไม่ได้ทำให้ memory ค้าง (โดยเฉพาะ observer ที่อายุสั้น sub กับ subject ที่อยู่ยาว — เช่น UI component listen global event bus)

วิธีแก้:

  • explicit unsubscribe — observer call removeObserver() ใน destructor / componentWillUnmount / @PreDestroy
  • weak reference — เก็บ List<WeakReference<OrderObserver>> แทน → GC เก็บได้แม้ยังอยู่ใน list (ระวัง: weak ref อาจเป็น null เมื่อใช้ → ต้อง filter)
  • หรือใช้ library ที่จัดการให้ (Spring @EventListener, RxJS Subscription.unsubscribe())

Modern — Event-driven

java
// Spring
@EventListener
public void onOrderCreated(OrderCreatedEvent event) { ... }

applicationEventPublisher.publishEvent(new OrderCreatedEvent(order));

@EventListener + ApplicationEventPublisher คือ implementation ของ Observer ที่ Spring จัดการให้ — ดูบท Spring เพื่อรายละเอียด (ที่นี่แสดงรูปแบบเฉย ๆ)

Real World

  • DOM eventselement.addEventListener('click', handler)
  • RxJS Observable
  • React useEffect (subscribe state)
  • Kafka consumer
  • Redis Pub/Sub

10. Command — Encapsulate operation

🔤 Command (คอม-แมนด์ = คำสั่ง) — ห่อ "การกระทำ" เป็น object (action เป็นข้อมูลที่ส่งต่อ/เก็บ/undo ได้)

ใช้เมื่อ: ต้อง queue / undo / log / replay action; CQRS write side; job processor อย่าใช้เมื่อ: action เกิดและจบทันที + ไม่ต้อง undo → method call ปกติพอ

Command ห่อ "การกระทำ" เป็น object — ทำให้เก็บ, ส่งต่อ, queue, undo ได้ (object รู้ทั้งวิธี execute และ undo ตัวเอง) เป็นรากฐานของ undo/redo ใน editor, job queue, และ CQRS (command = write):

java
interface Command {
    void execute();
    void undo();
}

class CreateUserCommand implements Command {
    private final UserService service;
    private final CreateUserRequest req;
    private Long createdUserId;   // ⚠️ mutable state — ดู note ด้านล่าง
    
    public void execute() {
        User u = service.create(req);
        this.createdUserId = u.getId();
    }
    
    public void undo() {
        service.delete(createdUserId);
    }
}

// Invoker
class CommandHistory {
    // ✅ ใช้ Deque/ArrayDeque แทน java.util.Stack (legacy, synchronized, ช้า)
    private final Deque<Command> history = new ArrayDeque<>();
    
    public void execute(Command cmd) {
        cmd.execute();
        history.push(cmd);
    }
    
    public void undo() {
        if (!history.isEmpty()) history.pop().undo();
    }
}

📌 java.util.Stack เป็น legacy class (Java 1.0) — extends Vector ซึ่ง synchronize ทุก method (ช้าและ thread-safe แบบไม่จำเป็น) → ใช้ Deque interface กับ implementation ArrayDeque (Java 6+) แทน เร็วกว่าและเป็นแนวทาง modern ที่ Oracle แนะนำเอง (push/pop/peek ใช้แบบเดิมได้)

⚠️ immutability concern: createdUserId เป็น mutable field — ถ้า command ถูก execute() 2 ครั้ง หรือ thread ขนานกัน → state เพี้ยน (overwrite, race) แนวทางสะอาดกว่า: ทำ Command ให้ immutable + คืน Result object ที่บรรจุ createdUserId (เช่น record CreateUserResult(Long userId)) → invoker เก็บ result คู่กับ command ไว้ใน history

Real World

  • Undo/Redo (text editor, Figma)
  • Queue/Job processor
  • Saga pattern (microservices) — Command + Compensation
  • CQRS — Command = write, Query = read

11. Template Method — Algorithm structure ใน parent

🔤 Template Method (เท็ม-เพลท เม-ธ็อด) — parent กำหนดโครงของ algorithm + subclass override ทีละขั้น

ใช้เมื่อ: หลาย class มีโครงทำงานเหมือนกัน + ต่างกันแค่บางขั้น อย่าใช้เมื่อ: ขั้นตอนยังไม่นิ่ง / ความแตกต่างเยอะ → Strategy (composition) ยืดหยุ่นกว่า

💡 Hollywood Principle: "Don't call us, we'll call you" — Template Method ทำตามหลักนี้: parent (framework) เรียก subclass ตอนเหมาะสม subclass ไม่เรียก parent → เป็นรูปแบบหนึ่งของ Inversion of Control (IoC) ที่เป็นรากฐานของ Spring framework

💡 abstract class = class ที่ instantiate ไม่ได้ (สร้าง new ไม่ได้) มีไว้ให้ extends — มี method ปกติ (มีโค้ด) + abstract method (ไม่มีโค้ด — บังคับ subclass เขียน) ที่นี่ DataProcessor กำหนดลำดับใน process() แล้วบังคับ subclass เขียน read()/validate()/transform()/write() เอง

Template Method กำหนด "โครงของ algorithm" ไว้ใน parent (ลำดับขั้น read→validate→transform→write) แล้วให้ subclass เติมรายละเอียดแต่ละขั้น — ขั้นตอนหลักคงที่ แต่แต่ละแบบทำต่างกันได้ เจอใน framework เยอะ (Spring JdbcTemplate, lifecycle method):

java
abstract class DataProcessor {
    // Template method
    public final void process() {
        Data data = read();
        Data validated = validate(data);
        Data transformed = transform(validated);
        write(transformed);
    }
    
    protected abstract Data read();
    protected abstract Data validate(Data data);
    protected abstract Data transform(Data data);
    protected abstract void write(Data data);
}

class CsvProcessor extends DataProcessor {
    protected Data read() { /* read csv */ }
    protected Data validate(Data data) { /* csv-specific validation */ }
    protected Data transform(Data data) { /* csv-specific */ }
    protected void write(Data data) { /* write csv */ }
}

class JsonProcessor extends DataProcessor { ... }

→ Parent มี algorithm — subclass override step

⚠️ Trade-off

Template Method = inheritance — ตรวจสอบว่า "is-a" จริงไหม

Modern alternative: Strategy (composition)


12. Repository Pattern

java
interface UserRepository {
    Optional<User> findById(Long id);
    Optional<User> findByEmail(String email);
    User save(User user);
    void deleteById(Long id);
}

class JpaUserRepository implements UserRepository { ... }
class InMemoryUserRepository implements UserRepository { ... }   // for testing

ข้อดี:

  • Domain ไม่รู้ DB
  • Test ด้วย in-memory repo
  • เปลี่ยน persistence ได้

ใน Spring → extends JpaRepository<User, Long> — auto-generate


13. Specification Pattern

Specification ทำให้ "เงื่อนไขทางธุรกิจ" (active user, อายุ ≥ 18) เป็น object ที่ combine, test, reuse ได้ — แทนที่จะเขียน if ซ้อนกระจาย เอา spec มาต่อด้วย AND/OR เจอใน Spring Data JPA Specification และ validator chain:

java
interface Specification<T> {
    boolean isSatisfiedBy(T item);
}

class ActiveUserSpec implements Specification<User> {
    public boolean isSatisfiedBy(User u) { return u.isActive(); }
}

class AdultUserSpec implements Specification<User> {
    public boolean isSatisfiedBy(User u) { return u.getAge() >= 18; }
}

// Combinator
class AndSpec<T> implements Specification<T> {
    private final List<Specification<T>> specs;

    @SafeVarargs
    public AndSpec(Specification<T>... specs) {       // varargs constructor
        this.specs = List.of(specs);
    }

    public boolean isSatisfiedBy(T item) {
        return specs.stream().allMatch(s -> s.isSatisfiedBy(item));
    }
}

// ใช้
Specification<User> spec = new AndSpec<>(
    new ActiveUserSpec(),
    new AdultUserSpec()
);
users.stream().filter(spec::isSatisfiedBy).toList();

→ Filter logic = first-class object — combine, test, reuse

ใน Real World

  • Spring Data JPA Specifications (บทที่ 02 Spring Boot)
  • Validator chain
  • Predicate ใน Stream

14. Chain of Responsibility

🔤 Chain of Responsibility (เชน ออฟ รี-สปอน-ซิ-บิ-ลิ-ตี้ = ลูกโซ่ความรับผิดชอบ) — handler ต่อกันเป็นลูกโซ่ request วิ่งผ่านทีละตัว

ใช้เมื่อ: มี processing step หลาย step ที่สลับ/เพิ่ม/ลบได้ (เช่น HTTP middleware) อย่าใช้เมื่อ: ขั้นตอนคงที่ + ไม่เปลี่ยน → call ตรง ๆ ง่ายกว่า

Chain of Responsibility ต่อ handler เป็นลูกโซ่ — request วิ่งผ่านทีละตัว (auth → rate limit → validation) แต่ละตัวจัดการส่วนของมันแล้วส่งต่อ ทำให้เพิ่ม/สลับ/ลบ handler ได้ง่าย นี่คือหลักการเบื้องหลัง HTTP middleware และ Servlet/Security FilterChain:

java
abstract class Validator {
    private Validator next;
    
    public Validator setNext(Validator next) {
        this.next = next;
        return next;
    }
    
    public void validate(Request req) {
        doValidate(req);
        if (next != null) next.validate(req);
    }
    
    protected abstract void doValidate(Request req);
}

class AuthValidator extends Validator {
    protected void doValidate(Request req) {
        if (req.getToken() == null) throw new UnauthorizedException();
    }
}

class RateLimitValidator extends Validator {
    protected void doValidate(Request req) {
        if (rateLimiter.exceeded(req.getUserId())) throw new RateLimitException();
    }
}

class InputValidator extends Validator {
    protected void doValidate(Request req) {
        if (req.getBody() == null) throw new ValidationException();
    }
}

// ใช้
Validator chain = new AuthValidator();
chain.setNext(new RateLimitValidator()).setNext(new InputValidator());

chain.validate(request);

Real World

  • HTTP middleware (Express, Spring filter)
  • Servlet Filter Chain
  • Spring Security FilterChain

15. State Pattern

🔤 State (สเตท = สถานะ) — แต่ละสถานะเป็น class ที่มีพฤติกรรมต่างกัน object เปลี่ยน state = เปลี่ยน behavior

ใช้เมื่อ: object มีพฤติกรรมต่างกันตาม status + transitions ซับซ้อน (state machine) อย่าใช้เมื่อ: status มีแค่ 2-3 ตัว + behavior เปลี่ยนไม่เยอะ → enum ตรง ๆ ง่ายกว่า

เมื่อ object มีพฤติกรรมต่างกันตาม "สถานะ" (order: pending/shipped/delivered) การเขียน if/switch เช็ค status ทุก method จะรก — State pattern แยกแต่ละสถานะเป็น class ที่มีพฤติกรรมของตัวเอง object เปลี่ยนพฤติกรรมโดยเปลี่ยน state object ทำให้ state machine ชัดและเพิ่มสถานะใหม่ง่าย:

java
interface OrderState {
    void confirm(OrderContext ctx);
    void ship(OrderContext ctx);
    void cancel(OrderContext ctx);
}

class PendingState implements OrderState {
    public void confirm(OrderContext ctx) { ctx.setState(new ConfirmedState()); }
    public void ship(OrderContext ctx) { throw new IllegalStateException("Confirm first"); }
    public void cancel(OrderContext ctx) { ctx.setState(new CancelledState()); }
}

class ConfirmedState implements OrderState {
    public void confirm(OrderContext ctx) { throw new IllegalStateException("Already confirmed"); }
    public void ship(OrderContext ctx) { ctx.setState(new ShippedState()); }
    public void cancel(OrderContext ctx) { ctx.setState(new CancelledState()); }
}
// ...

class OrderContext {
    private OrderState state = new PendingState();
    
    public void setState(OrderState s) { this.state = s; }
    public void confirm() { state.confirm(this); }
    public void ship() { state.ship(this); }
    public void cancel() { state.cancel(this); }
}

ทาง alternative — enum + abstract method (Java feature ที่ enum value override method ได้แต่ละตัว):

java
public enum OrderStatus {
    PENDING {
        @Override OrderStatus confirm() { return CONFIRMED; }
        @Override OrderStatus ship() { throw new IllegalStateException(); }
    },
    CONFIRMED {
        @Override OrderStatus confirm() { throw new IllegalStateException(); }
        @Override OrderStatus ship() { return SHIPPED; }
    },
    SHIPPED { ... };
    
    abstract OrderStatus confirm();
    abstract OrderStatus ship();
}

💡 Java เอกลักษณ์: enum value แต่ละตัวเหมือน "subclass สั้น ๆ" — เขียน PENDING { ... } คือ enum value ที่ override abstract method confirm()/ship() ของ enum class เอง → ได้ state machine compact ที่ compile-time exhaustive (compiler บังคับให้ enum ทุกตัว implement abstract method)

→ State machine


16. ใน React — Patterns ที่เห็นทุกวัน

ถ้าไม่ได้ใช้ React สามารถข้ามตอนนี้ได้ — เก็บไว้เป็น reference ตอนเข้าบท Frontend / React ภายหลัง

design pattern คลาสสิกไม่ได้อยู่แค่ Java — React ก็เต็มไปด้วยมันแค่เรียกชื่อต่างกัน HOC คือ Decorator, render props คือ Strategy, Context คือ DI, useReducer คือ Command พอเห็นความเชื่อมโยงนี้ก็จะเข้าใจ React ลึกขึ้น:

  • Container/Presenter — split logic + view
  • HOC (Higher-Order Component) = Decorator (function รับ component → คืน component ใหม่ที่ทำ behavior เพิ่ม)
  • Render Props = Strategy (ส่ง render function เป็น prop → component ใช้ function นั้น render)
  • Hooks = Composition + Strategy + Custom hooks
  • Context = Singleton + Dependency Injection
  • Reducer = Command (action → state transition)
  • Compound Component = Builder ใน UI

React 2024-2026 — patterns ใหม่ที่ควรรู้

  • Server Components (Next.js App Router default) — render บน server, ส่ง HTML + serialized props ลง client → data อยู่ใกล้แหล่งกำเนิด ลด client bundle
  • Server Actions — function ทำ mutation บน server เรียกจาก client เหมือน RPC ('use server') — ลด API boilerplate
  • use hook (React 19) — รับ promise/context ใน component แล้ว Suspense รอให้เสร็จ → data fetching แบบใหม่ที่เลิกพึ่ง useEffect สำหรับ async

17. ⚠️ Anti-patterns: Pattern Overuse

java
// ❌ Pattern soup
class UserManagerFactoryProviderSingletonBuilder { ... }

→ Pattern ที่ดีที่สุด = pattern ที่จำเป็น + อ่านง่าย

กฎ Pattern

  • 🌟 รู้จัก pattern → recognize เมื่อเจอ
  • 🌟 ใช้เมื่อ problem ตรงกับ pattern
  • ❌ อย่าไป force pattern กับ code ที่ไม่ต้องการ
  • ❌ ไม่ใช่ tool ที่ใช้ทำให้ "ดูเทพ"

18. Pattern ที่ไม่ค่อยได้ใช้ (อ่านพอ)

  • Prototype — clone object
  • Bridge — แยก abstraction กับ implementation
  • Flyweight — share state ระหว่าง object เล็ก ๆ
  • Memento — snapshot state
  • Visitor — operation บน hierarchy
  • Iterator — sequential access (built-in ในภาษาส่วนใหญ่)
  • Mediator — coordinate communication
  • Interpreter — language parser

→ รู้ว่ามี + อ่านเมื่อเจอ — อย่าท่อง


19. ตัวอย่างจริง — Payment System

ปิดท้ายด้วยเคสจริงที่รวมหลาย pattern เข้าด้วยกัน — payment system ที่ใช้ Strategy (เลือก processor), Factory (สร้าง), Decorator (เพิ่ม logging/retry) ฯลฯ ในระบบจริง pattern ไม่ได้ใช้เดี่ยว ๆ แต่ประกอบกันเพื่อแก้ปัญหาที่ซับซ้อน:

ใช้หลาย pattern พร้อมกัน:

java
// 1. Strategy — payment processor
interface PaymentProcessor {
    PaymentResult charge(BigDecimal amount, PaymentMethod method);
}

// 2. Factory — create processor by type (pure factory — ไม่ cache ซ้ำกับ Spring container)
@Component
class PaymentProcessorFactory {
    private final PaymentConfig config;

    public PaymentProcessorFactory(PaymentConfig config) {
        this.config = config;
    }

    public PaymentProcessor create(String type) {
        return switch (type) {
            case "STRIPE" -> new StripeProcessor(config);
            case "PAYPAL" -> new PayPalProcessor(config);
            default -> throw new IllegalArgumentException("Unknown type: " + type);
        };
    }
}
// 💡 ถ้า processor เป็น Spring bean อยู่แล้ว → Spring container เก็บ singleton ให้
// ไม่ต้อง cache ใน factory อีก (double-cache สับสน)

// 3. Decorator — add retry, logging
class LoggingPaymentProcessor implements PaymentProcessor {
    private final PaymentProcessor inner;
    
    public PaymentResult charge(BigDecimal amount, PaymentMethod method) {
        log.info("Charging {} via {}", amount, inner);
        long start = System.currentTimeMillis();
        try {
            PaymentResult result = inner.charge(amount, method);
            log.info("Charged in {}ms: {}", System.currentTimeMillis() - start, result);
            return result;
        } catch (Exception e) {
            log.error("Charge failed", e);
            throw e;
        }
    }
}

// 4. Observer — notify on payment events
@EventListener
public void onPaymentSuccess(PaymentSucceededEvent event) {
    // Send email, update analytics, ...
}

// 5. Facade — checkout flow
@Service
class CheckoutFacade {
    private final PaymentProcessorFactory factory;
    
    public OrderConfirmation checkout(Order order, PaymentMethod method) {
        PaymentProcessor processor = factory.create(order.getPaymentType());
        PaymentResult result = processor.charge(order.total(), method);
        // ...
    }
}

// 6. Repository — persistence
interface OrderRepository {
    Order save(Order order);
}

20. ⚠️ Common Pitfalls

Force pattern ตอนยังไม่มี problemwait for pattern → 3rd time refactor
Singleton everywhereDI + Spring container
Inheritance for code reuseComposition + Strategy
*Manager, *Helper, *Util classService + domain methods
23 pattern memorizeรู้จัก top 10 + recognize
Pattern ที่ทำให้ code อ่านยากขึ้นwrong pattern — choose simpler

21. Checkpoint

🛠️ Checkpoint 3.1 — Spot Patterns
อ่าน source code ของ Spring framework / open source — list pattern ที่เห็น (อย่างน้อย 5)

🛠️ Checkpoint 3.2 — Refactor with Strategy

java
class Compressor {
    public byte[] compress(String type, byte[] data) {
        if (type.equals("GZIP")) { /* gzip */ }
        else if (type.equals("ZIP")) { /* zip */ }
        else if (type.equals("LZ4")) { /* lz4 */ }
    }
}

Refactor with Strategy

🛠️ Checkpoint 3.3 — Build Notification System

  • Multiple channels (email, SMS, push) — Strategy
  • Add logging + retry to each — Decorator
  • Send to multiple channels per user pref — Observer
  • One service to coordinate — Facade

🛠️ Checkpoint 3.4 — State Machine
ทำ BlogPost state: DRAFT → REVIEW → PUBLISHED → ARCHIVED

  • ใช้ enum + abstract method หรือ State pattern
  • Action: submit, approve, reject, publish, archive

22. สรุปบท

✅ Pattern = recurring solution — vocabulary สำหรับ communicate
Strategy ⭐ — ใช้บ่อยที่สุด (= OCP)
Factory + Builder — สร้าง object ที่ flexible
Decorator — add behavior โดยไม่แก้ class เดิม
Facade — interface เดียวซ่อน sub-system
Observer — event-driven, pub/sub
Repository — abstract data access (Spring Data)
State — state machine ที่ explicit
✅ Singleton = ระวัง — ใช้ DI container แทน
✅ Pattern ใน framework: Spring, React = SOLID + Patterns ฝังลึก
อย่า force pattern — wait for pattern emerge


23. Glossary — คำศัพท์ + คำอ่าน

23 GoF patterns

คำศัพท์คำอ่านความหมาย
Singletonซิง-เกิ้ล-ตันแบบเดี่ยว — instance เดียวทั้ง app
Factoryแฟค-โท-รี่โรงงาน — สร้าง object โดยไม่ระบุ class
Abstract Factoryแอ๊บ-สแตร็คท์ แฟค-โท-รี่โรงงานของโรงงาน — สร้างตระกูล object
Builderบิ้ล-เดอร์ผู้สร้างทีละขั้น
Prototypeโพร-โท-ไทป์ต้นแบบ — clone object
Adapterอะ-แด๊พ-เตอร์หัวแปลง — แปลง interface
Bridgeบริดจ์สะพาน — แยก abstraction กับ implementation
Compositeคอม-โพ-สิทโครงสร้างต้นไม้ — รวม object เป็นกลุ่ม
Decoratorเด-คอ-เร-เตอร์ตัวประดับ/ห่อ — เพิ่ม behavior
Facadeฟา-ซ้าดด้านหน้า — interface เดียวซ่อน sub-system
Flyweightฟลาย-เวทน้ำหนักเบา — แชร์ state
Proxyพร็อก-ซี่ตัวแทน
Chain of Responsibilityเชน ออฟ รี-สปอน-ซิ-บิ-ลิ-ตี้ลูกโซ่ความรับผิดชอบ
Commandคอม-แมนด์คำสั่ง — encapsulate action
Interpreterอิน-เทอร์-พรี-เตอร์ตัวตีความ
Iteratorอิ-เท-เรเตอร์ตัววนซ้ำ
Mediatorมี-เดีย-เตอร์ผู้ประสานงานกลาง
Mementoเม-เม็น-โต้บันทึก/snapshot
Observerออบ-เซิร์ฟ-เวอร์ผู้สังเกต/ติดตาม
Stateสเตทสถานะ
Strategyสแตร-ทิ-จี้กลยุทธ์ — สลับ algorithm
Template Methodเท็ม-เพลท เม-ธ็อดโครงวิธีการใน parent
Visitorวิ-ซิ-เตอร์ผู้เยี่ยม — operation ข้าม class

ศัพท์อื่นในบท

คำศัพท์คำอ่านความหมาย
Creationalครี-เอ-ชั่น-แนล(หมวด) เกี่ยวกับการสร้าง object
Structuralสตรัค-เจอ-รัล(หมวด) เกี่ยวกับโครงสร้าง
Behavioralบี-เฮฟ-วิ-เออ-รัล(หมวด) เกี่ยวกับพฤติกรรม/interaction
GoFจี-โอ-เอฟ (Gang of Four)แก๊งสี่คนผู้เขียนหนังสือ Design Patterns 1994
Pub/Subพับ-ซับPublish/Subscribe = ประกาศ/สมัครรับ
telescoping constructorเทเลส-โค-ปิ้ง คอน-สตรัค-เตอร์constructor หลายตัวที่ยาวขึ้นเรื่อย ๆ
Law of Demeterลอว์ ออฟ ดี-มี-เทอร์"อย่าคุยกับคนแปลกหน้า" — คุยกับเพื่อนใกล้ตัวเท่านั้น
Hollywood Principleฮอลลีวูด พริน-ซิ-เปิ้ล"Don't call us, we'll call you" = IoC
Inversion of Control (IoC)อิน-เวอร์-ชั่น ออฟ คอน-โทรลกลับด้านการควบคุม — framework เรียกเรา ไม่ใช่เราเรียก framework
Dependency Injection (DI)ดี-เพ็น-เด็น-ซี่ อิน-เจ๊ค-ชั่นฉีด dependency เข้าจากภายนอก แทนสร้างเอง
immutableอิม-มิว-ทะ-เบิ้ลเปลี่ยนค่าไม่ได้หลังสร้าง
weak referenceวีค เร-เฟอ-เร็นซ์reference ที่ GC เก็บได้แม้ยังถืออยู่
BigDecimalบิ๊ก-เด-ซี่-มอลtype สำหรับเลขทศนิยมที่แม่นยำ ใช้กับเงิน

← บทที่ 2 | บทที่ 4 → Clean Code