0Pricing
Java Academy · บทเรียน

การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม

ตัดสินใจเลือกรูปแบบคลาสซ้อนโดยพิจารณาจากการเชื่อมโยง ความต้องการด้านการเข้าถึง และความอ่านง่าย

การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

การเลือกกลยุทธ์การซ้อนที่เหมาะสม

Java มีทางเลือกในการซ้อนสี่แบบ ได้แก่ ระดับบนสุด คลาสซ้อนแบบสแตติก คลาสภายใน (ไม่ใช่สแตติก) และคลาสภายในเมธอดหรือนิรนาม การเลือกแบบที่เหมาะสมขึ้นอยู่กับระดับการเชื่อมโยง ความต้องการเข้าถึง และขอบเขตการนำกลับมาใช้

แผนผังการตัดสินใจ

ถามคำถามต่อไปนี้เพื่อเลือก:

  1. คลาสนี้ใช้เฉพาะภายในคลาสภายนอกหรือไม่ → พิจารณาการซ้อนคลาส
  2. คลาสนี้ต้องใช้อินสแตนซ์ภายนอกหรือไม่ → ใช้คลาสภายใน มิฉะนั้น → ใช้คลาสซ้อนแบบสแตติก
  3. คลาสนี้ใช้เฉพาะในเมธอดเดียวหรือไม่ → ใช้คลาสภายในเมธอดหรือคลาสนิรนาม
  4. คลาสนี้นำเมธอดเดียวไปใช้หรือไม่ → ใช้แลมบ์ดา

คลาสระดับบนสุด

ใช้คลาสระดับบนสุดเมื่อประเภทนั้นถูกใช้งานอย่างกว้างขวางในฐานโค้ด แสดงแนวคิดสำคัญของโดเมน หรือจะถูกนำเข้าโดยคลาสอื่นจำนวนมาก

// Top-level: used across many packages
public class Product {
    // ...
}

public interface Repository<T, ID> {
    Optional<T> findById(ID id);
    void save(T entity);
}

// These belong in separate .java files and are widely imported

คลาสซ้อนแบบสแตติก

ใช้คลาสซ้อนแบบสแตติกเมื่อคลาสนั้นมีความหมายเฉพาะในบริบทของคลาสภายนอก ไม่จำเป็นต้องใช้อินสแตนซ์ภายนอก และควรเปิดให้เข้าถึงจากภายนอกได้

class Graph {
    // Edge only makes sense in the context of a Graph
    public static class Edge {
        public final int from, to, weight;
        public Edge(int from, int to, int weight) {
            this.from = from; this.to = to; this.weight = weight;
        }
    }

    private List<Edge> edges = new ArrayList<>();

    public void addEdge(int from, int to, int weight) {
        edges.add(new Edge(from, to, weight));
    }
}

คลาสภายใน (ไม่ใช่สแตติก)

ใช้คลาสภายในเมื่อคลาสนั้นมีความเกี่ยวข้องเชิงแนวคิดอย่างมากกับอินสแตนซ์ภายนอก และต้องเข้าถึงสมาชิกส่วนตัวของคลาสนั้น เช่น ตัววนซ้ำหรืออ็อบเจ็กต์ช่วยเหลือที่เชื่อมโยงกันอย่างใกล้ชิด

class DataStore<T> {
    private final List<T> data;

    DataStore(List<T> data) { this.data = data; }

    // Must access data — inner class makes sense
    class Snapshot {
        private final List<T> copy;

        Snapshot() { this.copy = List.copyOf(data); } // accesses outer

        T get(int idx) { return copy.get(idx); }
        int size()     { return copy.size(); }
    }

    Snapshot snapshot() { return new Snapshot(); }
}

คลาสภายในเมธอด

ใช้คลาสภายในเมธอดเมื่อคลาสนั้นจำเป็นเฉพาะในเมธอดเดียว ซับซ้อนพอที่จะมีหลายเมธอดหรือมีสถานะ และต้องการจำกัดขอบเขตให้แคบที่สุด

static int countWords(String text, boolean caseSensitive) {
    class WordCounter {
        Map<String, Integer> counts = new HashMap<>();

        void count(String word) {
            String key = caseSensitive ? word : word.toLowerCase();
            counts.merge(key, 1, Integer::sum);
        }

        int uniqueCount() { return counts.size(); }
    }

    WordCounter wc = new WordCounter();
    for (String w : text.split("\\s+")) wc.count(w);
    return wc.uniqueCount();
}
System.out.println(countWords("the cat sat on the mat", false)); // 5

คลาสนิรนามกับแลมบ์ดา

ใช้คลาสนิรนามเมื่อต้องการหลายเมธอดหรือการสืบทอดจากคลาสนามธรรม ใช้แลมบ์ดากับอินเทอร์เฟซแบบ SAM

// Lambda: clean for SAM
list.sort((a, b) -> a.compareTo(b));

// Anonymous: multi-method, or extending abstract class
java.util.TimerTask task = new java.util.TimerTask() {
    @Override public void run() { System.out.println("Tick!"); }
};
// TimerTask is abstract class — can't use lambda directly

ตารางสรุป

ข้อมูลอ้างอิงอย่างรวดเร็ว:

  • ระดับบนสุด: นำกลับมาใช้ได้อย่างกว้างขวาง เป็นประเภทของส่วนติดต่อโปรแกรมสาธารณะ
  • ซ้อนแบบสแตติก: จัดกลุ่มตามตรรกะ และไม่จำเป็นต้องใช้อินสแตนซ์ภายนอก
  • ภายใน: เชื่อมโยงอย่างมากกับอินสแตนซ์ภายนอก และต้องเข้าถึงสมาชิกส่วนตัว
  • ภายในเมธอด: จำกัดขอบเขตอยู่ในเมธอด และซับซ้อนพอสำหรับการเป็นคลาส
  • นิรนาม: การนำไปใช้ครั้งเดียวและมีอายุสั้น
  • แลมบ์ดา: ใช้กับอินเทอร์เฟซแบบ SAM ไม่มีสถานะ และกระชับ

เชิงปฏิบัติ: การออกแบบ HttpClient

การประยุกต์ใช้กลยุทธ์การซ้อนกับการออกแบบ HttpClient ที่สมจริง

class HttpClient {
    // Static nested: request config has no outer reference
    public static class RequestConfig {
        public final int timeoutMs;
        public final String userAgent;
        RequestConfig(int timeout, String ua) { this.timeoutMs = timeout; this.userAgent = ua; }
    }

    // Static nested: response is standalone data carrier
    public static class Response {
        public final int statusCode;
        public final String body;
        Response(int code, String body) { this.statusCode = code; this.body = body; }
    }

    private final RequestConfig config;
    HttpClient(RequestConfig config) { this.config = config; }

    public Response get(String url) {
        // Implementation using this.config
        return new Response(200, "body");
    }
}

หลีกเลี่ยงการซ้อนหลายชั้นเกินไป

หลีกเลี่ยงการซ้อนที่ลึกเกินหนึ่งระดับ คลาสที่ซ้อนกันลึกจะนำทางและทดสอบได้ยาก หากซับซ้อนขึ้น ควรแยกเป็นคลาสระดับบนสุดหรือคลาสที่จำกัดการเข้าถึงไว้ภายในแพ็กเกจ

ทางเลือกแบบจำกัดการเข้าถึงภายในแพ็กเกจ

แทนที่จะซ้อนคลาส ลองกำหนดให้คลาสจำกัดการเข้าถึงอยู่ภายในแพ็กเกจ โดยไม่ใช้ตัวปรับแต่งการเข้าถึง คลาสนี้จะมองเห็นได้เฉพาะภายในแพ็กเกจ แต่ทดสอบและดูแลรักษาได้ง่ายกว่าคลาสซ้อน

// In package com.example.order:

// Package-private: visible in package, no nesting
class OrderLine {  // no 'public' modifier
    final String sku;
    final int qty;
    OrderLine(String sku, int qty) { this.sku = sku; this.qty = qty; }
}

// Public class in same package can use OrderLine freely
public class Order {
    private List<OrderLine> lines = new ArrayList<>();
    // ...
}

เรคคอร์ดและการปิดผนึกเป็นทางเลือก

คุณสมบัติสมัยใหม่ของ Java ได้แก่ เรคคอร์ดและอินเทอร์เฟซแบบปิดผนึก มักช่วยลดความจำเป็นในการใช้รูปแบบการซ้อนที่ซับซ้อนได้

// Instead of nested inner class hierarchy:
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double r) implements Shape {}
record Rectangle(double w, double h) implements Shape {}
record Triangle(double b, double h) implements Shape {}

static double area(Shape s) {
    return switch (s) {
        case Circle c      -> Math.PI * c.r() * c.r();
        case Rectangle r   -> r.w() * r.h();
        case Triangle t    -> 0.5 * t.b() * t.h();
    };
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

เมื่อใดที่คลาสภายใน (ไม่ใช่สแตติก) เป็นตัวเลือกที่ดีกว่าคลาสซ้อนแบบสแตติก

ทบทวน: การเลือกกลยุทธ์การซ้อนที่เหมาะสม

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

  • ระดับบนสุด: ประเภทสาธารณะที่ใช้อย่างกว้างขวางทั่วทั้งฐานโค้ด
  • ซ้อนแบบสแตติก: จัดกลุ่มตามตรรกะกับคลาสภายนอก และไม่จำเป็นต้องใช้อินสแตนซ์ภายนอก
  • คลาสภายใน: เชื่อมโยงอย่างมากกับอินสแตนซ์ภายนอก และเข้าถึงสมาชิกส่วนตัว
  • คลาสภายในเมธอด: จำกัดขอบเขตอยู่ในเมธอด และซับซ้อนพอสำหรับหลายเมธอด
  • คลาสนิรนาม: ใช้ครั้งเดียว รวมการประกาศและการสร้างอินสแตนซ์ไว้ด้วยกัน
  • แลมบ์ดา: สะอาดที่สุดสำหรับอินเทอร์เฟซแบบ SAM ควรพิจารณาเมื่อต้องการแทนที่คลาสนิรนาม

คำถามที่พบบ่อย

บทเรียน “การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม”

ตัดสินใจเลือกรูปแบบคลาสซ้อนโดยพิจารณาจากการเชื่อมโยง ความต้องการด้านการเข้าถึง และความอ่านง่าย คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. คลาสซ้อนแบบ Static
  2. คลาสภายในและการเข้าถึงคลาสภายนอก
  3. คลาสเฉพาะที่และคลาสนิรนาม
  4. การเลือกกลยุทธ์การซ้อนคลาสที่เหมาะสม
← กลับไปที่ Java Academy