นากต้นฉบับตรวจกล่องแรกที่ว่างก่อนดึงคันเปิดตู้สำรองซึ่งมีของอยู่

Java Optional.or: ต่อ Fallback แบบ Lazy โดยไม่แกะ Optional กลางทาง

พี่โชว์Engineering

fallback chain เช่น cache → database → default มักถูกเขียนด้วย isPresent หลายชั้น หรือใช้ orElseGet เร็วเกินไปจนค่ากลางหลุดจาก Optional Optional.or รับ supplier ที่คืน Optional อีกก้อน จึงต่อแหล่งข้อมูลหลายชั้นได้โดยยัง lazy และยังไม่บังคับเลือกค่าปลายทางครับ

import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    static Optional<String> resolve(
            Optional<String> cache,
            AtomicInteger databaseCalls) {
        return cache.or(() -> {
            databaseCalls.incrementAndGet();
            return Optional.of("from-database");
        });
    }

    public static void main(String[] args) {
        AtomicInteger calls = new AtomicInteger();
        String cached = resolve(Optional.of("from-cache"), calls).orElseThrow();
        assert cached.equals("from-cache");
        assert calls.get() == 0 : "fallback must stay lazy";

        String loaded = resolve(Optional.empty(), calls).orElseThrow();
        assert loaded.equals("from-database");
        assert calls.get() == 1;
        System.out.println("fallback chain verified");
    }
}

ความต่างเล็กที่มีผลใหญ่

orElse(value) ประเมิน value ก่อนเรียก จึงไม่เหมาะกับงานแพง orElseGet(supplier) lazy แต่จบ chain ด้วยค่า T ขณะที่ or(supplier) lazy และยังคืน Optional<T> เหมาะเมื่อ fallback เองอาจไม่มีข้อมูล หรือเราต้องการต่อแหล่งที่สาม

supplier ห้ามคืน null เพราะผิดสัญญา ถ้าแหล่งข้อมูลล้มเหลวอย่ากลืน exception แล้วแปลงเป็น empty โดยอัตโนมัติ—“ไม่มีข้อมูล” กับ “ฐานข้อมูลล่ม” เป็นคนละสถานะ อาจใช้ result type ของโดเมนหรือปล่อย exception ที่แปลความหมายได้

chain ที่อ่านสวยอาจซ่อน I/O หลายรอบ ควรใส่ metric แยก hit/miss/failed และกำหนด timeout ของแต่ละแหล่ง หาก cache miss หลายคำขอพร้อมกัน fallback อาจยิงฐานข้อมูลซ้ำ Optional.or ไม่ได้ทำ request coalescing ให้เรา

Recap

  • or รักษา chain เป็น Optional และเรียก fallback แบบ lazy
  • แยก empty ออกจาก failure ของแหล่งข้อมูล
  • วัด hit/miss และจำกัด I/O ที่ supplier
  • ใช้ orElseGet เมื่อปลายทางต้องการค่า ไม่ใช่ Optional ต่อครับ