it-swarm-eu.dev

Takže Singletons jsou špatní, tak co?

V poslední době se hodně diskutovalo o problémech s používáním (a nadužíváním) Singletonů. Byl jsem jedním z těch lidí už v mé kariéře. Vidím, v čem je problém nyní, a přesto stále existuje mnoho případů, kdy nevidím pěknou alternativu - a mnoho z anti-singletonských diskusí to opravdu nenabízí.

Zde je skutečný příklad z nedávného velkého projektu, kterého jsem se účastnil:

Tato aplikace byla silným klientem s mnoha samostatnými obrazovkami a komponenty, které používají obrovské množství dat ze stavu serveru, který není příliš často aktualizován. Tato data byla v zásadě uložena do mezipaměti v objektu Singleton „manager“ - obávaný „globální stav“. Myšlenkou bylo mít toto jedno místo v aplikaci, která udržuje uložená a synchronizovaná data, a pak se všechny nové obrazovky, které jsou otevřeny, mohou dotazovat jen na to, co od nich potřebují, aniž by ze serveru opakovaně požadovaly různá podpůrná data. Neustálé vyžádání serveru by vyžadovalo příliš velkou šířku pásma - a já mluvím o tisíce dolarů navíc internetové účty týdně, takže to bylo nepřijatelné.

Existuje nějaký jiný přístup, který by zde mohl být vhodný, než v podstatě mít tento druh objektu mezipaměti globálního správce dat? Tento objekt nemusí být oficiálně „Singleton“, ale z koncepčního hlediska má smysl být jedním. Co je tady pěkná čistá alternativa?

570
Bobby Tables

Je důležité rozlišovat mezi jednotlivé instance a Singletonův návrhový vzor .

Jednotlivé případy jsou prostě realitou. Většina aplikací je navržena tak, aby fungovala současně s jednou konfigurací, jedním uživatelským rozhraním najednou, jedním souborovým systémem najednou a tak dále. Pokud je třeba zachovat mnoho stavu nebo dat, určitě byste chtěli mít jen jednu instanci a udržovat je naživu tak dlouho, jak je to možné.

Singleton návrhový vzorec je velmi specifický typ jedné instance, konkrétně ten, který je:

  • Přístupné prostřednictvím globálního statického pole instance;
  • Vytvořeno při inicializaci programu nebo při prvním přístupu;
  • Žádný veřejný konstruktér (nemůže přímo vytvořit instanci);
  • Nikdy explicitně uvolněno (implicitně uvolněno při ukončení programu).

Díky této konkrétní konstrukční volbě způsob představuje několik potenciálních dlouhodobých problémů:

  • Neschopnost používat abstraktní nebo třídy rozhraní;
  • Neschopnost podtřídy;
  • Vysoká vazba napříč aplikací (obtížné úpravy);
  • Obtížné vyzkoušet (nelze testovat falešně/zesměšňovat v jednotkových testech);
  • Obtížné paralelizovat v případě proměnlivého stavu (vyžaduje rozsáhlé uzamčení);
  • a tak dále.

Žádný z těchto příznaků není ve skutečnosti endemický pro jednotlivé případy, pouze pro Singletonův vzorec.

Co místo toho můžete udělat? Jednoduše nepoužívejte vzor Singleton.

Citace z otázky:

Myšlenkou bylo mít toto jedno místo v aplikaci, která udržuje uložená a synchronizovaná data, a pak se všechny nové obrazovky, které jsou otevřeny, mohou dotazovat jen na to, co od nich potřebují, aniž by ze serveru opakovaně požadovaly různá podpůrná data. Neustálé vyžádání serveru by vyžadovalo příliš velkou šířku pásma - a já mluvím o tisíce dolarů navíc internetové účty týdně, takže to bylo nepřijatelné.

Tento koncept má jméno, jak jste trochu naznačil, ale zní nejistě. Říká se tomu cache . Pokud chcete získat fantazii, můžete ji nazvat „offline cache“ nebo jen offline kopií vzdálených dat.

Mezipaměť nemusí být singleton. Pokud chcete zabránit načtení stejných dat pro více instancí mezipaměti, musí být může jedna instance; ale to neznamená, že ve skutečnosti musíte vystavit vše všem.

První věc, kterou bych udělal, je oddělit různé funkční oblasti mezipaměti do samostatných rozhraní. Řekněme například, že jste na Microsoft Access dělali nejhorší klon YouTube na světě:

 MSAccessCache 
 ▲ 
 | 
 + ----------------- + -------- --------- + 
 | | | 
 IMediaCache IProfileCache IPageCache 
 | | 
 | | | 
 VideoPage MyAccountPage MostPopularPage 

Zde máte několik rozhraní popisujících konkrétní typy dat, které může určitá třída potřebovat přístup - média, uživatelské profily a statické stránky (jako přední strana). To vše je implementováno o jednu mega-cache, ale místo toho navrhujete své individuální třídy, aby akceptovaly rozhraní, takže se nestarají o jaký druh instance mají. Fyzickou instanci inicializujete jednou, když se spustí váš program, a poté začnete procházet instance (obsazení do určitého typu rozhraní) prostřednictvím konstruktorů a veřejných vlastností.

Toto se mimochodem nazývá Dependency Injection ; nemusíte používat jaro nebo speciální kontejner IoC, jen pokud váš obecný návrh třídy přijímá jeho závislosti od volajícího místo jejich okamžité uvedení nebo odkazující globální stav.

Proč byste měli používat design založený na rozhraní? Tři důvody:

  1. Usnadňuje čtení kódu; můžete jasně pochopit z rozhraní přesně na jakých datech závislých třídách závisí.

  2. Pokud a když si uvědomíte, že Microsoft Access nebyl nejlepší volbou pro datové back-end, můžete jej nahradit něčím lepším - řekněme SQL Server.

  3. Pokud a když si uvědomíte, že SQL Server není nejlepší volbou pro média konkrétně, můžete přerušit implementaci aniž by to ovlivnilo jakoukoli jinou část systém. Tam přichází skutečná síla abstrakce.

Pokud to chcete udělat ještě o krok dále, můžete použít kontejner IoC (DI framework), například Spring (Java) nebo Unity (.NET). Téměř každý rámec DI bude provádět svou vlastní správu životnosti a konkrétně vám umožní definovat konkrétní službu jako jednu instanci (často ji nazýváme „singleton“, ale to je jen pro povědomí). Tyto rámce v podstatě šetří většinu opičích prací při ručním procházení instancí, ale nejsou nezbytně nutné. Pro implementaci tohoto návrhu nepotřebujete žádné speciální nástroje.

Pro úplnost bych chtěl poukázat na to, že výše uvedený návrh není ani ideální. Když pracujete s mezipamětí (jako jste), měli byste mít ve skutečnosti zcela oddělenou vrstv. Jinými slovy, design jako je tento:

 + - IMediaRepository 
 | 
 Mezipaměť (obecná) --------------- + - IProfileRepository 
 ▲ | 
 | + - IPageRepository 
 + ----------------- + ----------------- + 
 | | | 
 IMediaCache IProfileCache IPageCache 
 | | 
 | | | 
 VideoPage MyAccountPage MostPopularPage 

Výhodou je to, že pokud se rozhodnete refaktor, nikdy nebudete muset rozdělit svou Cache instanci; jak můžete ukládat média, můžete jednoduše změnit alternativní implementací IMediaRepository. Pokud přemýšlíte o tom, jak to do sebe zapadá, uvidíte, že stále vytváří pouze jednu fyzickou instanci mezipaměti, takže nikdy nemusíte načítat stejná data dvakrát.

Nic z toho neznamená, že každý kus softwaru na světě musí být navržen podle těchto náročných standardů vysoké soudržnosti a volné vazby; záleží to na velikosti a rozsahu projektu, na vašem týmu, na vašem rozpočtu, konečných termínech atd. Ale pokud se ptáte, jaký je nejlepší design (použít místo singletonu), pak to je ono.

P.S. Jak již uvedli ostatní, pravděpodobně není nejlepším nápadem, aby závislé třídy věděly, že používají cache - to je detail implementace, o který by se nikdy neměli starat. Jak již bylo řečeno, celková architektura by stále vypadala velmi podobně, jako na obrázku výše, jen byste jednotlivá rozhraní neodkazovali na Caches. Místo toho byste je pojmenovali Služby nebo něco podobného.

826
Aaronaught

V případě, že to dáte, zní to jako použití Singletonu Singleton není problém, ale příznak problému - větší, architektonický problém.

Proč obrazovky dotazují objekt mezipaměti na data? Ukládání do mezipaměti by mělo být pro klienta transparentní. Pro poskytnutí údajů by měla existovat vhodná abstrakce a implementace této abstrakce by mohla využívat ukládání do mezipaměti.

Je pravděpodobné, že závislosti mezi částmi systému nejsou správně nastaveny, a to je pravděpodobně systémové.

Proč musí obrazovky vědět, kde získávají svá data? Proč nejsou obrazovky opatřeny objektem, který dokáže splnit jejich požadavky na data (za kterými je cache skrytá)? Odpovědnost za vytváření obrazovek není často centralizována, a proto není jasné, jak by se měly závislosti vstřikovat.

Opět se zabýváme rozsáhlými otázkami architektury a designu.

Je také velmi důležité pochopit, že životnost objektu lze zcela oddělit od toho, jak je objekt nalezen použití.

Mezipaměť bude muset žít po celou dobu životnosti aplikace (aby byla užitečná), takže životnost objektu je životnost Singleton.

Problém s Singletonem (přinejmenším společná implementace Singletonu jako statické třídy/vlastnosti) je však to, jak ostatní třídy, které jej používají, jdou najít.

Při statické implementaci Singletonu je úmluva jednoduše použít, kdekoli je potřeba. Ale to zcela skryje závislost a pevně spojuje obě třídy.

Pokud poskytneme závislost na třídě, tato závislost je explicitní a všechny náročné třídy musí znát, je smlouva, kterou má k dispozici.

48
quentin-starin

Na tuto otázku jsem napsal celou kapitol . Většinou v souvislosti s hrami, ale většina by se měla vztahovat mimo hry.

tl; dr:

Vzor Gang of Four Singleton dělá dvě věci: poskytuje pohodlný přístup k objektu odkudkoli a zajišťuje, že lze vytvořit pouze jednu jeho instanci. 99% času, vše, na čem vám záleží, je první polovina toho, a nákupy podél druhé poloviny, aby to přidalo zbytečné omezení.

Nejen to, ale existují i ​​lepší řešení pro pohodlný přístup. Vytvoření globálního objektu je jadernou možností, jak to vyřešit, a způsob, jak snadno zničit vaše zapouzdření. Všechno, co je na globálech špatné, se vztahuje úplně na singletony.

Používáte-li to jen proto, že máte mnoho míst v kódu, která se musí dotýkat stejného objektu, zkuste najít lepší způsob, jak dát těmto objektům právě, aniž byste je vystavili celá kódová základna. Další řešení:

  • Příkop úplně. Viděl jsem spoustu singletonových tříd, které nemají žádný stav a jsou to jen pytle pomocných funkcí. Ti vůbec nepotřebují příklad. Prostě je udělejte jako statické funkce nebo je přesuňte do jedné ze tříd, které funkce bere jako argument. Nepotřebujete zvláštní třídu Math, pokud byste mohli dělat pouze 123.Abs().

  • Předejte to. Jednoduchým řešením, pokud metoda potřebuje nějaký jiný objekt, je jen to předat. Není nic špatného s předáváním některých objektů kolem.

  • Vložte ji do základní třídy. Pokud máte mnoho tříd, které všechny potřebují přístup k nějakému speciálnímu objektu a sdílejí základní třídu, můžete učinit tento objekt členem na základně. Když ji postavíte, předejte objekt. Odvozené objekty to nyní mohou získat, když je potřebují. Pokud je chráněna, zajistíte, že objekt zůstane zapouzdřený.

45
munificent

To není problém sám o sobě.

Opravdu si musíte dělat starosti pouze s global mutable state. Konstantní stav není ovlivňován vedlejšími vlivy, a proto je méně problémem.

Hlavní zájem o singleton spočívá v tom, že přidává spojení a tím ztěžuje testování (er). Spojení můžete snížit získáním singletonu z jiného zdroje (např. Z továrny). To vám umožní oddělit kód od konkrétní instance (i když jste více propojeni s továrnou (ale alespoň továrna může mít alternativní implementace pro různé fáze)).

Ve vaší situaci si myslím, že se s tím můžete dostat pryč, pokud váš singleton skutečně implementuje rozhraní (takže alternativu lze použít i v jiných situacích).

Ale další hlavní zpětná vazba s singletony je, že jakmile jsou na místě, je odstraní z kódu a nahradí je něčím jiným, stane se skutečným těžkým úkolem (opět je to spojení).

// Example from 5 minutes (con't be too critical)
class ServerFactory
{
    public:
        // By default return a RealServer
        ServerInterface& getServer();

        // Set a non default server:
        void setServer(ServerInterface& server);
};

class ServerInterface { /* define Interface */ };

class RealServer: public ServerInterface {}; // This is a singleton (potentially)

class TestServer: public ServerInterface {}; // This need not be.
21
Martin York

A co pak? Protože to nikdo neřekl: Toolbox. To je, pokud chcete globální proměnné.

Singleton zneužívání lze zabránit pohledem na problém z jiného úhlu. Předpokládejme, že aplikace potřebuje pouze jednu instanci třídy a aplikace nakonfiguruje tuto třídu při spuštění: Proč by měla být třída sama zodpovědná za to, že je singleton? Zdá se docela logické, aby aplikace převzala tuto odpovědnost, protože aplikace vyžaduje takové chování. Aplikace, nikoli součást, by měla být singleton. Aplikace poté zpřístupní instanci komponenty pro jakýkoli kód specifický pro aplikaci. Pokud aplikace používá několik takových součástí, může je agregovat do toho, co jsme nazvali panelem nástrojů.

Jednoduše řečeno, sada nástrojů aplikace je singleton, který je zodpovědný buď za konfiguraci samotné, nebo za umožnění spouštěcího mechanismu aplikace jej nakonfigurovat ...

public class Toolbox {
     private static Toolbox _instance; 

     public static Toolbox Instance {
         get {
             if (_instance == null) {
                 _instance = new Toolbox(); 
             }
             return _instance; 
         }
     }

     protected Toolbox() {
         Initialize(); 
     }

     protected void Initialize() {
         // Your code here
     }

     private MyComponent _myComponent; 

     public MyComponent MyComponent() {
         get {
             return _myComponent(); 
         }
     }
     ... 

     // Optional: standard extension allowing
     // runtime registration of global objects. 
     private Map components; 

     public Object GetComponent (String componentName) {
         return components.Get(componentName); 
     }

     public void RegisterComponent(String componentName, Object component) 
     {
         components.Put(componentName, component); 
     }

     public void DeregisterComponent(String componentName) {
         components.Remove(componentName); 
     }

}

Ale Hádej co? Je to singleton!

A co je singleton?

Možná právě tam začíná zmatek.

Pro mě je singleton objekt vynucený, aby měl pouze jednu instanci a vždy. Můžete k němu kdykoli a kdekoli přistupovat, aniž byste jej museli vytvářet. Proto je to tak úzce spjato s static . Pro srovnání, static je v podstatě stejná věc, kromě toho, že to není instance. Nemusíme to instinktovat, nemůžeme ani, protože je to automaticky přiděleno. A to může a může přinést problémy.

Podle mých zkušeností, nahrazení static za Singleton vyřešilo mnoho problémů v projektu středně velké patchworkové tašky, na které jsem. To znamená, že má nějaké využití pro špatně navržené projekty. Myslím, že je příliš mnoho diskuze , pokud singletonový vzorec je žitečné nebo ne a nemůžu se hádat, jestli je to opravdu špatné . Stále však existují dobré argumenty pro singleton před statickými metodami obecně .

Jediná věc, o které jsem si jistá, že je u singletonů špatná, je, když je používáme a ignorujeme dobré postupy. To je opravdu něco, s čím není snadné se vypořádat. Špatné praktiky však lze použít na jakýkoli vzorec. A já vím, že je příliš obecné říci, že ... Myslím tím, že je toho příliš mnoho.

Nechápejte mě špatně!

Jednoduše řečeno, stejně jako globální vars , singleton by měl stále = vždy se vyhnout . Zvláště proto, že jsou příliš zneužívány. Globálním výkyvům se však nelze vždy vyhnout a v posledním případě bychom je měli použít.

Každopádně existuje mnoho jiných návrhů než Toolbox, a stejně jako toolbox, každý z nich má svou aplikaci ...

Jiné alternativy

  • nejlepší článek, který jsem právě četl o singletonech navrhuje Lokátor služeb jako alternativu. Pro mě je to v podstatě " statický Toolbox ", pokud chcete. Jinými slovy, z Lokátoru služeb udělejte Singleton a máte Toolbox. To je v rozporu s jeho počátečním návrhem, jak se vyhnout singletonu, ale to je jen proto, aby se prosadil singletonův problém, je to, jak se používá, nikoliv samotný vzorec.

  • Ostatní navrhnout Factory Pattern jako alternativa. Byla to první alternativa, kterou jsem slyšel od kolegy, a my jsme ji rychle vyloučili pro naše použití jako globální var. Určitě má své použití, ale stejně tak i singletony.

Obě výše uvedené alternativy jsou dobrými alternativami. Ale vše záleží na vašem použití.

Teď, implikování singletonů by se mělo za každou cenu vyhnout, je prostě špatné ...

  • Aaronaught odpověď naznačuje, že nikdy nepoužívá singletony , z řady důvodů . Jsou to však všechny důvody proti tomu, jak je špatně využíváno a zneužíváno, nikoli přímo proti vzoru samotnému. Souhlasím se všemi starostmi o těchto bodech, jak nemůžu? Jen si myslím, že je to zavádějící.

Neschopnosti (k abstraktům nebo podtřídám) skutečně existují, ale co? Není to určeno. Neexistuje žádná neschopnost rozhraní, pokud můžu říct . Vysoká vazba může být také tam, ale to je jen proto, jak se běžně používá. Nemusí to . Spojení samo o sobě nemá nic společného se singletonovým vzorem. Toto objasnění již také eliminuje obtížnost testování. Pokud jde o obtížnost paralelizace, záleží to na jazyce a platformě, takže opět na vzorci není problém.

Praktické příklady

Často vidím, jak se používá 2, a to jak ve prospěch, tak i proti singletonům. Webová vyrovnávací paměť (můj případ) a služba protokolu .

Protokolování, někteří budou argumentovat , je perfektním příkladem singletonu, protože a cituji:

  • Žadatelé potřebují dobře známý objekt, do kterého se budou odesílat požadavky na protokolování. To znamená globální přístupový bod.
  • Protože služba protokolování je jediným zdrojem události, do kterého se může registrovat více posluchačů, musí existovat pouze jedna instance.
  • Ačkoli se různé aplikace mohou přihlásit k různým výstupním zařízením, způsob, jakým registrují své posluchače, je vždy stejný. Veškeré přizpůsobení se provádí prostřednictvím posluchačů. Klienti mohou požadovat protokolování, aniž by věděli, jak a kde bude text zaznamenán. Každá aplikace by proto používala službu protokolování přesně stejným způsobem.
  • Každá aplikace by měla být schopna se dostat pryč pouze s jednou instancí služby protokolování.
  • Jakýkoli objekt může být žadatelem o protokolování, včetně opakovaně použitelných komponent, takže by se neměl spojovat s konkrétní aplikací.

Zatímco jiní budou argumentovat, že je obtížné rozšířit logovací službu, jakmile si nakonec uvědomíte, že by to vlastně neměl být jen jeden případ.

No, říkám, že oba argumenty jsou platné. Problém zde opět není v singletonovém vzorci. Je to na architektonických rozhodnutích a vážení, pokud je refaktoring životaschopným rizikem. Je to další problém, když obvykle je refaktoring posledním potřebným nápravným opatřením.

20
cregox

Můj hlavní problém se vzorem singletonového designu je, že je velmi obtížné psát dobré jednotkové testy pro vaši aplikaci.

Každá komponenta, která má závislost na tomto „správci“, tak provádí dotazováním na jeho singletonovou instanci. A pokud chcete napsat test jednotky pro takovou komponentu, musíte do této singletonové instance vložit data, což nemusí být snadné.

Pokud je naopak váš „manažer“ vstřikován do závislých komponent prostřednictvím parametru konstruktoru a komponenta nezná konkrétní typ správce, pouze rozhraní nebo abstraktní základní třídu, kterou správce implementuje, pak jednotka test by mohl poskytnout alternativní implementace manažera při testování závislostí.

Pokud použijete kontejnery IOC) ke konfiguraci a vytvoření instancí komponent, které tvoří vaši aplikaci, můžete snadno nakonfigurovat svůj kontejner IOC) a vytvořit pouze jednu instanci „manager“, což vám umožní dosáhnout stejné, pouze jedné instance ovládající globální mezipaměť aplikací.

Ale pokud vám nezáleží na jednotkových testech, pak návrhový vzor singletonu může být naprosto v pořádku. (ale stejně bych to neudělal)

5
Pete

Singleton není v zásadním smyslu špatný, v tom smyslu, že cokoli, co počítá s designem, může být dobré nebo špatné. Může to být vždy správné (dává očekávané výsledky) nebo ne. Může to být také užitečné nebo ne, pokud to činí kód jasnějším nebo efektivnějším.

Jeden případ, ve kterém jsou užitečné tituly, je, když představují entitu, která je skutečně jedinečná. Ve většině prostředí jsou databáze jedinečné, skutečně existuje pouze jedna databáze. Připojení k této databázi může být komplikované, protože vyžaduje zvláštní oprávnění nebo průchod několika typy připojení. Organizace tohoto spojení do singletonu z tohoto důvodu pravděpodobně dává smysl.

Musíte si však být také jisti, že singleton je skutečně singleton, nikoli globální proměnná. To záleží, pokud je jediná jedinečná databáze skutečně 4 databázemi, každá pro produkci, inscenování, vývoj a testování. Databázový singleton zjistí, ke kterému z nich by se měl připojit, chytit jednu instanci této databáze, připojit ji v případě potřeby a vrátit ji volajícímu.

Když singleton není opravdu singleton (to je situace, kdy se většina programátorů rozruší), je to líný instanční globál, není žádná příležitost vstříknout správnou instanci.

Dalším užitečným rysem dobře navrženého vzoru singletonů je to, že není často pozorovatelný. Volající požádá o připojení. Služba, která poskytuje, může vrátit sdružený objekt, nebo pokud provádí test, může vytvořit nový pro každého volajícího nebo namísto toho poskytnout falešný objekt.

Použití singletonového vzoru, který představuje skutečné objekty, je naprosto přijatelné. Píšu pro iPhone a v kostru Cocoa Touch je spousta singletů. Samotná aplikace je reprezentována singleton třídy UIApplication. Existuje jen jedna aplikace, kterou jste, takže je vhodné ji reprezentovat pomocí singletonu.

Použití singletonu jako třídy správce dat je v pořádku, pokud je navrženo správně. Pokud je to skupina datových vlastností, není to lepší než globální rozsah. Pokud je to sada getrů a setterů, je to lepší, ale stále to není skvělé. Pokud jde o třídu, která opravdu spravuje všechna rozhraní k datům, včetně snad načítání vzdálených dat, ukládání do mezipaměti, nastavení a roztržení ... To by mohlo být velmi užitečné.

3
Dan Ray

Singletons jsou pouze projekcí servisně orientované architektury do programu.

API je příklad singletonu na úrovni protokolu. Máte přístup k Twitteru, Google atd. Prostřednictvím v podstatě singletonů. Tak proč se singletony v programu stanou špatnými?

Záleží na tom, jak si myslíte o programu. Pokud považujete program za společnost služeb spíše než za náhodně vázané instance v mezipaměti, pak singletony dávají dokonalý smysl.

Singletons jsou servisní přístupový bod. Veřejné rozhraní k pevně vázané knihovně funkcí, která skrývá snad velmi sofistikovanou vnitřní architekturu.

Takže nevidím singlet jako ten odlišný od továrny. Singleton může mít předány parametry konstruktoru. Může být vytvořen nějakým kontextem, který ví, jak například vyřešit výchozí tiskárnu proti všem možným mechanismům výběru. Pro testování můžete vložit vlastní vysmívat se. Takže to může být docela flexibilní.

Klíč je interně v programu, když spustím a potřebuji trochu funkčnosti, mohu přistupovat k singletonu s plnou důvěrou, že služba je připravena k použití. Toto je klíčové, když v procesu začínají různá vlákna, která musí být považována za připravená, přes státní stroj.

Obvykle bych zalomil třídu XxxService, která omotá singleton kolem třídy Xxx. Singleton není vůbec ve třídě Xxx, je rozdělen do jiné třídy, XxxService. Je to proto, že Xxx může mít více instancí, i když to není pravděpodobné, ale stále chceme mít jednu instanci Xxx globálně přístupnou v každém systému. XxxService poskytuje pěkné oddělení obav. Xxx nemusí vynucovat zásadu singleton, ale můžeme použít Xxx jako singleton, když to potřebujeme.

Něco jako:

//XxxService.h:
/**
 * Provide singleton wrapper for Xxx object. This wrapper
 * can be autogenerated so is not made part of the object.
 */

#include "Xxx/Xxx.h"


class XxxService
{
    public:
    /**
     * Return a Xxx object as a singleton. The double check
     * singleton algorithm is used. A 0 return means there was
     * an error. Developers should use this as the access point to
     * get the Xxx object.
     *
     * <PRE>
     * @@ #include "Xxx/XxxService.h"
     * @@ Xxx* xxx= XxxService::Singleton();
     * <PRE>
     */

     static Xxx*     Singleton();

     private:
         static Mutex  mProtection;
};


//XxxService.cpp:

#include "Xxx/XxxService.h"                   // class implemented
#include "LockGuard.h"     

// CLASS SCOPE
//
Mutex XxxService::mProtection;

Xxx* XxxService::Singleton()
{
    static Xxx* singleton;  // the variable holding the singleton

    // First check to see if the singleton has been created.
    //
    if (singleton == 0)
    {
        // Block all but the first creator.
        //
        LockGuard lock(mProtection);

        // Check again just in case someone had created it
        // while we were blocked.
        //
        if (singleton == 0)
        {
            // Create the singleton Xxx object. It's assigned
            // to a temporary so other accessors don't see
            // the singleton as created before it really is.
            //
            Xxx* inprocess_singleton= new Xxx;

            // Move the singleton to state online so we know that is has
            // been created and it ready for use.
            //
            if (inprocess_singleton->MoveOnline())
            {
                LOG(0, "XxxService:Service: FAIL MoveOnline");
                return 0;
            }

            // Wait until the module says it's in online state.
            //
            if (inprocess_singleton->WaitTil(Module::MODULE_STATE_ONLINE))
            {
                LOG(0, "XxxService:Service: FAIL move to online");
                return 0;
            }

            // The singleton is created successfully so assign it.
            //
            singleton= inprocess_singleton;


        }// still not created
    }// not created

    // Return the created singleton.
    //
    return singleton;

}// Singleton  
3
Todd Hoff

Nechte každou obrazovku vzít ve Správci ve svém konstruktoru.

Po spuštění aplikace vytvoříte jednu instanci správce a předáte ji.

Toto se nazývá inverze kontroly a umožňuje vám vyměnit řadič při změně konfigurace a testech. Kromě toho můžete v Parallell spustit několik instancí své aplikace nebo jejích částí (dobré pro testování!). Nakonec váš manažer zemře s vlastním objektem (spouštěcí třída).

Takže strukturu aplikace jako strom, kde věci výše vlastní vše použité pod nimi. Neimplementujte aplikaci jako mřížku, kde každý zná každého a najde se navzájem pomocí globálních metod.

1

IMO, tvůj příklad zní dobře. Navrhl bych faktoring takto: cache objekt pro každý (a za každý) datový objekt; objekty mezipaměti a přístupové objekty db mají stejné rozhraní. To dává možnost zaměnit mezipaměti dovnitř a ven z kódu; plus poskytuje snadnou cestu rozšíření.

Grafický:

DB
|
DB Accessor for OBJ A
| 
Cache for OBJ A
|
OBJ A Client requesting

Přístupový přístup k databázi a mezipaměť mohou zdědit stejný objekt nebo typ kachny tak, aby vypadal jako stejný objekt. Dokud se můžete připojit/kompilovat/testovat a stále to funguje.

To odděluje věci, takže můžete přidávat nové mezipaměti, aniž byste museli vstupovat a upravovat nějaký objekt Uber-Cache. YMMV. IANAL. ATD.

1
Paul Nathan

První otázka, najdete v aplikaci spoustu chyb? možná jste zapomněli aktualizovat mezipaměť nebo špatnou mezipaměť nebo je obtížné ji změnit? (Pamatuji si, že by aplikace nechtěla měnit velikost, pokud jste také nezměnili barvu ... můžete však změnit barvu zpět a zachovat velikost).

To, co byste udělali, je mít tuto třídu, ale ODSTRANIT VŠECHNY STATICKÉ ČLENY. Ok to není nessacary, ale doporučuji to. Opravdu prostě inicializujete třídu jako normální třídu a PASS ukazatel do. Neříkejte říkat ClassIWant.APtr (). LetMeChange.ANYTHINGATALL () .andhave_no_structure ()

Je to víc práce, ale ve skutečnosti je méně matoucí. Některá místa, kde byste neměli měnit věci, které nyní nemůžete změnit, protože již nejsou globální. Všechny mé manažerské třídy jsou pravidelné, jen s tím zacházejte.

1
user2528

Trochu pozdě na párty, ale stejně.

Singleton je nástroj v sadě nástrojů, stejně jako cokoli jiného. Doufejme, že máte více nástrojů než jen jediné kladivo.

Zvaž toto:

public void DoSomething()
{
    MySingleton.Instance.Work();
}

vs

public void DoSomething(MySingleton singleton)
{
    singleton.Work();
}
DoSomething(MySingleton.instance);

1. případ vede k vysoké vazbě atd .; 2. způsob nemá žádné problémy @Aaronaught popisuje, pokud to mohu říct. Je to všechno o tom, jak jej používáte.

1
Evgeni