3. Třídy a objekty
(Poslední úprava 17.2.96,12.3.96,7.3.2000, 13.3.2000,15.3.2000,18.3.2000,
24.3.2002)

Obsah kapitoly:
3. Třídy a objekty
3.1. Povaha objektu
3.1.1. Co je a co není objekt
3.1.1.1 Stav
3.1.1.2 Chování
3.1.2 Identita
3.2. Vztahy mezi objekty
3.2.1. Druhy vztahů
3.2.2. Vztah propojení (užití)
3.2.3. Vztahy agregace (obsažení)
3.3. Povaha třídy
3.3.1. Co je a co není třída
3.3.2. Vnitřní a vnější pohledy na třídu – rozhraní a implementace
3.4. Vztahy mezi třídami
3.4.1. Druhy vztahů
3.4.2. Asociace
3.4.3. Dědičnost
3.4.3.1. Jednoduchá dědičnost
3.4.3.2. Jednoduchý polymorfismus
3.4.3.3. Dědičnost a typová kontrola
4.4.3.4. Volání metody
3.4.3.5. Vícenásobná dědičnost
3.4.5.6. Vícenásobný polymorfismus
3.4.6. Agregace
3.4.7. Užití
3.4.8. Práce s instancemi 
3.4.9. Metatřída
3.5. Souhra tříd a objektů
3.5.1. Vztahy mezi třídami a objekty
3.5.2. Úloha tříd a objektů v návrhu
3.6. Kvalita výstavby tříd a objektů
3.6.1. Míra kvality abstrakce
3.6.2.  Volba operací
3.6.2.1. Vlastnosti funkční sémantiky
3.6.2.2. Vlastnosti časoprostorové sémantiky
3.6.3.  Volba vztahů
3.6.3.1. Spolupráce
3.6.3.2. Mechanismus a viditelnost
3.6.4. Volba implementace
3.6.4.1 Volba representace třídy nebo objektu
3.6.4.2 Volba umístění třídy nebo objektů v modulu
3.7. Souhrn kapitoly

3. Třídy a objekty

Inženýr i umělec musejí být velmi důvěrně seznámeni s materiálem, ze
kterého tvoří. Pro objektově orientovanou tvorbu jsou základním
materiálem třídy a objekty.

3.1. Povaha objektu

3.1.1. Co je a co není objekt
Z pohledu člověka je objektem :
•	hmatatelná a viditelná věc
•	něco co lze zachytit či uchopit pomocí intelektu
•	něco, na co se zaměřuje činnost nebo mysl
Rozšířená definice označuje objekt jako "položku, jednotku, něco co
existuje ať již reálně nebo abstraktně, s dobře definovanou úlohou v
problémové doméně".
Jiná definice říká, že objekt je "něco, co má jasně definovanou
hranici".

(obr3-1.jpg)
Některé objekty jsou hmatatelné, ale nemají pevnou hranici jako např. řeka,
mlha nebo dav lidí. Některé pojmy jako čas, barva, krása nejsou objekty,
ale jsou potenciálními kandidáty na vlastnosti objektů. Objekty musí mít
vlastnost, kterou se různé objekty navzájem rozlišují. Proto přijmeme
pro objekt definici, která říká:

Objekt má stav, chování a identitu; struktura a chování podobných
objektů definuje jejich společná třída; pojem "případ" či "výskyt"
(instance) a pojem objekt budeme chápat jako synonyma.

3.1.1.1 Stav

Stav objektu zahrnuje všechny (většinou statické) vlastnosti objektu a
všechny aktuální (většinou dynamické) hodnoty těchto vlastností.
Vlastností prodejního automatu je schopnost přijímat mince. Skutečná
hodnota přijaté mince v daném okamžiku představuje dynamickou hodnotu
této vlastnosti.

Vlastnost je vrozenou nebo rozlišovací charakteristikou, rysem, kvalitou
nebo povahou, která činí objekt jedinečným. Např. Vlastností výtahu je
transport nahoru a dolů a nikoliv horizontální. Vlastnosti jsou obvykle
statické, protože jsou základní a neměnnou povahou objektů.
Každá vlastnost má určitou hodnotu. Může to být jednoduchá kvantitativní
hodnota, nebo to může být odkaz na jiné objekty. Stavem výtahu může být
např. číslo 3 označující podlaží. Stavem prodejního automatu může být
výběr nápojů, které automat nabízí. Každý nápoj představuje objekt sám o
sobě. Jednoduchá hodnota jako "tři" je nadčasová, neměnná, nemá více
výskytů - instancí (vždy je to táž "trojka") zatím co objekt existuje v
čase, může se měnit a může se vyskytovat ve více instancích, (může byt
vytvořen a zrušen, může být sdílen ap). Ze skutečnosti, že objekt má
určitý stav vyplývá, že každý objekt zaujímá určitý prostor (ať již v
reálném světě nebo v počítači).

Příklady stavu v jazyce C++

Struktura osobního záznamu může mít tvar:

struct PersonelRecord
{ 
  char name[100];
  int socialSecurityNumber;
  char department[10];
 float salary;
}
Deklarace označuje vlastnosti určité abstrakce. Označuje třídu objektů,
ne jeden objekt. Pak můžeme deklarovat jednotlivé objekty -
 instance třídy takto:
PersonelRecord deb, dave, karen, jim, tom, denise,kaitlyn,krista,elyse;

Zde je 8 různých objektů, z nichž každý zaujímá svůj prostor v paměti. Žádný z nich nesdílí prostor s jiným, ačkoliv všechny mají stejné vlastnosti.

Je vhodnější stav zapouzdřovat, než ho ponechat dostupný z okolí.
class PersonelRecord{
public
 char* employeeName() const;
 int employeeSocialSecurityNumber() const;
 char* employeeDepartment() const;
protected
 char name[100];
 int SocialSecurityNumber;
 char department[10];
 float salary;
}
Druhý způsob je komplikovanější ale vhodnější v řadě případů. Vše je ukryto uvnitř objektu. Když změníme vnitřní strukturu objektu, změní se kompilací pouze vnitřní kód. Jeho okolí se změna nedotkne.

3.1.1.2 Chování

Žádný objekt není zcela izolován. Většinou jsou objekty předmětem
jistých akcí a/nebo samy provozují činnost nad jinými objekty. Pak lze
chování definovat následujícími výroky:
"Chování je způsob, jak objekty realizují akce a reakce, prostřednictvím
změny stavu a zasílání zpráv."
"Chování objektu je úplně popsáno jeho akcemi".
Operace je akce, kterou provádí klient nad objektem. Např. operací
"push" se zvětší obsah zásobníku. Klient může také vyvolat operaci
"Length_Of", která vrátí počet prvků fronty; přitom nezmění stav fronty.
Některé OO jazyky, jako Smalltalk používají terminologie, že jeden
objekt zasílá zprávu druhému objektu. Zpráva je způsob, jak jeden objekt
provádí činnost s druhým objektem. Pro další úvahy budeme pojem "zpráva"
a "operace" považovat za synonyma. Pro operaci se používají i jiné
pojmy. V některých OO jazycích (Object Pascal) se operacím říká
"metody", C++ používá pojmu "členská funkce" (member function).

Příklady chování

Příklady chování jsou uvedeny na deklarace fronty v C++

class Queue {
public:
  Queue();
  Queue(const Queue&);
  virtual ~Queue();

  virtual Queue& operator=(const Queue&);
  virtual int operator==(const Queue&) const
  int operator!=(const Queue&) const;

  virtual void clear();
  virtual void append(const void*)
  virtual void pop();
  virtual void remove(int at);

  virtual int length() const;
  virtual int isEmpty() const;
  virtual const void* front() const;
  virtual int location(const void*);

protected:
…
}
Typ Queue představuje třídu, ne jeden objekt. Zpřístupňování před void* umožňuje  generičnost třídy a její potenciální heterogennost. Tento způsob není z pohledu typové čistoty zcela bezpečný, protože klient si musí pamatovat, jaký typ objektů vložil do fronty. Tento přístup přes ukazatel nezabezpečuje ani spolehlivou funkci destruktoru ~Queue() při destrukci všech prvků fronty.
Protože Queue představuje třídu a ne objekt, musíme deklarovat instance, s nimiž klienti mohou pracovat.
Queue a,b,c,d;
Pak teprve můžeme napsat operace nad frontami jako např.:
a.append(&deb);
a.append(&karen);
a.append(&denise);
b=a;
a.pop();

Význam operací

Klient provádí nad objekty nejčastěji 5 typů operací. Nejčastější jsou
následující tři:
•	Modifikátor ... operace, která mění stav objektu; bývá to operátor
 pro "zápis" nebo pro zpřístupnění za účelem změny
 stavu objektu
•	Selektor ... operace, která zpřístupňuje ale nemění stav
 objektu; bývá to operátor typu "čtení"
•	Iterátor ... operace, která umožňuje přístup ke všem částem
 objektu v určitém, jasně stanoveném pořadí

Protože operace jsou logicky rozčleněny, bývá užitečné je při
programování seskupovat podle jejich typu. Např. při definici objektu
"zásobník" napřed definujeme modifikátory jako: Clear, Append, Pop,
 jako procedury, potom selektory jako: Is_Equal, Length_Of,
Front_Of (Top), Is_Empty jako funkce, a pak deklarujeme iterátory a
generické operace (Iterate);
Některé jazyky jako Smalltalk, C++, Object Pascal, CLOS deklarují další
dva druhy operací:
•	Konstruktor ... Operace, která vytváří objekt a/nebo inicializuje
 jeho stav
•	Destruktor ... Operace, která "nuluje" stav objektu a/nebo
 likviduje objekt jako takový

V C++ jsou konstruktory a destruktory deklarovány jako část definice
třídy, zatímco ve Smallalku a CLOS jsou takové operace typicky částí
protokolu metatřídy (třídy tříd).
Ve Smalltalku mohou být operace definovány jen jako metody, protože
jazyk neumožňuje deklarovat procedury a funkce oddělené od tříd. Na
rozdíl od toho Object Pascal, C++, CLOS a ADA umožňují vytvářet operace
jako "volné podprogramy". "Volné podprogramy" jsou procedury nebo
funkce, které slouží jako neprimitivní operace nad objektem nebo objekty
téže nebo odlišné třídy. Volné podprogramy bývají typicky seskupeny
podle tříd, nad kterými jsou postaveny. Proto takovým uskupením volných
podprogramů říkáme "pomocné programy třídy" (class utilities). Pro uvedenou deklaraci třídy Queue zle napsat následující volný podprogram

void  copyUntilFound(Queue& from, Queue& to, void* item);
{
  while ((!from.isEmpty())) && (from.front() != item)) {
     to.append(from.front());
     from.pop()
   }
}

Účelem této operace je číst a rušit položky jedné fronty a vkládat je do druhé, až se v první frontě narazí na položku item. Je obvyklé posbírat logicky k sobě patřící volné podprogramy a vložit jejich deklarace  do třídy, která nemá žádný stav. Tato operace není primitivní. Je vytvořena ze základních operací, které jsou součástí třídy (package) Simple_Queue. Můžeme říci, že všechny metody jsou operace, ale ne všechny operace jsou metody. Jsme často v pokušení učinit všechny operace metodami. Někdy je to však nevhodné; v případě, že operace ovlivňuje dva nebo více různých objektů, není výhodou, je-li operace deklarována v jedné třídě.

Metody a "volné podprogramy" dohromady, náležející k jistému objektu
vytvářejí "protokol" tohoto objektu. Protokol vytváří "obálku"
dovoleného chování objektu a zahrnuje jak statický, tak dynamický pohled
na objekt.

Objekt jako stroj

Existence stavu objektu naznačuje, že může být významné pořadí operací,
které se nad objektem aplikuje. To budí dojem, že každý objekt se chová
jako malý, nezávislý stroj - mechanismus. Pro některé objekty je časové
uspořádání operací natolik významné, že chování objektu se lépe
charakterizuje pojmy konečného stavového automatu.
Objekty mohou být aktivní a pasivní. Aktivní objekty mají vlastní linii
řízení, zatím co pasivní objekty ji nemají. Aktivní objekty jsou obecně
autonomní, to znamená, že mohou provádět nějaké chování bez vnějšího
vlivu jiných objektů. Pasivní objekty mění stav pouze pod vlivem z
vnějšku. Aktivní objekty v systému slouží jako kořeny linie řízení.
Je-li náš systém paralelní, musí obsahovat několik aktivních objektů.
Sekvenční systémy mají v daném čase jen jeden aktivní objekt (jednu
linii řízení).

3.1.2 Identita

Podle Khosafiana a Copelanda: "Identita je vlastnost objektu, která
zajišťuje odlišnost objektu od jiných objektů". Většina programovacích a
databázových jazyků používají jmen proměnných pro odlišení dočasných
objektů. Tím se směšuje adresovatelnost s identitou. Většina
databázových systémů používá identifikačního klíče k rozlišení
perzistentních objektů a tím směšují hodnotu údajů s identitou.
Neschopnost rozlišit mezi jménem objektu a objektem samým vede k mnoha
druhům chyb v objektově orientovaném programování.

Příklady identity

Předpokládejme následující deklaraci v C++, která představuje bod v dvojrozměrném prostoru:
struct Point {
  int x;
  int y;
  Point() : x(0), y(0), {}
  Point(int xValue, int yValue) : x(xValue), y(yValue) {}
};
Zde jsme deklarovali bod Point jako záznam a ne jako plnohodnotnou  třídu, aby příklad byl jednoduchý. Pro usnadnění práce jsme vytvořili jeden konstruktor pro nastavení implicitní hodnoty (0,0) a jeden konstruktor pro explicitní nastavení hodnoty. Jestliže ale náš záznam potřebuje více než ustavení a čtení, pak je lepší vytvořit třídu.

Ukažme dále příklad třídy označující zobrazení položky. Je to abstrakce společná všem GUI. Representuje třídu objektů, které mají vizuální reprezentaci v nějakém okně, a zachycuje strukturu a chování společné všem takovým objektům.

class DisplayItem {
  public:
    DisplayItem();
    DisplayItem(const Point& location);
    virtual ~DisplayItem();

   virtual void draw();
   virtual void erase();
   virtual void select();
   virtual void unselect();
   virtual void move(const Point& location);

   int isSelected() const;
   Point location() const;
   int isUnder(const Point& location) const;

protected:
…
}
Uvedená deklarace není úplná. Záměrně byly vynechány konstruktory a operace pro čtení, zápis a test rovnosti. 
Protože jsme očekávali, že klient bude vytvářet podtřídu, jsou všechny destruktory a modifikátory jsou virtuální. Zvláště u draw očekáváme, že dojde k předefinování respektujícímu vlastnosti objektu podtřídy. Naopak, selektory nejsou virtuální, protože se neočekává jejich předefinování v podtřídách. Selektor isUnder je složitější; neočekává pouze vracení hodnoty stavu, ale také výpočet, zda zadaný bod je uvnitř rámečku zobrazujícího položku item.
Při deklaraci instancí této třídy můžeme např.:

DisplayItem item1;
DisplayItem* item2 = new DisplayItem(Point(75, 75));
DisplayItem* item3 = new DisplayItem(Point(100,100);
DisplayItem* item4 = 0;

Pouze item2 a item3 ukazují na odlišné body. Tyto body nemají jméno a odkaz je pouze nepřímý, přes ukazatel. Tedy item2 ukazuje na bod pomocí *item2. Item1 je jméno  statického bodu a item4 je ukazatel na neexistující bod.

(fig3-1.bmp)
Situaci zobrazuje fig3-1.jpg/a
Situaci po provedení příkazů:
item1.move(item2->location());
item4=item3;
item4->move(point38,100));
zobrazuje  obrázek fig3-1.jpg/b.

Situaci po provedení příkazů
item2=&item1;
item4->move(item2->location());
znázorňuje obr. fig3-1.jpg/c

První příkaz zavádí vztah zvaný "alias", kdy identifikátor item2 označuje stejný objekt jako item1. Druhý příkaz dosahuje stavu objektu  item1 prostřednictvím nově vytvořeného alias. Zde ale došlo k "prosáknutí" paměti; objekt původně přiřazený k item2 nelze nadále odkázat tímto identifikátorem, ani přímo ani nepřímo, a tak objekt ztratil svou identitu. V jazycích jako Smalltalk nebo Clos budou takové jazyky podrobeny zpracování garbage collectoru a tím bude jejich prostor regenerován. V C++ bude paměť obsazena až do konce programu bez jakékoli využitelnosti. Podobná vlastnost může být u dlouho běžících programů nepříjemné nebo i nebezpečné.

Duplikace, přiřazení a rovnost
Strukturální sdílení je důsledkem vztahu alias, který dá objektu druhé jméno. Ve většině OOP nelze takovému jevu zabránit. 
void highlight(DisplayItem& i);
void drag(DisplayItem i); // Nebezpečný zápis
Vyvolání první funkce s parametrem item1 vyvolá alias: formální parametr i označuje referenci (ukazatel) na objekt označený skutečným parametrem a tak i a item1 pojmenovávají v době výpočtu shodný objekt. Druhý příkaz vyvolaný se skutečným parametrem vytvoří item1 vytvoří kopii (duplikát) skutečného parametru a nemá tedy význam vztahu alias; i pak označuje úplně jiný objekt (se stejným stavem jako item1). V jazycích, které rozlišují předávání parametrů odkazem a hodnotou, je třeba bedlivě rozlišovat, má-li příkaz pracovat s originálním objektem, nebo jeho duplikátem. Proto je pro  polymorfismus často požadováno předávání odkazem.  
V některých případech je ale duplikace požadována a jazyky jako C++ mají možnost ovládat sémantiku kopírování. Můžeme např. zavést konstruktor, který je součástí deklarace DisplayItem:
DisplayItem (const DisplayItem&);
V C++ může být takový kopírovací konstruktor vyvolán explicitně, nebo implicitně při předávání parametru hodnotou. Opomenutí uvést takový konstruktor způsobí implicitní volání, jehož sémantika je definovaná jako členský duplikát. U objektů, jejichž stav sám zahrnuje ukazatele nebo odkazy na jiné objekty, je implicitní vyvolání duplikace velmi nebezpečné. Proto je nepsaným zákonem, že explicitní duplikaci vynecháme jen u primitivních hodnot. Ve všech ostatních případech je vhodné používat explicitní vytváření kopií (duplikátů). 
Tyto praktiky nazývají některé jazyky jako plytké nebo hluboké kopie. Např. Smalltalk vytváří pomocí shallowCopy duplikát, který duplikuje objekt, ale společně sdílí stav, zatím co deepCopy duplikuje objekt i stav.

Přiřazování je obecně také mechanismem pro duplikaci a jazyky jako C++ mohou ovládat jeho sémantiku. Lze např. zavést deklaraci:
virtual DisplayItem& operator=(const DisplayItem&);
Deklarujeme operátor jako virtuální, protože mechanismus přiřazení se může u dědiců lišit.  Podobně jako u duplikace, můžeme u přiřazení definovat plytkou nebo hlubokou sémantiku kopie. Při vynechání explicitní deklarace se vyvolá implicitní přiřazovací sémantika, která je definovaná jako členská kopie. 
S uvedenými úvahami úzce souvisí predikát rovnosti. Ačkoliv se to zdá být jednoduché, rovnost může mít dva významy:
•	dvě různá jména označující týž objekt
•	dvě různá jména označující dva různé objekty , jejichž stav se sobě rovná

Např. na obr. fig3-1.jpg/c  platí oba typy rovností pro item1 a item2 ,ale pro item1 a item3 platí pouze druhý případ rovnosti. 
V C++ není žádný implicitní operátor rovnosti a tak musíme zavést vlastní explicitní operátor:
virtual int operator ==(const DisplayItem&) const;
int operator !=(const DisplayItem&) const;
Jak jsme už zavedli, operátor rovnosti je virtuální, protože může být u dědiců předefinován. Naopak operátor nerovnosti (!=) není virtuální, protože ho chápeme jako opak rovnosti.
V tomto stylu můžeme deklarovat také operátory pro relaci uspořádání.

Rozsah  života objektu
Doba života objektu začíná u jeho vytvoření (a přidělení prostoru) až do vrácení prostoru, který zaujímá. Explicitně vytvoříme objekt jeho deklarací nebo alokací. Deklarací objektu, jako např. item1, se vytvoří nová instance na zásobníku. Alokací objektu,  jako item3, se nové instanci přidělí prostor na heapu. V obou případech v C ++ vyvolá konstruktor, jehož úkolem je vyhradit paměťový prostor a ustavit počáteční stav.
Objekty se často vytvářejí implicitně. v C++ např. při předávání parametru hodnotou.
V jazycích s garbage collectorem se objekt automaticky likviduje, když na něj přestanou existovat odkazy. V C++, které nemá garbage collector existují objekty i když na ně žádné reference neexistují. Objekty, které jsou alokovány na stacku se ztrácí, když je ukončen blok, v němž jsou deklarovány. Objekty na heapu, vytvořené pomocí new se musí zrušit pomocí delete. Nepoužití delete může vést k "prosakování" paměti, jak bylo uvedeno v předchozím příkladu. Dealokování objektu dvakrát (nejčastěji ve spojení s aliasem) je podobně špatné a může vést k poruše v paměti nebo zhroucení celého systému.
Poněkud jiné chování mají objekty, které jsou persistentní. Persistentní objekty jsou nejčastěji součástí rozsáhlejší databáze a jejich rušení je součástí ovládacího systému takové databáze.

Pro testování, zda dvě jména označují týž objekt používají jazyky tyto
operátory:
 Smalltalk ==
 Object Pascal =
 C++ ==
 CLOS eql
 Ada =
Smalltalk a C++ dovoluje předefinování těchto operátorů. Pro testování,
zda dva různé objekty mají stejný stav používají uvedené jazyky tyto
operátory:
 Smalltalk =
 Object Pascal definováno uživatelem
 C++ definováno uživatelem
 CLOS equalp
 Ada definováno uživatelem

3.2. Vztahy mezi objekty
3.2.1. Druhy vztahů
	
Objekt sám o sobě je nezajímavý. Jeho význam je především ve vzájemných
vztazích objektů objektově orientovaného systému. Ingalls říká:"Místo
procesorů, které se vrhají na bity a plundrují datové struktury, svět
|objektů je pln vznešených společenských mravů, kde jeden objekt žádá
druhý o splnění svých různých přání". Vezměme např. objektovou strukturu
letadla, která může být definována jako "soubor částí, které mají
vrozenou tendenci spadnout na zem a které vyžadují neustálý dohled a
snahu, jejímž výsledkem musí být zabránění tomuto pádu". Jen dokonalá
souhra všech součástí umožní tomuto souboru součástek létat.
Vztah mezi dvěma objekty zahrnuje všechny předpoklady, které platí o
vztahu jednoho účastníka ke druhému, včetně všech použitelných operací a
výsledků chování objektů. Dvě významné hierarchie tvoří:
•	Vztahy užití - propojení
•	Vztahy obsažení - agregace
Seidewitz a Stark říká těmto vztahům "seniorství" a "vztah
rodič/potomek".

3.2.2. Vztah  propojení – (užití)

Propojení představuje vztah v němž  jeden objekt (klient) využívá služeb jiného objektu (dodavatele). 0br. fig3-2.jpg popisuje několik typů propojení. Na obr. představuje propojení dvou tvarů představujících objekt a pojmenovaná šipka představuje zprávu. Na obrázku má objekt aControler propojení na dvě instance  DisplayItem , (a a b). Na obrázku je propojení z objektu a do pohledu aView. Podobné propojení asi existuje z obou instancí, ale v příkladu zdůrazníme jen jedno. 
Většinou je propojení pomocí zprávy jednosměrné, ale může se vyskytnout i obousměrné propojení. Na obrázku např. aController může vyvolat operace nad dvěma zobrazovanými objekty (např. posunout je, nebo získat jejich polohu) ale objekty nemohou operovat samy nad sebou prostřednictvím aController. Toto oddělení vlivu je typické pro dobře strukturované programy. Všimněme si, že ačkoliv zpráva směřuje od klienta (aController) k dodavateli (objekt a), data mohou směřovat ve všech směrech: např. aController  vyvolá operaci move nad objektem a, data plynou od klienta k dodavateli. Pak ale aController vyvolá operaci isUnder nad objektem b a výsledek prochází od dodavatele ke klientovi.

 Ve většině systémů hraje objekt jednu ze tří rolí:
•	Činitel (actor) - je objekt, který operuje s jinými objekty a přitom sám není manipulován jinými objekty. V jistém smyslu jsou "aktivní objekt" a činitel synonyma.
•	Sluha (server) - je objekt, který nikdy neoperuje s jinými objekty; je pouze manipulován/užíván jinými objekty.
•	Agent - objekt, který operuje s jinými objekty a sám je manipulován/užíván jinými objekty. Agent je obvykle vytvořen, aby pracoval pro nějakého činitele nebo jiného agenta.

V obr. fig3-2.jpg představuje aController činitele, aView representuje server.

Příklad
V některých průmyslových procesech se používá systém pro ohřev materiálu. Systém ohřeje materiál předepsaným způsobem na danou teplotu, ne které teplotu po danou dobu udržuje. Poté se materiál chladí opět předepsaným způsobem. Někdy je třeba ochlazování velmi pomalé, jindy naopak rychlé. Takové zařízení má tedy přesně definovaný způsob chování. Napřed tedy zavedeme typ pro uplynulý čas v minutách:

// Číslo v minutách
typedef unsigned int Minute;

Dále zavedeme typ pro ohřevné zařízení:
class RemperatureRamp {
public:
  TemperatureRamp();  
  virtual ~TemperatureRamp();
  virtual void clear();
  virtual void bind(Temperature, Minute);
  
  Temperature temperatureAt(Minute);
protected:
 …
};
Toto zařízení představuje více než systém mapující čas/teplotu. Můžeme požadovat, aby v čase 60 minut byla teplota 250 a v čase 180 minut 150, potřebujeme ale vědět, jaká teplota bude ve 120. minutě. To vyžaduje lineární interpolaci, což je další vlastnost tohoto zahřívacího zařízení. Od objektu nechceme explicitní ovládání teploty pro určité zahřívací zařízení. Dáváme přednost většímu oddělení funkcí a tak požadované chování získáme spoluprací tří objektů: instance ohřevného zařízení, ohřívač a řídicí jednotka ohřevu. Pak můžeme mít:
class TemperatureController {
public:
  TemperatureController(Location);
  ~TemperatureController();

  void process(const TemoperatureRamp&);
  
  Minute schedule(const TemperatureRamp&) const;

private:
 …
};
Operace process poskytuje chování této abstrakce, které zajistí chování určitého ohřevného zařízení. Např. deklarace:

TemperatureRamp growingRamp;
TemperatureController rampController(7);

ustaví ohřevné zařízení a zadá řídící jednotce požadované chování zápisem:
growingRamp.bind(250, 60);
growingRamp.bind(150, 180);

rampController.process(growingRamp);

Podívejme se na vztah objektů growingRamp a rampController. Objekt rampController je agent zodpovědný za  vytvoření ohřevného zařízení a používá growingRamp jako server. Toto propojení vyjadřuje skutečnost, že objekt rampController používá objekt používá objekt growingRamp jako parametr jedné své operace.
  
Viditelnost

Předpokládejme existenci dvou objektů, A a B a jejich propojení.  Aby mohl objekt A zaslat zprávu objektu B, musí být B objektu A nějak viditelný. Viditelnost začíná být významný, když se začneme zabývat implementací problému. 

V předchozím příkladu vidí objekt rampController na objekt growingRamp, protože oba objekty jsou deklarovány se stejným rozsahem existence a objekt growingRamp je parametrem operace nad rampController.  Můžeme rozlišit čtyři typy viditelnosti:
•	Nadřazený objekt je globální vůči klientovi
•	Nadřazený objekt je parametrem některé operace klienta
•	Nadřazený objekt je částí objektu klienta
•	Nadřazený objekt je deklarován lokálně v operaci klienta

Zůstává otázkou taktiky, jak učiníme jeden objekt viditelný druhému.

Synchronizace

Kdykoli jeden objekt pošle zprávu druhému, říká se, že tyto dva objekty jsou synchronizovány. V sekvenční aplikaci se synchronizace realizuje sekvenčním voláním metod. V systému s vícenásobnou linií řízení je to však složitější a musí se použít nástroj vzájemného vyloučení, který je známý z teorie paralelních procesů.  Jak už bylo popsáno dříve, aktivní objekty obsahují svou vlastní linii řízení a tak se očekává, že jejich sémantika je zajištěna přítomnosti ostatních aktivních objektů. Jestliže aktivní objekt má spojení na pasivní, pak musíme vybrat jednu ze tří možností synchronizace.
•	sekvenční – sémantika pasivního objektu je zajištěna pouze přítomností jednoho pasivního objektu v daném okamžiku
•	řízená – sémantiky pasivních objektů jsou zajištěny existencí vícenásobné linie řízení, ale aktivní klienti musí spolupracovat při dosahování vzájemného vyloučení
•	synchronní - sémantiky pasivních objektů jsou zajištěny existencí vícenásobné linie řízení, a dodavatel zajišťuje vzájemné vylučování

Agregace

(obr fig3-3.bmp)

Zatímco propojení representuje vztah "rovný s rovným" (peer to peer") nebo vztah "klient/dodavatel" (client/supplier), agregace representuje vztah hierarchie - "celek/část". Přitom existuje možnost přejít od "celku" (agregátu) k jeho částem (atributům).  V tomto smyslu je agregace druhem asociace. Na obr. 3.3 má objekt rampController propojení na objekt growingRamp a stejně tak na atribut h třídy Heater; z toho vyplývá, že rampController je celek a heater h je částí jeho stavu. Pro daný rampController je možno zjistit jeho ohřívač h. Je-li dán objekt h, pak přístup k jeho zapouzdřujícímu objektu (říká se mu také "kontejner" je možný pouze tehdy, je-li tato informace součástí stavu objektu h. 
Agregace i propojení má své výhody a nevýhody. Výhodou agregace je, že může zapouzdřovat části a ukrývat je před okolím. Výhodou propojení je, že dovoluje volnější spřažení objektů.

Příklad
Můžeme pokračovat v privátní části deklarace TemperatureController deklarací:
Heater h;
Podle deklarace třídy Heater z předcházející kapitoly musíme správně vytvořit tento atribut, protože jeho třída neposkytuje implicitní konstruktor. A tak můžeme napsat implicitní konstruktor pro TemperatureControllor takto:
TemperatureController::TemperatureController
(Location1) 
  :h(1){]
XXX
3.3. Povaha třídy
3.3.1. Co je a co není třída

Objekt a třída spolu souvisí natolik úzce, že nemá smyslu hovořit o jednom bez druhého. Zatímco objekt je konkrétní existující realita (entita) třída je abstrakce, obsahující základní rysy objektu.
Třída je množina objektů, které mají společnou strukturu a chování

 Savec je třída, představující všechny živočichy, kteří mají
charakteristické rysy savců.

(obr3-2.bmp)
Jeden objekt je jedním výskytem - instancí - své třídy. Objekt nemůže
být třídou, i když je možné, že třída, za jistých okolností, může být
objektem.

3.3.2. Vnitřní a vnější pohledy na třídu – implementace a rozhraní

Myers a Snyder navrhují, že programování je většinou "zakázková" -
"dohodová" záležitost (zakázka, smlouva, dohoda - contract). Různé
funkce rozsáhlého programu jsou dekomponovány na menší problémy na
základě "dohody" o různých částech celého návrhu. OO návrh je pro tento
přístup typický. Zatímco jednotlivý objekt hraje svou individuální roli
v systému, třída zachycuje obecné chování všech svých objektů. Třída
může sloužit jako vazební dohoda mezi abstrakcí a všemi jejími klienty.
Zachycení těchto vlastností do rozhraní (interface) třídy umožňuje
jazykům se silnou typovou kontrolou detekovat porušení takové dohody již
v době kompilace.
Vnější pohled na třídu representuje "rozhraní", zatímco vnitřní pohled na
třídu representuje "implementace". Rozhraní informuje klienta o
možnostech a chování abstrakce a ukrývá způsob, jakým je toto chování
realizováno uvnitř třídy. Implementace třídy zahrnuje všechny detaily
a datové struktury, kterými jsou metody a chování třídy provedeny.
Rozhraní lze dále dělit na tři části:
 Veřejné rozhraní -  (public interface) deklarace, které tvoří tu část rozhraní třídy, která je viditelná všem klientům, kteří jsou viditelní třídě
 Chráněné rozhraní (protected) deklarace, které tvoří část rozhraní třídy, které nejsou viditelné z jiných tříd ale jsou viditelné ze všech podtříd
 Soukromé rozhraní - (private) deklarace, které tvoří část rozhraní třídy, které není viditelné pro žádné jiné třídy

Nejlépe splňuje realizaci těchto tří typů rozhraní C++. Smalltalk,
Object Pascal a CLOS dosáhnou těchto rozlišení vnucením jistých
programátorských konvencí.
Stav objektu je representován nejčastěji prostřednictvím konstant a
proměnných deklarovaných v soukromé části rozhraní. Z toho pohledu je
representace společná všem objektům třídy zapouzdřena a její změny
neovlivní žádné okolní klienty.
Pozorný čtenář se může tázat, proč je reprezentace objektů částí
rozhraní a ne částí implementace. Důvod je praktický. Jiný přístup by si
vynucoval objektově orientovaný hardware nebo složitou kompilační
techniku. Když kompilátor zpracovává deklaraci objektu jako např v C++:
Shape aShape;
musí znát, kolik paměti má pro objekt vyhradit. Kdybychom definovali
representaci objektu v implementaci třídy, museli bychom dokončit
implementaci dříve, než bychom mohli použít jakéhokoli klienta; tím
bychom potlačili hlavní účel oddělení vnitřního a vnějšího pohledu na
třídu.
Konstanty a proměnné, které representují třídu, jsou v různých jazycích
známy pod různými jmény: Smalltalk používá pojmu "instanční proměnná",
Object Pascal používá pojmu "složka" (field), C++ "členský objekt" (member
object), a CLOS používá pojem "štěrbina, otvor" (slot).

3.4. Vztahy mezi třídami

3.4.1. Druhy vztahů

Představme si podobnosti a rozlišnosti následujících tříd objektů:
květiny, sedmikrásky, rudé růže a okvětní plátek. Můžeme popsat
následující pozorování:
 sedmikráska je druh květiny
 růže je (jiný) druh květiny
 rudé růže a žluté růže jsou druhy růží
 okvětní lístek je částí obou druhů květin
Můžeme rozlišovat tři druhy vztahů. Prvním je "zobecnění", označující
"druh" nebo "typ" něčeho. Např. růže je druh květiny. To vyjadřuje
skutečnost, že "růže" je specialisovaná podtřída obecnější třídy
"květina". Druhým vztahem je "agregace", která označuje vztah "něco je
částí něčeho". Třetím vztahem je asociace. Tento vztah vyjadřuje
sémantické spojení dvou jinak zcela odlišných a nezávislých tříd. Jako
příklad uveďme třídy "růží" a "svící", které obě slouží k ozdobě
slavnostního stolu. Ve většině jazyků jsou vztahy typu "zobecnění",
"agregace" a "asociace" podporovány následujícími vztahy mezi třídami:
•	vztahy dědičnosti
•	vztahy užití
•	vztahy instanční
•	vztahy metatřídové
Někdy se setkáme s alternativou dědičnosti ve formě "delegace", kdy na
objekty se nahlíží jako na prototypy, které "delegují" své chování do
objektů k nim vztažených a tím vylučují nutnost pojmu "třída".
Dědičnost je jedním z nejmocnějších mechanismů a dá se použít jak pro
"generalizaci" tak pro "asociaci". Nestačí na vyjádření všech vztahů,
které se v problémové doméně mohou vyskytnout.

3.4.2. Asociace

(fig3-4.bmp)
Představme si prodejní automat a dvě jeho klíčové abstrakce: druh zboží (Product) a prodej (nákup) (Sale). Obrázek 3-4 ukazuje jednoduchou asociace mezi těmito třídami. Třída Product označuje zboží, jako část nákupu. Třída Nákup označuje transakci jejíž prostřednictvím je několik položek zboží nakoupeno. 

V C++ to můžeme implementovat prostřednictvím mechanismu , kterému Rumbaugh říká "pohřbené ukazatele":
class Product;
class Sale;

class Product {
public:
 …
protected:
  Sale* lastSale;
};
class Sale{
public:
 …
protected:
  Product** productSold;
};
Na tomto příkladu vidíme implementaci vztahu "jeden ku mnoha". Každá instance třídu Product má ukazatel na jeden "poslední nákup". Každá instance "nákup" může mít skupinu ukazatelů na položky zboží.

Kardinalita
Příklad ukázal asociaci typu "jeden ku mnoha". Mnohačetnost asociace vyjadřuje kardinalitu vztahu. Asociace má tři možné podoby:
•	jedna ku jedné
•	jedna ku mnoha
•	mnoho ku mnoha

Příklad asociace jedna ku jedné je např. nákup na automatu kreditní kartou. Každá instance Sale má vztah právě k jedné instanci CreditCardTransaction. Vztah mnoho ku mnoha je také obvyklý. Každá instance třídy zákazník Customer může iniciovat transakci s několika instancemi třídy prodavač SalesPerson a každý prodavač může mít vztah s několika různými zákazníky.

3.4.2. Dědičnost

Příklady vztahu dědičnosti

Jako příklad předpokládejme informaci o stavu různých měřicích
subsystémů kosmické stanice dohromady označované jako "telemetry data".
Přenášejí se sériově jako bitový proud obsahující hlavičku, která
obsahuje časové označení (time stamp) a identifikační klíč druhu
informace a několik rámců dat zpracovaných z několika subsytémů a
snímačů. V C++ bychom mohli nabýt pokušení napsat pro každý typ dat
záznam v následující podobě:

class Time 

struct ElectricalData {
 Time timeStamp;
 int id;
 float fuelCell1Voltage,fuelcell2Voltage;
 float fuelCell1Amperes;fuelCell2Amperes;
 float currentPower;
}; 

obr3-3.bmp

Jednou z nevýhod takové struktury je, že je zcela nezapouzdřená. Každá
změna ve struktuře se promítne u všech klientů a bude se muset
překompilovat každý modul s odkazem na tuto strukturu. Změna může
porušit i předpoklady, které byly uplatněny v klientských modulech a to
může znamenat zhroucení logiky systému. Předpokládejme dále, že analýza
zjistí potřebu několika stovek různých telemetrických dat, včetně
dalších elektrických dat zahrnujících předcházející strukturu. Zjistíme,
že deklarace všech těchto struktur povede ke značné redundanci a
opakujícím se datovým strukturám a funkcím.
Lepším způsobem jak řešit tento problém je deklarovat jedno třídu pro
každý druh telemetrických dat. Tím způsobem lze zahrnout návrhová
rozhodnutí do jedné třídy. Tím způsobem se ukryje vnitřní pohled na
třídu do implementace, ale ani tento přístup neredukuje duplicitu a
redundanci.
Nejlepším způsobem je zahrnout návrhová rozhodnutí do hierarchie tříd,
kde specializované třídy zdědí chování definované na obecnější
hierarchické hladině. Např.:

class TelemetryData {
public:

 TelemetryData ();
 virtual ~TelemetryData ();
 virtual void transmit ();
 Time currentTime () const;

protected:
  int id;
 Time timeStamp;
};
Zde je deklarována třída i se svým konstruktorem a virtuálním destruktorem
(označeným tildou ~, virtuálním proto, že očekáváme vznik podtříd) a dále funkce transmit a currentTime, které jsou viditelné všem klientům. Členský objekt id  a timeStamp jsou poněkud lépe obaleny, ale protože jsou deklarovány jako "protected" je viditelný pouze z vnitřku vlastní třídy a z tříd, které dědí 
Zkusme nyní přepsat deklaraci pro ElectricalData jako třídu:

class ElectricalData : public TelemetryData {
public:
 ElectricalData(float v1,float v2,float a1,float a2);
 virtual ~ElectricalData(void);

 virtual void transmit ();
 float currentPower() const;

protected:
 float fuelCell1Voltage, fuelCell2Voltage;
 float fuelCell1Amperes, fuelCell2Amperes;
};

Tato třída zdědí strukturu a chování třídy TelemetryData,  přidá
čtyři členské objekty (napětí a proud palivových článků) a
předefinuje chování (smysl operace transmit). 

3.4.3.1. Jednoduchá dědičnost

Dědičnost je vztah, který určuje, že jedna třída sdílí strukturu a
chování definované v jiné třídě (jednoduchá dědičnost) nebo ve více
jiných třídách (vícenásobná dědičnost). Třídy, od nichž daná třída dědí,
se nazývá "nadtřída" (superclass). Podobně třídu, která dědí nazýváme
"podtřídou" (subclass). Jak je vidět, podtřída může rozšířit nebo
předefinovat existující strukturu nebo chování nadtřídy. Možnost
podporovat tento způsob práce odlišuje "objektově orientované jazyky" od
"jazyků založených na objektech".

Obr. fig3-5.bmp znázorňuje vztah jednoduché dědičnosti mezi třídami.
Obrázek znázorňuje vztah jednoduché dědičnosti mezi nadtřídou
TelemetryData a jejími podtřídami. Třída CameraData je podtřídou třídy
SensorData a ta je opět podtřídou třídy TelemetryData.
V uvedeném schématu předpokládáme, že některé třídy budou mít své
instance, zatím co jiní nikoliv. Např. specializované třídy jako
ElectricalData nebo SpectrometerData budou mít své instance, zatímco
obecnější třídy jako mezitřída "SensorData" nebo třída TelemetryData
nebudou mít své instance. Třídám, které nemají žádné instance, se říká
"abstraktní třídy". O abstraktní třídě se předpokládá, že její podtřídy
doplní implementaci jejich (často nekompletních) metod. Ve Smalltalku
je povoleno s použitím metody abstraktní třídy "subclassResponsibility"
vnutit podtřídě předefinování metody abstraktní třídy. Když se podtřídě
nepodaří předefinování, pak vyvolání takové metody způsobí chybu v době
výpočtu. Podobně C++ dovoluje, aby programátor napsal tvrzení, že metoda
abstraktní třídy nemůže být vyvolána přímo tím, že inicializuje její
deklaraci na nulovou hodnotu. Taková metoda se nazývá "čistá virtuální
metoda" a jazyk zamezuje vytvářet instance třídy, která vyváží takové
funkce.

Nejobecnější třídou je "bázová třída". Mnohé aplikace mají řadu
bázových tříd, které představují zobecněné kategorie abstrakcí v dané
aplikační doméně. Některé jazyky zavádějí nejvyšší supertřídu, která
funguje jako absolutní nadtřída všech tříd. Ve Smalltalku se tato třída
jmenuje Object a v Object Pascalu TObject. Nejvyšší supertřída v C++ je
anonymní.

Daná třída má typicky dva druhy klientů:
•	instance
•	podtřídy

Je užitečné definovat pro oba druhy klientů různá rozhraní. Právě to je
důvodem, proč C++ má tři druhy rozhraní : public, protected a private.
Smalltalk, Object Pascal C++ dovolují, aby podtřída rozšířila datovou
strukturu nadtřídy. Uvedení členského objektu se shodným identifikátorem
jako nadtřída, ve snaze o jeho předefinování, se však kvalifikuje jako
chyba! Podobně nelze u těchto jazyků redukovat zděděnou strukturu.
Existuje jisté napětí mezi dědičností a ukrýváním dat. Abychom mohli
použít zděděné vlastnosti a metody, musíme často studovat i na
implementační úrovni výstavbu zděděných struktur a metod.
Podtřídy dědí také chování (metody) své nadtřídy. Většina OO jazyků
dovolují zdědění ale i vyloučení metod nadtřídy, a dovolují přidání
nových metod a modifikaci zděděných. V jazycích jako Smalltalk, Object
Pascal a CLOS je povoleno předefinování metod nadtřídy. Takovým operacím
se říká "generické funkce". V Object Pascalu se to provede s použitím
klíčového slova "override" v deklaraci metody, která má být
předefinovaná. V C++ je ještě více volnosti: Členské funkce, které jsou
deklarovány jako "virtuální" (jako např. funkce transmit v předchozím
příkladu) mohou být v podtřídě předefinovány, na rozdíl od jiných funkcí
(jako např. currentTime).

3.4.3.2. Jednoduchý polymorfismus

Pro třídy TelemetryData můžeme implementovat členskou funkci transmit takto:

void TelemetryData :: send (void) 
 // přenos (transmit) id
 // přenos (transmit)časové známky (timeStamp)
};

Všimněme si, že v deklaraci je explicitně uvedeno jméno třídy
TelemetryData. Ačkoliv tělo této členské funkce není kompletní (obsahuje
pouze komentáře požadovaných akcí) vyjadřuje skutečnost, že můžeme oddělit
rozhraní a implementaci metody. 
Stejně můžeme implementovat členskou funkci pro třídu ElectricalData jako:

void ElectricalData :: transmit () 
{
 TelemetryData :: transmit();
 // přenos napětí 
 // přenos proudu 
};

V implementaci této členské funkce se napřed vyvolá chování - funkce (nadtřídy) předka definované v nadřazené třídě a pak se toto chování rozšíří o přenos (zaslání) dat specializované třídy (napětí a proudy palivových článků).

 Předpokládejme, že máme dvě instance těchto tříd (i když to pro třídu
TelemetryData není typické).

TelemetryData telemetry;
ElectricalData electrical(5.0,-5.0,3.0,7.0);

Předpokládejme, že jim odpovídající objekty již byly vytvořeny. Mějme
následující nečlenskou funkci:

void TransmitFreshData(TelemetryData& d,const Time& t) 
{
  if (d.currentTime() >= t
    D.transmit();
};

Co se pak stane při provedení těchto dvou příkazů:

TransmitFreshData(telemetry, Time(60));
TransmitFreshData(electrical, Time(120));

V prvním případě se přenese pouze časová známka a identifikátor id, zatímco ve
druhém případě se kromě toho přenesou 4 další hodnoty typu float. Přitom implementace příkazu TransmitFreshData provede příkaz d.transmit() aniž by rozlišoval mezi různými d. Příčinou této skutečnosti je "polymorfismus". Polymorfismus je v teorii typů vlastnost, že jméno formálního parametru jako je d může označovat instance různých tříd, pokud mají společnou nadtřídu (předka). 

Konvenční jazyky mají funkce a procedury, jejichž operandy mají jedinečný
typ. Takovým jazykům se říká "monomorfické". (I v Pascalu jsou operace
write a read nemonomorfické - tedy polymorfické). Polymorfické jsou tedy
jazyky, u nichž operace nemusí pracovat s operandy (parametry) téhož
typu. V moderních jazycích se tomuto principu říká také "přetěžování"
(overloading).

Bez polymorfismu bychom v řadě případů museli řešit situaci rozsáhlou
strukturou case, která by řešila případ různých typů operandů. V Pascalu
bychom např.nemohli vytvořit hierarchii tříd různých druhů
telemetrických dat. Museli bychom vytvořit jeden, rozsáhlý záznam s
variantní částí, která by obsahovala různé typy dat. Pak by ekvivalent
procedury sendtelemetryData mohl vypadat takto:

const
 Electrical=1;
 Propulsion=2;
 Spectrometer=3;
 …
procedure Transmit_Fresh_Data(The_Data:Data,The_Time :Time);
begin
 if (The_Data.Current_Time >= The_Time) then 
   case The_Data.Kind of
     Electrical : SendElectricalData (TheData);
     Propulsion : SendPropulsionData (TheData);
 ...
   end; (* case*)
end;

Polymorfismus je nejužitečnější, když v něm mohou být třídy se stejným
protokolem. S polymorfismem se lze vyhnout velkým příkazům case, protože
každý objekt zná implicitně svůj vlastní typ. Dědičnost je možná i bez
polymorfismu, ale není pak dostatečně užitečná. To je typická pro Adu,
která je monomorfická.

Polymorfismus úzce souvisí s pozdní vazbou. Polymorfismus způsobuje, že
vazba metody s jejím jménem je určena až při výpočtu. V C++ (ale také v
Turbo Pascalu a TS Module-2) určuje programátor, zda metoda (členská
funkce) použije nebo nepoužije pozdní vazby.

3.4.3.3. Dědění a kontrola typů

Předpokládejme předefinování členské funkce "transmit".

void ElectricalData :: transmit (void) 
 TelemetryData::transmit();
 // transmit the FuelCell1Voltage and fuelCell2Voltage
 // transmit the FuelCell1Amperes and fuelCell2Amperes
};

Většina OOJ dovoluje, aby v se implementaci podtřídy volala metoda
definovaná v některé nadtřídě. Je to vidět v předcházejícím příkladě na
případě volání send. Ve Smalltalku nebo v Object Pascalu se to zajišťuje
s použitím klíčového slova "super" resp. "inherited". V těchto jazycích
se lze také odvolat na objekt, pro který je metoda vyvolána s použitím
klíčového slova "self". V C++ lze vyvolat metodu kteréhokoliv předka
tím, že se před jméno metody dá předpona třídy, jak je vidět na uvedeném
příkladě.

Tvůrce obvykle volá metodu nadtřídy před nebo po nějaké akci. Pak tedy
tělo podtřídy doplňuje chování metody nadtřídy. V jazyce CLOS se to
vyjadřuje explicitně tím, že se metoda deklaruje s kvalifikátorem
":before", ":after" nebo ":around". Metoda bez kvalifikátoru se považuje
za "primární" a hraje ústřední roli daného chování. Metody "before" a
"after" rozšiřují chování primární metody. Metoda "around" tvoří jakýsi
obal primární metody, která může být vyvolána kdekoliv zevnitř zvláštní
funkcí "call-next-metod".

Obr. 3-5 zde uveden podruhé

Na fig3-5.bmp jsou všechny podtřídy také podtypy své rodičovské třídy.
Instance ElectricalData jsou považovány stejně za podtypy jako za
podtřídy třídy TelemetryData. Skutečnost, že typy hrají souběžnou roli s
dědičností, jsou běžné u jazyků se silnou kontrolou typů jako je Object
Pascal a C++.

Vztah mezi typem a dědičností lze očekávat tam, kde se vytváří
zobecnění resp. specializace prostřednictvím dědičnosti. Předpokládejme
deklarace:

TelemetryData telemetry;
ElectricalData electrical(5.0, -5.0, 3.0, 7.0);
Následující přiřazení je korektní:

telemetry=electrical; 
//electrical je podtypem telemetry

Následující přiřazení je ilegální:
electrical = telemetry; 
// telemetry není podtypem electrical

Přiřazení je možné pouze tehdy, je-li objekt na levé i pravé straně téhož typu, nebo přiřazujeme-li instanci podtřídy na pravé straně instanci nadtřídy na levé straně. 
(Pozn. PodtřídA je vždy "širší". Při přiřazení musí být všechny složky levé straně "pokryty" složkami pravé strany.)

Většina silně typovaných jazyků dovolují konverzi typů pouze tehdy,
jsou-li mezi nimi vztah nadtřída/podtřída. V C++ proto existuje
přetypovací mechanismus (type cast). Object Pascal používá mechanismus
zvaný "type coercion". Tyto přetypovací mechanismy jsou čisté a typově
korektní; jsou kontrolovány na sémantickou korektnost v době kompilace.
Většinou se přetypování používá pro konverzi specializovaného typu na
více obecný (nadtřídní). Opačné konverze nejsou vyloučeny, nejsou ale
typově bezpečné a mohou se projevit nečekanou chybou až v době výpočtu.

Jako příklad ze uvést často používaný mechanismus seskupování
bezparametrických objektů do "pytle" (bag) nebo "sbírky" (collection).
Potřebujeme mít možnost seskupit instance objektů libovolných tříd a
proto takové struktury jako "pytel" nebo "sbírka" pracují nad nějakým
obecným typem TObject. Tvůrce pak potřebuje vložit do struktury nějaký
specializovanější podtyp bázové třídy. Pak iterační operace nad takovou
třídou umí pouze vrátit objekt dané struktury. Pro vyvolání operace
specifické pro daný podtyp musí tvůrce přetypovat "nadtřídu" na
"podtřídu". Tato operace však může selhat, objeví-li se ve struktuře
nějaký objekt nečekaného typu.

Na dědičnost lze nahlížet jako na soukromé rozhodnutí tvůrce
"znovupoužít" již vytvořený kus programu. Z jiného pohledu lze na
dědičnost nahlížet jako na mechanismus, který v obecné rovině
definuje objekty a dědičné objekty přejímají (poslouchají) chování
rodičovských tříd, přičemž toto chování specializují nebo rozšiřují. v
některých jazycích (C++) má tvůrce větší volnost v ovládání dědičnosti.
Je-li nadtřída "public", pak podtřídy jsou současně podtypy, protože
mají stejné rozhraní a mají tedy stejnou strukturu a chování. V
deklaraci je však možno definovat nadtřídu jako "private" a pak dědic
není podtypem nadtřídy. Prakticky to znamená, že pro nadtřídy "private"
se členské funkce typu "public" a "protected" stávají privátními členy
podtřídy.

3.4.3.4  Volání metody.
(fig3-6.bmp)

V tradičních jazycích je volání podprogramu (procedury/funkce) zcela
statické. To znamená, že při překladu, kompilátor může vygenerovat kód,
který bude realizovat příslušnou operaci. V jazycích, které podporují
nějakou formu polymorfismu (Smalltalk, Object Pascal, C++, CLOS) je
vyvolání dynamické, protože třída, se kterou má operace pracovat nemusí
být v době překladu známa. Situace se ještě komplikuje, zahrneme-li do
ni dědičnost. Dědičnost bez polymorfismu není složitější než statické
volání podprogramu. S polymorfismem se to však stává složitým problémem.
Předpokládejme hierarchii tříd z fig3-6 :
 DisplayItem (bázová třída)
 Circle Triangle Rectangle
 SolidRectangle (vyplněný  pravoúhelník – podtřída Rectangle)

 Pro třídu DisplayItem předpokládejme instanční proměnnou theCenter (danou
souřadnicemi X,Y), a operace:
 
draw (vykreslí obrazec)
move (posune obrazec)
location (vrací souřadnice umístěného obrazce)

Operace location je společná pro všechny obrazce.
Operace draw  se musí předefinovat pro každý nový obrazec. Protože třída DisplayItem je abstraktní, má její operace draw prázdnou implementaci (v terminologii C++ je to čistá virtuální funkce). 
Třída Circle zahrnuje instanční proměnnou theRadius a odpovídající
operace pro manipulaci s touto proměnnou. Podobně třída Rectangle bude
mít proměnné theHeight a theWidth. Podtřída SolidRectangle dědí
vlastnosti třídy Rectangle ale předefinuje operaci draw. Operace

Předpokládejme heterogenní  pole objektů typu DisplayItem. Každým prvkem seznamu může být libovolný potomek třídu DisplayItem.

DisplayItem* items[10];
 …
for (unsigned index=0; index<10; index++)
  items[index]->draw();

Pak metoda draw musí mít polymorfické chování. Při kompilaci tohoto úseku nemůže kompilátor vyřešit problém, která metoda draw bude vyvolána v době kompilace. Tento problém se řeší následujícím způsobem:

V takovém případě kompilátor nemůže dopředu přeložit odpovídající kód operace, protože nemůže být známa dříve, než v době výpočtu.
Když klient vyšle zprávu Draw do objektu seznamu, pak se
provedou následující akce:

•	Objekt vyhledá zprávu ve svém seznamu zpráv třídy
•	Najde-li tuto zprávu, vyvolá odpovídající kód, který je definován lokálně v daném objektu
•	Nenajde-li v seznamu zprávu, jde o třídu výše a hledá zprávu v seznamu nadtřídy objektu

Tento proces se opakuje, dokud se zpráva nenajde nebo dokud se
neprohledá nejvyšší třída. Když se zpráva nenajde ani v nejvyšší třídě,
vrátí např. Smalltalk chybovou zprávu doesNotUnderstand.

Klíčovým nástrojem je seznam zpráv, který je součástí representace třídy
a je ukryt před klientem. Tento seznam se vytváří v době vytvoření třídy
a obsahuje všechny metody této třídy. Prohledávání seznamu spotřebuje
určitý čas. Vyhledání metody v seznamu je ve Smalltalku asi 1.5 krát
delší než prosté volání podprogramu. Většina implementací Smalltalku se
snaží tento proces optimalizovat použitím cash-seznamu, takže často
vyvolávané metody mohou být zpracovány rychle. Cash-princip zlepší výkon
o 20-30%.

Operace draw definovaná v podtřídě SolidRectangle má zvláštní postavení. Řekli jsme, že draw napřed vyvolá tuto metodu v nadtřídě Rectangle. Ve Smalltalku se metoda nadtřídy specifikuje s použitím klíčového slova "super". Smalltalk použije stejného vyhledávacího mechanismu, ale v nadřazené třídě. 

Některé studie (Deutsch) tvrdí, že polymorfizmus není zapotřebí v 85% případů a že lze využít prostého volání procedur. Duff říká, že v takových případech návrhář implicitně zvolí "včasnou vazbu" metod objektů. Některé jazyky bez typové kontroly (Smaltalk), však neumožňují podobnou volbu. 
V C++ nebo v Object Pascalu si může tvůrce zvolit způsob vazby. Protože
je žádoucí vyhnout se vyhledávání metody, ale při tom zachovat
polymorfismus, je metoda volání jiná než ve Smalltalku. Volbou metody
jako virtuální, je automaticky dána pozdní vazba. Ostatní metody jsou statické a
mají vazbu včasnou. V našem příkladu můžeme metodu draw definovat jako
virtuální a metodu Location jako statickou metodu, protože nemusí být předefinována. Tvůrce může také zvolit režim metody jako "inline", což znamená, že místo volání podprogramu se provede expanze kódu ve stylu makropříkazu. Není to ovšem zadarmo - úspora času se platí prostorem.

Pro práci s virtuálními metodami vytváří většina implementací C++
tabulku virtuálních metod, která se vytvoří pro každý objekt. Tabulka
většinou sestává ze seznamu ukazatelů na těla procedur virtuálních
metod. Vytvoříme-li např. objekt třídy Rectangle, pak tabulka
virtuálních metod bude obsahovat pro každou virtuální metodu položku,
která bude ukazovat na nejbližší implementaci metody draw.
Když bude mít třída DisplayItem funkci Rotate, která není předefinovaná v třídě rectangle, pak bude položka draw objektu Rectangle ukazovat na
implementaci Rotate do třídy DisplayItem. Tímto způsobem je vyhledávání nahrazeno nepřímým odkazem přes ukazatel.

Operace draw pro třídu SolidRectangle má v C++ zvláštnost. Aby se
přinutilo vyvolat operaci draw v nadtřídě napíše se:

Rectangle::Draw();

Stroustrupovy studie říkají, že virtuální metody jsou prakticky stejně
účinné jako statické. V případě jednoduché dědičnosti vyžadují oproti
volání podprogramu pouze tři nebo čtyři paměťové odkazy navíc.

3.4.5.6. Vícenásobná dědičnost
Jednoduchá dědičnost znamená, že dědická podtřída je odvozena právě od
jediné nadtřídy. Někdy se ale stává, že tvůrce je nucen zvolit jednu ze
dvou stejně užitečných nadtříd. Vícenásobnou dědičnost podporuje C++,
v omezené míře Smalltalk, a některé jiné jazyky. potřeba a použití
vícenásobné dědičnosti je však stále předmětem odborných diskusí. Podle
zkušeností autora, lze vícenásobnou dědičnost pohlížet asi jako padák: "Většinou ho nepotřebujeme, ale když je zle, jsme moc šťastní, že ho máme."

Na fig3-7.bmp je uveden příklad vícenásobné dědičnosti. Z anglické terminologie v bankovnictví:
	asset  				aktivum
	interest-bearing		úročený
	insurable			pojistný
	account			konto
	real estate			nemovitost
	security			m.j. sociální zabezpečení, pojištění
	bond				závazek, hypotéka
	stock				akcie

Předpokládejme následující bázové třídy:

class Asset …
class InsurableItem …
class InterestBearingItem …

Předpokládejme dále mezitřídy, z nichž každá má vícenásobné nadtřídy:

class BankAccount: 	public Asset,
					public InsurableItem,
					public InterestBearingItem…
class RealEstate:		public Asset,
					public InsurableItem…
class Security:		public Asset,
					public InterestBearingItem…

a mějme pak koncové třídy:

class SavingsAccount :	public BankAccount…
class CheckingAccount:	public BankAccount…

class Stock:	public Security…
class Bond: public Security… 

Návrh struktury s vícenásobnou dědičností není snadná věc. Většinou se
navrhuje v inkrementálním a iteračním procesu, jak bude vysvětleno v kap. 4.
S vícenásobnou dědičností jsou spojeny dva problémy: 
•	kolise identifikátorů zděděných z různých tříd a řešení opakované dědičnosti.
Ke kolisi identifikátorů dochází může dojít např. v případě tříd InsurableItem a Asset které obě mají atributy se shodným jménem presentValue. Protože třída RealEstate dědí od obou těchto tříd, znamená to, že zdědí dvě operace se stejnými jmény.  Obecně existují tři způsoby řešení takové situace.

(a)	Sémantika jazyka kolisi nepřipouští a kompilace hlásí chybu. To je
případ jazyků jako je Smalltalk a Eiffel. Řešením je přejmenování jedné
ze složek.

(b) Sémantika může považovat stejné jméno zavedené ze dvou tříd jako
jméno složky té třídy, ke které má reference přístup.

c) Sémantika připouští použití stejných jmen a kolisi zabrání plnou
kvalifikací zdroje složky. Tak tomu je v C++. 

K opakované dědičnosti dojde např. tehdy, když od třídy A jsou dědičně odvozeny dvě podtřídy B a C a třída D vznikne vícenásobnou dědičností odvozením od třídy B i C. Vlastnosti nadtřídy A se pak v podtřídě D vyskytnou dvakrát.

Představme si podtřídu :

class MutualFind: 	public Stock,
					public Bond …

Tato třída vnese opakovanou dědičnost z třídy Security, která je nadtřídou třídy Stock i Bond.

Existují opět tři způsoby řešení této situace:

a) Opakovaná dědičnost je zakázaná. Tak to používá např. Smalltalk a
Eiffel.

b) Duplikace složek je povolena, ale požaduje se plně kvalifikované
identifikátory složek konkrétní instance. Takto pracuje C++.

c) Vícenásobné reference na stejnou třídu se chápou jako reference na
stejnou třídu. Takto pracuje C++ v případě, že opakovaná nadtřída je
vnesena jako "virtuální bázová třída". Virtuální bázová třída je třída,
kterou podtřída označí jako virtuální nadtřídu, čímž indikuje, že jde o
sdílenou třídu. Podobným způsobem pracuje CLOS, který používá
mechanismus označovaný jako "seznam třídových precedencí". Tento seznam,
který se sestaví při zavedení každé nové třídy, zahrne nadtřídy bez
duplikací s použitím následujících pravidel:
•	Třída má vyšší precedenci nad svou podtřídou
•	Každá třída nastavuje precedenční pořadí všech svých přímých nadtříd.

S tímto přístupem se graf dědičnosti zjednoduší, všechny duplicity jsou
odstraněny a výsledná hierarchie se řeší jako by měla jednoduchou
dědičnost. Při zpracování seznamu se může dojít k cyklickým dědičnostem.
Takové objekty jsou pak vyloučeny.

Existence vícenásobné dědičnosti dala vzniknout speciální programátorský
styl tříd, zvaných "mixins". Tento styl vznikl v prostředí jazyka
"Flavors". Mixins jsou malé třídy, kterých se používá k budování tříd s
větší složitostí chování. "Mixin" je syntakticky stejná jako třída, ale
má jiný účel. Používá se k tomu, aby přidala "chuť" (flavor) jiné,
z řady "mixinů" odvozované třídě. Mixin nikdy nemá svou vlastní
instanci, ale je jako "koření", které se přidává do jiných užitných
tříd. Nikdy se nevyskytují samy o sobě. Třídě, které se skládá pouze z
"ingrediencí" a nepřidává nové vlastnosti na své definiční úrovni, se
říká "agregovaná třída".

3.4.5.6. Vícenásobný polymorfismus

Předpokládejme členskou funkci deklarovanou pro třídu DisplayItem:

virtual void draw();

Účelem této funkce je vykreslit obrázek daného objektu. Je deklarována jako virtuální a tudíž polymorfická.  Předpokládáme, že každá podtřída poskytne metody pro vykreslení svého obrázku. V tomto případě se polymorfismus projeví tím, že při vyvolání metody pro daný objekt, vykreslí jemu odpovídající obrázek. To je ovšem příklad jednoduchého polymorfismu.

Předpokládejme, že bychom chtěli takové chování, kdy by se v jednom
|případě vykreslil obrázek ve vysoké grafické kvalitě a ve druhém případě obrázek v hrubé nízké grafické kvalitě. Samozřejmě, že bychom mohli vytvořit dvě různé funkce, ale to se nezdá být vhodné pro zbytečnou redundanci.

V jazyce CLOS lze napsat metody nazvané "multimetody", které mají více
než jeden polymorfně se projevující parametr. 

V jazycích, které nepodporují vícenásobná polymorfismus, jako C++, lze podobnou vlastnost předstírat akcí nazvanou "double dispatching".

Napřed musíme definovat hierarchii displejových zařízení, které má kořen ve třídě DisplayDevice. Pak můžeme přepsat operaci DisplayItem.

virtual void draw(DisplayDevice&();

V implementaci této metody vyvoláme kreslicí operace, které jsou polymorfní daným skutečným parametrem DisplayDevice a to pak projeví polymorfní chování založené na skutečné podtřídě parametru DisplayDevice .

3.4.6. Agregace

Vztah agregace mezi třídami má paralelu ve vztahu agregace objektů těchto odpovídajících tříd. Předpokládejme následující deklaraci třídy TemperatureController.

class TemperatureController {
public:
  TemperatureController(Location);
  ~TemperatureController();

  void process(const TemperatureRamp&);
  Minute schedule(const TemperatureRamp&) const;

private:
  Heater(h);
};

Na fig3-8.bmp představuje třída TemperatureController celek a instance třídy Heater část.  Odpovídá to vztahu agregace znázorněném na fig.3-3.bmp. 

Fyzické obsažení

V případě třídy TemperatureController jde o fyzické obsažení "hodnotou", protože objekt Heater neexistuje nezávisle na jeho obklopující instanci třídy TemperatureController. To souvisí i s dobou života těchto entit. Vytváříme-li instanci TemperatureControler, vytváříme automaticky i instanci Heater. Podobně je tomu při destrukci.

Méně přímou agregací je obsažení "referencí" (odkazem). Privátní část třídy TemperatureController můžeme např. nahradit deklarací:

Heater* h;

I v tomto případě jde o celek a část, ale k části se přistupuje nepřímo – odkazem. Existence obou entit není na sobě závislá, jako v předcházejícím případě.
Agregace vnáší vztah "jeden ku mnoha", např. Heater je součástí TemperatureController a ne naopak. 

Velmi často se stává, že vícenásobná dědičnost je zaměňována za agregaci. V C++ je možné protected nebo private dědičnost nahradit protected nebo private agregací instancí nadtřídy, bez ztráty sémantiky.  Užitečnou pomůckou je test "je částí čeho", který aplikujeme na dvě třídy a je-li úspěšný, pak je agregace nebo jiný vztah vhodnější než dědičnost.

3.4.7.  Užití

Na příkladu tříd rampController a growingRamp bylo ilustrováno spojení dvou objketů vztahem užití.

class TemperatureController {
public:
  
  TemperatureController(Location);
  ~TemperatureController();

  void process(const TemperatureRamp&);
  Minute schedule(const TemperatureRamp&) const;

private
  Heater h;
};

Třída TemperatureRamp je součástí zápisu některých členských funkcí a tak lze říci, že TemperatureController používá třídu TemperatureRamp.

Zákazníci a dodavatelé (klienti a dodavatelé)

Asociace vyjadřuje rovnocenný (obousměrný) vztah  mezi dvěma entitami (rovný s rovným – angl. "peer to peer"), zatím co vztah užití vyjadřuje jednosměrný vztah mezi klientem (zákazníkem) a dodavatelem (serverem). Jedna třída může použít druhou mnoha různými způsoby. Např. TemperatureController používá TemperatureRamp v popisu rozhraní.  Typicky se vztah "užití" se projevuje jako implementace určité operace deklarující lokální objekt použité třídy. Skutečný vztah užití je někdy omezující, protože dovoluje klientův přístup pouze do sekce "public" svého dodavatele. Někdy je třeba tento princip uvolnit. Příkladem toho je účel principu "friend" v jazyku C++.

3.4.8 Práce s instancemi

Dříve uvedená deklarace ADT fronta nebyla dostatečně typově bezpečná. Frontu můžeme vylepšit zavedením její generičnosti:

template<class Item>
class Queue {
public:

  Queue();
  Queue(const Queue<Item>&);
  virtual ~Queue();

  virtual Queue<Item>& operator=(const Queue<Item>&);
  virtual int operator==(const<Item>&) const;
  int operator!=(const Queue<Item>&) const;

  virtual void clear();
  virtual void append(const Item&);
  virtual void pop();
  virtual void remove(int at);

  virtual int length() const;
  virtual int isEmpty() const;
  virtual const Item& front() const;
  virtual int location(const void*);

protected:
  …
};

(fig3-10.bmp)

V uvedené deklaraci se už s prvky fronty nepracuje prostřednictvím void*, což není bezpečné, ale prostřednictvím třídy Item, která je deklarovaná jako parametr "template".

Parametrizovaná třída nemůže mít instance, dokud do ní instance nedosadíme. Můžeme např. deklarovat dva různé objekt typu Queue deklarací:

Queue<int> intQueue;
Queue<DisplayItem*> itemQueue;

Tyto postupy jsou typově bezpečné, protože odmítnout jakékoli pokusy vložit či vybrat z první fronty prvek typu integer a z druhé instance třídy DisplayItem nebo z jejich podtříd.  Obrázek fig3-10.bmp  se snaží znázornit vztah mezi parametrizovanou třídou Queue a odpovídající instancí Item.

Generičnost

Existují čtyři základní způsoby jak vytvářet parametrickou třídu Queue.
•	Prvním způsobem je použití makroinstrukcí. Stroustrup poznamenává, že tento způsob je vhodný jen pro malé programy.
•	Druhým způsobem je metoda použitá ve Smalltalku, využívající dědičnost a pozdní vazbu. Tímto způsobem lze budovat heterogenní kontejner tříd. S každým prvkem lze pracovat jako s instancí jiné bázové třídy.
•	Třetí způsob používá přístup Object Pascalu, který je silně typovaný, podporuje dědičnost ale nepodporuje formu parametrizovaných tříd. I zde vybudujeme kontejner, ale homogennost mu vnutíme v okamžiku vytvoření. Tento přístup vede ke zvětšení kódu a prodloužení programu vr fázi výpočtu.
•	
•	
•	Čtvrtým způsobem je způsob, uvedený v předcházejícím odstavci. Parametrizovaná třída – "generická třída" – je šablona, která se parametrizuje jinými třídami, objekty nebo operacemi. Do parametrizované třídy se musí dosadit instance dříve, než ze vytvoří objekt této třídy. C++ a Eifel podporují tento přístup.

3.4.9 Metatřída

Až doposavad jsme třídu chápali jako množinu instancí. Metatřída je taková třída, jejímiž prvky jsou opět třídy. Matatřída je tedy třída tříd.

3.4.5. Metatřídové vztahy
=========================
Doposavad jsme uvedli tři typy vztahů: vztahy dědičnosti, použití a
instanční. Až doposavad jsme vycházeli z předpokladu, že objekt jako
takový je instancí nějaké třídy. Co kdybychom rozhodli, že budeme
zacházet s třídou jako s takovou podobným způsobem jako s objektem. Pak
musíme zavést pojem "třída tříd" a to je to, co nazýváme "metatřída".
Metatřída je tedy třída, jejímiž instancemi jsou třídy. 

Robson poznamenává, že v systému, který se vyvíjí, poskytují třídy rozhraní
mezi programátorem a definicí objektů. Z toho pohledu by bylo velmi
užitečné, kdybychom mohli s třídami zacházet podobným způsobem jako s
objekty. Metatřídy podporuje Smalltalk a CLOS a v CLOS jsou mocnějším
nástrojem než ve Smalltalku. Smalltalk zavedl dobrý zvyk, že v každé
metatřídě je ukázka příkladu, jak se metatřídy používá.

I když v C++ není explicitní podpora metatříd, existuje možnost třídové
proměnné. Vytvoří se statický objekt, což znamená, že všechny dědické
instance jej sdílejí.

3.5. Vzájemná souhra tříd a objektů.
3.5.1. Vztahy mezi třídami a objekty

Třídy a objekty jsou dva oddělené pojmy, které však spolu úzce souvisí.
Každý objekt je instancí nějaké třídy a každá třída má žádnou nebo více
instancí. Z praktických důvodů jsou ve většině aplikací třídy statické;
jejich existence, sémantika a vztahy jsou fixovány před spuštěním
programu. Podobně, třídní příslušnost většiny objektů je statická, tedy,
jakmile je objekt vytvořen, jeho třídní příslušnost je pevná. Na rozdíl
od tříd, objekty jsou v průběhu chodu programu vytvářeny a rušeny.
Představme si letecký provoz z pohledu tříd a objektů. Mezi
nejdůležitější abstrakce patří letadla, letové plány, letištní dráhy, a letový
prostor. Ty jsou statické, protože jinak nelze postavit předpoklady jako
že letoun napřed odstartuje, pak letí a potom přistane, a že dva letouny
nemohou zaujímat stejný prostor ve stejném okamžiku. Naopak, objekty
jsou dynamické. Tu a tam se postaví nová letištní dráha a stará se zruší.
Vytvoří se nové letové řády a staré se zruší. Neustále další letadla
zaujímají a opouštějí letový prostor.

3.5.2. Úloha tříd a objektů v návrhu

V průběhu analýzy a prvních etap návrhu má tvůrce dva hlavní úkoly:
•	Identifikovat třídy a objekty, které tvoří slovník problémové domény
•	Vymyslet strukturu, v níž množiny objektů spolupracují tak, aby výsledné chování splňovalo zadání problému.

Úhrnem nazýváme tyto objekty a třídy "klíčovými abstrakcemi" problému a
celou spolupracující strukturu označujeme jako "mechanismus" implementace.

V průběhu této fáze návrhu se tvůrce soustřeďuje na vnější chování
klíčových abstrakcí a mechanismu. Tento pohled vytváří logický rámec
systému a zahrnuje i strukturu tříd a objektů. Na jejich implementaci se
zaměřuje až v další fázi návrhu, kdy se zaměřuje na vnitřní pohled na
třídy a objekty, včetně jejich fyzické representace. Tato návrhová
rozhodnutí nazýváme částí systémové modulární architektury a procesní
architektury.

3.6. Kvalita výstavby tříd a objektů

3.6.1. Míra kvality abstrakce

Ingalls navrhuje, že "systém má být postaven z minimálního množství
neměnných částí; tyto části mají být co nejobecnější a všechny části
mají být v daném rámci jednotné". V OOP jsou těmito částmi třídy a
objekty, které vytvářejí klíčové abstrakce a rámec je dán mechanismem.

Podle zkušeností je tvorba tříd postupným, inkrementální procesem. S
výjimkou triviálních řešení, nelze definovat všechny třídy napoprvé.
Samozřejmě, že postupné vylepšování něco stojí, ať již čas.
srozumitelnost, integritu atd. Toto je snaha přiblížit se co nejdřív
správnému řešení. Jak se pozná, že daná třída nebo objekt je dobře
navržen? Navrhuje se několik hodnotitelných vlastností:

•	Spřaženost (Coupling)
•	Soudržnost (Cohesion)
•	Dostatečnost (Sufficiency)
•	Úplnost (Completness)
•	Primitivnost (Primitiveness)

Spřaženost je pojem převzatý ze strukturního návrhu, a s poněkud
volnějším významem lze používá i pro OO přístup. Stevens, Myers a
Constantine definují spřaženost, jako sílu vazeb ustavených propojením
jednoho modulu s druhým. Silná spřaženost komplikuje porozumění i
údržbu systému. Složitost může být snížena snížením spřaženosti na co
nejmenší míru. Jako příklad nesprávného spřažení je modulární systém
stereopřijímače, v němž je napájecí zdroj umístěn v jedné z
reprodukčních skříní.

Spřaženost ve vztahu k modulární struktuře je použitelná i pro OO
strukturu a spřažení vztahu tříd a objektů je stejně důležité. Existuje
však určité napětí mezi principem spřažení a dědičností. Na jedné straně
požadujeme slabě spřažené třídy, na druhé straně dědičnost vytváří
těsnou vazbu.

Také princip soudržnosti vychází ze strukturního návrhu. Jednoduše
řečeno, soudržnost vyjadřuje míru propojení mezi jednotlivými prvky
jednoho modulu. V OO návrhu se tento projev vztahuje na třídy a objekty.
Nejméně žádoucí soudržnost je koincidenční soudržnost, která je projevem
toho, že součástí stejné třídy jsou abstrakce, které spolu jinak než
umístěním vůbec nesouvisí. Třída zahrnující psy a letadla by se asi
nevyznačovala dobrou soudržností. Nejlepší formou soudržnosti je
funkční soudržnost, při níž všechny elementy třídy nebo modulu vzájemně
spolupracují při zajišťování určitého úzce svázaného chování. A tak
třída "pes" by měla zahrnovat chování psa, všechno co dělá pouze pes,
nic víc a nic méně.

Dostatečností se rozumí vlastnost třídy, která obsahuje všechny
podstatné rysy chování zvolené abstrakce tak, aby byla smysluplně využitelná.
Jestli vytváříme např. abstrakci "množina", měla by obsahovat všechny
důležité operace, které pro množinu potřebujeme , např. přidání nového
prvku do množiny, zrušení existujícího prvku atd. Ostatně porušení
dostatečnosti se projeví obvykle velmi brzy.

Úplností se rozumí rozhraní, které zahrnuje všechny důležité vlastnosti
abstrakce. Zatím co dostatečnost vede k minimálnímu rozhraní, úplné
rozhraní je mnohem širší. Úplná třída má rozhraní, které je obecně
využitelné kterýmkoli klientem. Úplnost je subjektivní faktor; může
zahrnovat i řadu vlastností, které se nikdy nevyužijí.

Primitivní operace jsou operace, které lze účinně implementovat pouze s
přístupem k vnitřní representaci abstrakce. Např. přidání nového prvku
do množiny je primitivní operace. Přidání čtyřech prvků do množiny (za
předpokladu typičnosti použití takové operace) není primitivní funkcí;
je to vyšší operace, kterou lze vytvořit s použitím primitivní operace.
Ve skutečnosti i "účinnost" implementace je subjektivní faktor.
Primitivní operací je bez jakýchkoli pochybností operace, která nelze
implementovat jinak, než s přístupem k vnitřní representaci. Kandidátem
na primitivní operaci je však i operace, která sice lze postavit z
jiných primitivních operací, ale výsledkem by byla časově nebo
prostorově neúměrně složitá operace.

3.6.2. Volba operací

3.6.2.1. Vlastnosti funkční sémantiky

Vytváření rozhraní třídy nebo modulu je vcelku nezáživná a tvrdá práce.
Většinou se snažíme udržovat všechny operace na primitivní úrovni, kdy
každá provádí malou a jasně definovanou činnost. Takové metody
označujeme jako "jemnozrnné". V rozhraní oddělujeme metody, které
vzájemně nekomunikují. Tímto způsobem je mnohem jednodušší vytvořit
podtřídu, která předefinuje chování své nadtřídy. Rozhodnutí o
jedné metodě proti variantě několika, může mít dva protichůdné projevy:
sloučení několika projevů do jedné metodě vede k jednoduššímu rozhraní
se složitějšími metodami; rozptýlení projevů do mnoha metod vede ke
složitějšímu rozhraní s jednoduššími metodami.

V OO návrhu je obvyklé, že se metody tříd navrhují v celku, protože
tyto metody vzájemně spolupracují a vytvářejí celkový protokol
abstrakce. Máme-li určité chování, musíme rozhodnout do které třídy ho
umístíme. Halbert a O'Brien doporučují pro toto rozhodnutí následující
kriteria.
•	Znovupoužitelnost - Bude toto chování užitečné v širším  kontextu než v jediném daném?
•	Složitost - Jak složitá je implementace chování?
•	Aplikovatelnost - Nakolik je chování odpovídající typu, v němž je umístěno?
•	Znalost implementace - Závisí chování implementace na přístupu k vnitřním detailům typu?

Obvykle se používané operace soustřeďují do rozhraní tříd. V jazycích
jako Object Pascal, C++, CLOS a Ada se mohou takové operace deklarovat
jako "volné podprogramy" (free subprograms) a soustředit se ve třídě
pomocných programů. V terminologii C++ je volným podprogramem "nečlenská funkce". Protože však tyto podprogramy nemohou být předefinovány, mají nižší stupeň obecnosti.

3.6.2.2. Vlastnosti časoprostorové sémantiky

Jakmile jsme rozhodli o funkční sémantice, musíme uvažovat o
časoprostorové sémantice. To znamená úvahy o čase a paměťovém prostoru,
který operace vyžadují ke svému provedení. Tyto úvahy se nejčastěji
vedou v pojmech "nejlepší", "průměrný" a "nejhorší" případ.

S časovou a prostorovou složitostí úzce souvisí synchronizace. V řadě
jazyků je tento problém vyřešen triviálně, protože se jedná o
jednoprocesní, sekvenční zpracování. Předávání zpráv v takových
situacích je jednoduché a představuje prosté volání podprogramů. V
jazycích jako je Ada, Modula-2 se podporuje paralelní (concurrent)
zpracování, kdy dva procesy se ucházejí o řízení nad jedním objektem.
V takovém případě je synchronizace složitější.

Zdá se být užitečným vyjádřit paralelní sémantiku pro každou
jednotlivou operaci a pro objekt jako celek, protože různé operace
vyžadují různou synchronizaci. Zasílání zpráv pak mít některou z
následujících forem:

•	Synchronní - Operace nastane pouze tehdy, když odesilatel (synchronous) zahájí akci a příjemce je připraven zprávu přijmout; odesilatel a příjemce čekají bez omezení  dokud oba partneři nejsou připraveni k akci
•	"Balking" – Pozastavené - Chování stejné jako synchronní, ale v případě,  že příjemce není připraven, odesilatel zprávu nevyšle a opustí ji.
•	"Timeout" -  Chování stejné jako synchronní, ale odesilatel čeká  jen po vymezenou dobu jestli bude příjemce připraven
•	"Asynchronní" - Odesilatel může zahájit akci bez ohledu na to, zda je příjemce připraven k přijetí

Forma zasílání zpráv musí být vybrána na základě funkční sémantiky.

3.6.3.  Volba vztahů

3.6.3.1. Spolupráce

S volbou operací souvisí také volba vztahu. Jestliže rozhodneme, že
objekt Y zašle zprávu M objektu X, pak musí být objekt X viditelný z
třídy Y, abychom mohli pojmenovat operaci M v těle třídy X a objekt Y
musí být viditelný v X jinak by X bylo v Y neznámé. Abstrakce jsou
vzájemně viditelné pouze tehdy, když se rozsah jejich platnosti (scope)
vzájemně překrývá. V tom případě je spřaženost (coupling) mírou
viditelnosti.

Jedním z užitečných návodů na volbu vztahů mezi objekty se nazývá
"Demeterův zákon", který říká: "Metody třídy nesmí být závislé na
struktuře jiné třídy, z výjimkou bezprostředně vyšší struktury vlastní
třídy. Každá metoda by měla zasílat zprávy objektům patřícím k velmi
omezené množině tříd". Výsledkem aplikace tohoto zákona je tvorba volně
spřažených tříd, jejichž implementace je zapouzdřena a neviditelná.

Díváme-li se na strukturu z pohledu celého systému, zjišťujeme, že
hierarchie dědičnosti je buď široká a mělká, úzká a hluboká nebo
vyvážená. Struktury tříd, které jsou široké a mělké vytvářejí nejčastěji
"les" volných tříd, které jsou volně seskupeny. Struktury tříd které
jsou úzké a hluboké vytvářejí "strom" tříd, které mají vztah ke
společnému předchůdci. Každý přístup má své výhody a nevýhody. Lesy jsou
volněji spřaženy, ale nemají výhodu "společných" vlastností. Stromy mohou
využívat společných vlastností, takže jednotlivé třídy jsou menší než v
"lese". Porozumění jedné třídě však závisí na porozumění všem třídám, od
kterých byly vlastnosti zděděny. Je to situace, o které se již
hovořilo. 

Má-li se rozhodnout o třídě "Car" a jejím vztahu ke třídě "Wheel" a "Engine", pak půjde spíše o užití než o dědičnost. Mayer říká, že mezi třídami A a B je odpovídající dědičnost tehdy, když na každou instanci třídy B můžeme nahlížet jako na instanci třída A. Vztah klienta (užití) je odpovídající tehdy, když každá instance třída B používá jednu nebo více vlastností třídy A. Je-li chování objektu více než úhrn svých jednotlivých částí, pak je vytvoření takového objektu s použitím vztahu klienta je správnější než z pohledu dědičnosti.

3.6.3.2. Mechanismus a viditelnost

Při rozhodování o volbě vztahů hraje významnou roli vzájemná interakce.
Tvůrce si musí často položit otázku "kam co jde". Představme si výrobní
jednotku, v níž jednotky materiálu (lots) vstupují do výrobních buněk k
dalšímu zpracování. Protože materiál vstupuje do určité buňky, musíme
žádat umisťovací mechanismus o příslušnou akci. Nyní se musí tvůrce
rozhodnout: je umisťovací operace operací nad místem, nad jednotkou
materiálu nebo nad oběma. V první případě musí být místo viditelné z
jednotky materiálu, ve druhém případě musí být jednotka materiálu
viditelná z místa, protože materiál musí vědět, do kterého místa jde. V
posledním případě jde o obousměrnou viditelnost. Musíme také rozhodnout
o viditelnost mezi umisťovacím mechanismem na jedné straně a místem a
materiálem na straně druhé.

V průběhu návrhu bývá užitečné explicitně stanovit, kdo je viditelný komu. Jsou čtyři základní způsoby, jak je objekt X viditelný objektu Y.

•	Objekt dodavatele je vůči klientu globální
•	Objekt dodavatele je parametrem operace klienta
•	Objekt dodavatele je součástí objektu klienta
•	Objekt dodavatele je lokálně deklarován v rozsahu platnosti diagramu objektu

Variantou na všechny možnosti je sdílená viditelnost. Objekt Y může být např. částí objektu X, ale může být viditelný jiným objektům jiným způsobem.

3.6.4.  Volba implementace

Když uzavřeme fázi návrhu z vnějšího pohledu, začneme se zabývat vnitřní
výstavbou tříd a objektů. To zahrnuje obvykle dvě různá návrhová
rozhodnutí.

3.6.4.1 Volba representace třídy nebo objektu

Representace bývá často utajena zapouzdřením abstrakce. To umožňuje její
změnu, aniž by se porušily předpoklady klientů o chování abstrakce.
Wirth říká : "Volba representace je dosti obtížná záležitost a není
jednoznačně určena dostupným zařízením. Obvykle se řídíme operacemi,
které se provádějí nad daty". Mějme např. objekt, jehož částí je
informace o letovém plánu. Budeme representovat plán pro optimalizaci
vyhledávací operace nebo pro optimalizace vložení a zrušení položky?
Protože nelze optimalizovat oba požadavky, musíme zvážit, který z nich
je kritičtější. Někdy to dopadne tak, že končíme rodinami tříd, jejichž
rozhraní jsou prakticky identické ale jejichž implementace jsou radikálně
odlišné.

Jedním z obtížných rozhodnutí bývá mezi tím, zda hodnota má být stav
objektu nebo jeho složka (field). Mějme např. objekt "Kužel" a jeho
metodu "Obsah". Metoda vrací obsah kužele. Součástí kužele jsou složky
"výška" a "poloměr". Máme zavést složku "obsah" nebo se vždy znovu
vypočítá? Chceme-li aby metoda obsah byla rychlá, pak pro ni vytvoříme
složku. Je-li prostor kritický, pak hodnotu obsahu vždy znovu vypočítáme. Ať se rozhodneme jakkoli, zapouzdření nám dovolí změnit implementaci, aniž by se to jakkoli týkalo klientů této třídy.

3.6.4.2 Volba umístění třídy nebo objektů v modulu

Podobné úvahy lze vést o umístění tříd a objektů v modulu. Ve Smalltalku
nejsou problémy, protože nezná pojem "modul". Object Pascal, C++, CLOS ,
Ada a jiné jazyky však podporují modul jako samostatnou jazykovou
konstrukci. Naše rozhodnutí jsou řízena obvykle protichůdnými požadavky
na viditelnost a ukrývání informace, které ovlivňují deklarace tříd a
objektů. Rozhodnutí jsou také ovlivněna netechnickými faktory jako je
znovupoužití, bezpečnost a integrita systému, dokumentace apod. Parnas,
Clemens a Weiss poznamenali na účet ukrývání informace: "Není vždy
snadné uplatnit tento princip. Je veden snahou minimalizovat náklady na
software po dobu jeho použití a je nutné, aby tvůrce odhadl
pravděpodobnost změn. Takový odhad vyžaduje dřívější zkušenost a znalost
aplikační oblasti a použité softwarové a hardwarové technologie.

3.7. Souhrn kapitoly

•	Objekt má stav, chování a identitu
•	Struktura a chování podobných objektů je definovaná společnou třídou
•	Stav objektu obvykle zahrnuje všechny (obvykle statické) vlastnosti objektu a běžné (obvykle dynamické) hodnoty všech těchto vlastností.
•	Chování je definováno jako akce a reakce promítající se ve změnách stavu a v zasílání zpráv.
•	Identita je vlastnost objektu, kterou se objekt odlišuje od jiných objektů
•	Dva druhy objektové hierarchie zahrnují vztahy užití a obsažení
•	Třída je množina objektů, které mají společnou strukturu a chování
•	Čtyři druhy hierarchie tříd jsou: dědičnost, užití, vztah instancí a vztahy metatříd
•	Klíčové abstrakce jsou třídy a objekty, které vytvářejí slovník problémové domény.
•	Mechanismus je struktura která umožňuje aby množina objektů pracovala dohromady pro zajištění požadovaného chování problému
•	Mírou kvality abstrakce je spřaženost, soudržnost, dostatečnost,úplnost a primitivnost.