/ janturon.cz / Od kodéra k analytikovi / Singleton, Nulový objekt, Prototyp

Návrhové vzory: Singleton - Nulový objekt - Prototyp

Singleton

Singleton je vzor umožňující vytvoření pouze jedné instance objektu.


Využívá Líné inicializace vytvářející objekt až v době prvního použití. Základní princip je:

  1. Zneveřejnit konstruktor, kopírovací konstruktor a operátor přiřazení
  2. Vytvořit metodu nebo atribut vytvářející nebo vracející jedinou instanci.
// C++ class Singleton { Singleton() { } Singleton(const Singleton&) { } void operator=(const Singleton&) { } public: static Singleton& getInstance() { // statická alokace při prvním použití static Singleton instance; return instance; } }; // klientský kód Singleton* a = &Singleton::getInstance(); // nový objekt Singleton* b = &Singleton::getInstance(); // existující cout << (a == b); // 1 // Singleton c; // chyba // Singleton d = Singleton::getInstance(); // chyba // Javascript var Singleton = function() { if(Singleton.instance) { return Singleton.instance; } Singleton.instance = this; }; // klientský kód var a = new Singleton(); var b = new Singleton(); alert(a===b); // true ??

Javascriptový klientský kód je matoucí: operátor new by měl vytvářet nový objekt, ale zde se (díky return v sedinici Singleton) chová jinak. Je vhodné proto doplnit kód umožňující volání jako v C++:

// Javascript Singleton.getInstance = function () { return Singleton.instance || new Singleton(); } // klientský kód var a = Singleton.getInstance(); var b = Singleton.getInstance(); alert(a===b); // true

Poznámky

// C# class Singleton { private Singleton() { } private static Singleton instance; public static Singleton Instance { get { if(instance==null) instance = new Singleton(); return instance; } } } Singleton s = Singleton.Instance;

Pozn.: ve vícevláknových aplikacích je ještě zapotřebí vzít v úvahu bezpečnost vláken (thread safety) - vláknu může být odebrán procesor po testování instance, jiné vlákno vytvoří Singleton, potom se procesor vrátí k původnímu vláknu, které už má testování podmínky za sebou a Singleton tak vytvoří také. To lze řešit zamykáním.

Nulový objekt

Mějme třídu Color:

// C++ class Color { int red, green, blue; public: int Red() { return red; } int Green() { return green; } int Blue() { return blue; } Color(int r, int g, int b) : red(r), green(g), blue(b) { } }; // Javascript function Color(red,green,blue) { this.Red = function() { return red; } this.Green = function() { return green; } this.Blue = function() { return blue; } };

Protože s neinicializovanými objekty je nutno zacházet opatrně

// C++ Color* c = new Color(255,255,255); Color* d; if(c->Red()>127) cout << "light"; // ok if(d->Red()>127) cout << "light"; // chyba! if(d!=NULL && d->Red()>127) cout << "light"; // ok // Javascript var c = new Color(255,255,255); var d; if(c.Red()>127) alert("light"); // ok if(d.Red()>127) alert("light"); // chyba! if((d instanceof Color) && d->Red()>127) alert("light"); // ok

Lze zvolit jeden ze stavů, který se prohlásí za nulový. Ukazatelům pak už nikdy nepřiřazujeme NULL, ale onen nulový objekt. Tím lze předcházet chybám nealokované paměti.

// C++ static Color* nullColor = new Color(-1,-1,-1); Color* d = nullColor; if(d->red > 127) cout << "light"; // ok if(d==nullColor) cout << "nedefinováno"; // ok // Javascript var nullColor = new Color(-1,-1,-1); var c = new Color(255,255,255); var d = nullColor; if(d.Red()>127) alert("light"); // ok if(d===nullColor) alert("nedefinováno"); // ok

Prototyp

Je implementačně zcela stejný jako Nulový objekt: jeden ze stavů je zvolen za výchozí hodnotu objektů z něj vytvořených. Rozdíl je ve způsobu použití: zatímco Nulový objekt kopírujeme odkazem (abychom mohli použít operátor == ke zjištění, je-li objekt "nulový"), Prototyp kopírujeme hluboce, aby byly z něj vytvořené objekty nezávislé!

Tovární metoda

Je podobna Singletonu implementovaného metodou, instance však nemusí být jedinečná: tovární metoda je statická a vytváří objekt třídy, ve které je definována. Je to alternativa konstruktoru, narozdíl od něj ale nemusí pokaždé alokovat novou paměť.

class Color { static const Color nullColor; int red, green, blue; public: Color(int r, int g, int b) : red(r),green(g),blue(b) { } static Color fromHexCode(string code) { if(code.length()!=6) return nullColor; int rgb[3]; char col[3]; stringstream ss; ss << code; for(int i=0; i<3; i++) { ss.get(col,3); sscanf(col,"%x",&rgb[i]); } return Color(rgb[0],rgb[1],rgb[2]); } }; const Color Color::nullColor = Color(-1,-1,-1); int main() { Color red = Color::fromHexCode("ff0000"); Color white = Color::fromHexCode("ffffff"); }