โค้ดยิม/จาวาบล็อก/สุ่ม/จาวาซิงเกิลตันคลาส
John Squirrels
ระดับ
San Francisco

จาวาซิงเกิลตันคลาส

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะลงลึกในรายละเอียดของรูปแบบการออกแบบต่างๆ โดยเริ่มจากรูปแบบ Java Singleton มาทบทวนกัน: เรารู้อะไรบ้างเกี่ยวกับรูปแบบการออกแบบโดยทั่วไป รูปแบบการออกแบบเป็นแนวทางปฏิบัติที่ดีที่สุดที่เราสามารถนำไปใช้เพื่อแก้ปัญหาที่ทราบได้หลายประการ โดยทั่วไปแล้วรูปแบบการออกแบบจะไม่เชื่อมโยงกับภาษาโปรแกรมใดๆ ให้คิดว่าเป็นชุดคำแนะนำเพื่อช่วยคุณหลีกเลี่ยงข้อผิดพลาดและหลีกเลี่ยงการคิดค้นวงล้อใหม่รูปแบบการออกแบบ: Singleton - 1

singleton ใน Java คืออะไร?

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

  2. ให้จุดเดียวของการเข้าถึงทั่วโลกไปยังอินสแตนซ์นั้น

ดังนั้นจึงมีคุณสมบัติสองประการที่เป็นลักษณะเฉพาะของการนำรูปแบบซิงเกิลตันไปใช้ในเกือบทุกรูปแบบ:
  1. ตัวสร้างส่วนตัว สิ่งนี้จำกัดความสามารถในการสร้างวัตถุของคลาสนอกคลาส

  2. เมธอดสแตติกสาธารณะที่ส่งคืนอินสแตนซ์ของคลาส วิธีนี้เรียกว่าgetInstance นี่คือจุดของการเข้าถึงอินสแตนซ์ของคลาสทั่วโลก

ตัวเลือกการใช้งาน

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

  • รหัสที่เรียบง่ายและโปร่งใส: แน่นอนว่าเมตริกนี้เป็นอัตนัย แต่ก็มีความสำคัญ

  • ความปลอดภัยของเธรด: การทำงานที่ถูกต้องในสภาพแวดล้อมแบบมัลติเธรด

  • ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด: การบล็อกเธรดเพียงเล็กน้อยหรือไม่มีเลยเมื่อแบ่งปันทรัพยากร

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

  • รหัสที่ซับซ้อนและอ่านยาก เมตริกนี้เป็นอัตนัยเช่นกัน หากดวงตาของคุณเริ่มมีเลือดออก เราจะถือว่าการดำเนินการนั้นไม่ดีที่สุด

  • ขาดความปลอดภัยของเธรด กล่าวอีกนัยหนึ่งคือ "อันตรายจากด้าย" การดำเนินการที่ไม่ถูกต้องในสภาพแวดล้อมแบบมัลติเธรด

  • ประสิทธิภาพต่ำในสภาพแวดล้อมแบบมัลติเธรด: เธรดบล็อกกันตลอดเวลาหรือบ่อยครั้งเมื่อแบ่งปันทรัพยากร

รหัส

ตอนนี้เราพร้อมที่จะพิจารณาตัวเลือกการใช้งานต่างๆ และระบุข้อดีและข้อเสีย:

เรียบง่าย

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
การใช้งานที่ง่ายที่สุด ข้อดี:
  • รหัสที่เรียบง่ายและโปร่งใส

  • ความปลอดภัยของเธรด

  • ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด

จุดด้อย:
  • ไม่มีการเริ่มต้นขี้เกียจ
ในความพยายามที่จะแก้ไขข้อบกพร่องก่อนหน้านี้ เราได้รับการดำเนินการหมายเลขสอง:

การเริ่มต้นขี้เกียจ

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
ข้อดี:
  • การเริ่มต้นขี้เกียจ

จุดด้อย:
  • ไม่ด้ายปลอดภัย

การใช้งานนี้น่าสนใจ เราสามารถเริ่มต้นอย่างเกียจคร้าน แต่เราได้สูญเสียความปลอดภัยของเธรด ไม่ต้องกังวล — เราซิงโครไนซ์ทุกอย่างในการนำไปใช้งานหมายเลขสาม

การเข้าถึงแบบซิงโครไนซ์

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
ข้อดี:
  • การเริ่มต้นขี้เกียจ

  • ความปลอดภัยของเธรด

จุดด้อย:
  • ประสิทธิภาพการทำงานแบบมัลติเธรดต่ำ

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

ตรวจสอบการล็อคสองครั้ง

public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
ข้อดี:
  • การเริ่มต้นขี้เกียจ

  • ความปลอดภัยของเธรด

  • ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด

จุดด้อย:
  • ไม่รองรับใน Java เวอร์ชันก่อนหน้าที่ต่ำกว่า 1.5 (การใช้คีย์เวิร์ดระเหยได้รับการแก้ไขตั้งแต่เวอร์ชัน 1.5)

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

เจ้าของชั้นเรียน

public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
ข้อดี:
  • การเริ่มต้นขี้เกียจ

  • ความปลอดภัยของเธรด

  • ประสิทธิภาพสูงในสภาพแวดล้อมแบบมัลติเธรด

จุดด้อย:
  • การดำเนินการที่ถูกต้องต้องการการรับประกันว่า ออบเจกต์ ซิงเกิลตันเริ่มต้นได้โดยไม่มีข้อผิดพลาด มิฉะนั้น การเรียกเมธอด getInstance ครั้งแรกจะส่งผลให้เกิดExceptionInInitializerErrorและการเรียกที่ตามมาทั้งหมดจะทำให้เกิดNoClassDefFoundError

การใช้งานนี้เกือบจะสมบูรณ์แบบ มันขี้เกียจและเธรดปลอดภัยและรวดเร็ว แต่มีความแตกต่างกันเล็กน้อยตามที่อธิบายไว้ในรายการข้อเสีย การเปรียบเทียบการใช้งานรูปแบบซิงเกิลตันแบบต่างๆ:
การดำเนินการ การเริ่มต้นขี้เกียจ ความปลอดภัยของเธรด ประสิทธิภาพการทำงานแบบมัลติเธรด ใช้เมื่อไหร่?
เรียบง่าย - + เร็ว ไม่เคย. หรือเป็นไปได้เมื่อการเริ่มต้นแบบขี้เกียจนั้นไม่สำคัญ แต่ไม่เคยจะดีกว่า
การเริ่มต้นขี้เกียจ + - ไม่สามารถใช้ได้ เสมอเมื่อไม่ต้องการมัลติเธรด
การเข้าถึงแบบซิงโครไนซ์ + + ช้า ไม่เคย. หรือเป็นไปได้เมื่อประสิทธิภาพของมัลติเธรดไม่สำคัญ แต่ไม่เคยจะดีกว่า
ตรวจสอบการล็อคสองครั้ง + + เร็ว ในบางกรณีที่เกิดขึ้นไม่บ่อยเมื่อคุณต้องการจัดการกับข้อยกเว้นเมื่อสร้างซิงเกิลตัน (เมื่อไม่สามารถใช้ซิงเกิลตันตัวยึดคลาสได้)
เจ้าของชั้นเรียน + + เร็ว เมื่อใดก็ตามที่จำเป็นต้องใช้มัลติเธรดและมีการรับประกันว่าอ็อบเจกต์ singleton จะถูกสร้างขึ้นโดยไม่มีปัญหา

ข้อดีและข้อเสียของรูปแบบซิงเกิลตัน

โดยทั่วไป singleton ทำในสิ่งที่คาดหวัง:
  1. รับประกันได้ว่าจะมีเพียงหนึ่งตัวอย่างในชั้นเรียนเท่านั้น

  2. ให้จุดเดียวของการเข้าถึงทั่วโลกไปยังอินสแตนซ์นั้น

อย่างไรก็ตาม รูปแบบนี้มีข้อบกพร่อง:
  1. ซิงเกิลตันละเมิดหลักการความรับผิดชอบเดียว: นอกเหนือจากหน้าที่โดยตรงแล้ว คลาสซิงเกิลตันยังควบคุมจำนวนอินสแตนซ์ด้วย

  2. การพึ่งพาของชนชั้นสามัญในซิงเกิลไม่สามารถมองเห็นได้ในสัญญาสาธารณะของชั้นเรียน

  3. ตัวแปรส่วนกลางไม่ดี ในที่สุด singleton กลายเป็นตัวแปรระดับโลกที่หนักหน่วง

  4. การมีซิงเกิลตันช่วยลดความสามารถในการทดสอบของแอปพลิเคชันโดยรวมและคลาสที่ใช้ซิงเกิลตันโดยเฉพาะ

และนั่นแหล่ะ! :) เราได้สำรวจ Java Singleton Class กับคุณแล้ว ตลอดชีวิตที่เหลือของคุณ เมื่อพูดคุยกับเพื่อนที่เป็นโปรแกรมเมอร์ คุณสามารถพูดถึงไม่เพียงแต่ว่ารูปแบบนั้นดีเพียงใด แต่ยังสามารถพูดถึงสิ่งที่ทำให้มันแย่ได้อีกด้วย ขอให้โชคดีในการเรียนรู้ความรู้ใหม่นี้

อ่านเพิ่มเติม:

ความคิดเห็น
  • เป็นที่นิยม
  • ใหม่
  • เก่า
คุณต้องลงชื่อเข้าใช้เพื่อแสดงความคิดเห็น
หน้านี้ยังไม่มีความคิดเห็นใด ๆ