JavaRush /Java Blog /Random-TL /Mga Pattern ng Disenyo: Singleton

Mga Pattern ng Disenyo: Singleton

Nai-publish sa grupo
Kamusta! Ngayon ay titingnan natin ang iba't ibang mga pattern ng disenyo, at magsisimula tayo sa pattern ng Singleton, na tinatawag ding "singleton". Mga Pattern ng Disenyo: Singleton - 1Tandaan natin: ano ang alam natin tungkol sa mga pattern ng disenyo sa pangkalahatan? Ang mga pattern ng disenyo ay mga pinakamahusay na kasanayan na maaaring sundin upang malutas ang ilang kilalang problema. Ang mga pattern ng disenyo ay karaniwang hindi nakatali sa anumang programming language. Kunin ang mga ito bilang isang hanay ng mga rekomendasyon, kasunod nito ay maiiwasan mo ang mga pagkakamali at hindi na muling likhain ang iyong gulong.

Ano ang singleton?

Ang singleton ay isa sa mga pinakasimpleng pattern ng disenyo na maaaring ilapat sa isang klase. Minsan sinasabi ng mga tao na "isang singleton ang klase na ito," ibig sabihin, ipinapatupad ng klase na ito ang pattern ng disenyo ng singleton. Minsan kinakailangan na magsulat ng isang klase kung saan isang bagay lamang ang maaaring malikha. Halimbawa, isang klase na responsable para sa pag-log o pagkonekta sa isang database. Ang pattern ng disenyo ng Singleton ay naglalarawan kung paano natin magagawa ang ganoong gawain. Ang singleton ay isang pattern ng disenyo na gumagawa ng dalawang bagay:
  1. Nagbibigay ng garantiya na ang isang klase ay magkakaroon lamang ng isang instance ng klase.

  2. Nagbibigay ng pandaigdigang access point sa isang instance ng klase na ito.

Samakatuwid, mayroong dalawang tampok na katangian ng halos bawat pagpapatupad ng singleton pattern:
  1. Pribadong tagabuo. Nililimitahan ang kakayahang lumikha ng mga bagay ng klase sa labas ng klase mismo.

  2. Isang pampublikong static na pamamaraan na nagbabalik ng isang instance ng klase. Ang pamamaraang ito ay tinatawag na getInstance. Ito ang pandaigdigang access point sa instance ng klase.

Mga opsyon sa pagpapatupad

Ang pattern ng disenyo ng singleton ay ginagamit sa iba't ibang paraan. Ang bawat pagpipilian ay mabuti at masama sa sarili nitong paraan. Dito, gaya ng dati: walang perpekto, ngunit kailangan mong magsikap para dito. Ngunit una sa lahat, tukuyin natin kung ano ang mabuti at kung ano ang masama, at kung anong mga sukatan ang nakakaimpluwensya sa pagsusuri ng pagpapatupad ng isang pattern ng disenyo. Magsimula tayo sa positibo. Narito ang mga pamantayan na nagbibigay sa pagpapatupad ng juiciness at pagiging kaakit-akit:
  • Lazy initialization: kapag ang isang klase ay na-load habang ang application ay tumatakbo nang eksakto kapag ito ay kinakailangan.

  • Ang pagiging simple at transparency ng code: ang sukatan, siyempre, ay subjective, ngunit mahalaga.

  • Kaligtasan ng thread: gumagana nang tama sa isang multi-threaded na kapaligiran.

  • Mataas na pagganap sa isang multi-threaded na kapaligiran: ang mga thread ay humaharang sa isa't isa nang kaunti o hindi talaga kapag nagbabahagi ng isang mapagkukunan.

Ngayon ang cons. Inilista namin ang mga pamantayan na nagpapakita ng pagpapatupad sa isang masamang ilaw:
  • Non-lazy initialization: kapag ang isang klase ay na-load kapag nagsimula ang application, hindi alintana kung ito ay kinakailangan o hindi (isang kabalintunaan, sa mundo ng IT ay mas mahusay na maging tamad)

  • Pagiging kumplikado at mahinang pagiging madaling mabasa ng code. Ang sukatan ay subjective din. Ipagpalagay natin na kung dugo ang manggagaling sa mga mata, ang pagpapatupad ay so-so.

  • Kakulangan ng kaligtasan ng thread. Sa madaling salita, "thread hazard". Maling operasyon sa isang multi-threaded na kapaligiran.

  • Hindi magandang pagganap sa isang multi-threaded na kapaligiran: ang mga thread ay humaharang sa bawat isa sa lahat ng oras o madalas kapag nagbabahagi ng mapagkukunan.

Code

Ngayon ay handa na kaming isaalang-alang ang iba't ibang mga opsyon sa pagpapatupad, na naglilista ng mga kalamangan at kahinaan:

Simpleng Solusyon

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

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
Ang pinakasimpleng pagpapatupad. Mga kalamangan:
  • Ang pagiging simple at transparency ng code

  • Kaligtasan ng thread

  • Mataas na pagganap sa isang multi-threaded na kapaligiran

Minuse:
  • Hindi tamad na pagsisimula.
Sa pagtatangkang itama ang huling kapintasan, nakukuha namin ang pangalawang numero ng pagpapatupad:

Tamad na Initialization

public class Singleton {
  private static Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Mga kalamangan:
  • Tamad na pagsisimula.

Minuse:
  • Hindi ligtas sa thread

Ang pagpapatupad ay kawili-wili. Maaari naming simulan ang tamad, ngunit nawala namin ang kaligtasan ng thread. Walang problema: sa implementasyon ng numero tatlo, sini-synchronize namin ang lahat.

Naka-synchronize na Accessor

public class Singleton {
  private static Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread

Minuse:
  • Mahina ang pagganap sa isang multi-threaded na kapaligiran

Malaki! Sa ikatlo na pagpapatupad, ibinalik namin ang kaligtasan ng thread! Totoo, ito ay mabagal... Ngayon ang pamamaraan getInstanceay naka-synchronize, at maaari mo lamang itong ipasok nang paisa-isa. Sa katunayan, hindi namin kailangang i-synchronize ang buong pamamaraan, ngunit ang bahagi lamang nito kung saan nagsisimula kami ng isang bagong object ng klase. Ngunit hindi natin basta-basta maaaring ibalot synchronizedang bahaging responsable sa paglikha ng bagong bagay sa isang bloke: hindi ito magbibigay ng kaligtasan sa thread. Ito ay medyo mas kumplikado. Ang tamang paraan ng pag-synchronize ay ibinigay sa ibaba:

Dobleng Sinuri ang Pag-lock

public class Singleton {
    private static Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread

  • Mataas na pagganap sa isang multi-threaded na kapaligiran

Minuse:
  • Hindi suportado sa mga bersyon ng Java na mas mababa sa 1.5 (naayos ang pabagu-bagong keyword sa bersyon 1.5)

Tandaan ko na para gumana nang tama ang opsyon sa pagpapatupad na ito, kinakailangan ang isa sa dalawang kundisyon. Ang variable INSTANCEay dapat na alinman sa final, o volatile. Ang huling pagpapatupad na tatalakayin natin ngayon ay Class Holder Singleton.

May-hawak ng Klase na si 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;
   }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread.

  • Mataas na pagganap sa isang multi-threaded na kapaligiran.

Minuse:
  • Para sa tamang operasyon, kinakailangan upang matiyak na ang object ng klase Singletonay sinisimulan nang walang mga error. Kung hindi, ang unang paraan ng tawag getInstanceay magtatapos sa isang error ExceptionInInitializerError, at lahat ng kasunod ay mabibigo NoClassDefFoundError.

Ang pagpapatupad ay halos perpekto. At tamad, at thread-safe, at mabilis. Ngunit mayroong isang nuance na inilarawan sa minus. Talahanayan ng paghahambing ng iba't ibang pagpapatupad ng pattern ng Singleton:
Pagpapatupad Tamad na pagsisimula Kaligtasan ng thread Bilis ng multithreading Kailan gagamitin?
Simpleng Solusyon - + Mabilis Hindi kailanman. O kapag ang tamad na pagsisimula ay hindi mahalaga. Ngunit hindi kailanman mas mahusay.
Tamad na Initialization + - Hindi maaari Laging kapag hindi kailangan ang multithreading
Naka-synchronize na Accessor + + Dahan-dahan Hindi kailanman. O kapag ang bilis ng trabaho sa multithreading ay hindi mahalaga. Ngunit hindi kailanman mas mahusay
Dobleng Sinuri ang Pag-lock + + Mabilis Sa mga bihirang kaso kapag kailangan mong pangasiwaan ang mga pagbubukod kapag gumagawa ng singleton. (kapag hindi naaangkop ang Class Holder Singleton)
May-hawak ng Klase na si Singleton + + Mabilis Laging kapag kailangan ang multithreading at may garantiya na malilikha ang isang object ng singleton class nang walang problema.

Mga kalamangan at kahinaan ng pattern ng Singleton

Sa pangkalahatan, ginagawa ng singleton ang eksaktong inaasahan dito:
  1. Nagbibigay ng garantiya na ang isang klase ay magkakaroon lamang ng isang instance ng klase.

  2. Nagbibigay ng pandaigdigang access point sa isang instance ng klase na ito.

Gayunpaman, ang pattern na ito ay may mga kawalan:
  1. Ang Singleton ay lumalabag sa SRP (Single Responsibility Principle) - ang klase ng Singleton, bilang karagdagan sa mga agarang responsibilidad nito, ay kumokontrol din sa bilang ng mga kopya nito.

  2. Ang dependency ng isang regular na klase o pamamaraan sa isang singleton ay hindi nakikita sa pampublikong kontrata ng klase.

  3. Ang mga global variable ay masama. Ang singleton sa kalaunan ay nagiging isang mabigat na global variable.

  4. Ang pagkakaroon ng singleton ay binabawasan ang testability ng application sa pangkalahatan at ang mga klase na gumagamit ng singleton sa partikular.

OK tapos na ang lahat Ngayon. Tiningnan namin ang pattern ng disenyo ng singleton. Ngayon, sa isang pag-uusap para sa buhay kasama ang iyong mga kaibigan sa programmer, masasabi mo hindi lamang kung ano ang mabuti tungkol dito, kundi pati na rin ang ilang mga salita tungkol sa kung ano ang masama tungkol dito. Good luck sa pag-master ng bagong kaalaman.

Karagdagang pagbabasa:

Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION