NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s podporou uplatnění od 0 Kč. Více informací.

Diskuze: OOP - Rozdělení proměnných třídy do "bloků" a zapouzdření

V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Štěpán Wünsch:16.2.2016 18:23

Ahoj, začínám programovat v C# (prošel jsem základní konstrokuce a oop tady na ITNetworku)
Nyní se pokouším udělat RPG postavu s opravdu velkým množstvím proměnných. Rád bych je rozdělil do samotných bloků na Vlastnosti, Schopnosti a Dovednosti.
Obrázek řekne víc než tisíc slov

Takže chci mít třídu Postava a v ní třídy Vlastnosti, Schopnosti a Dovednosti.

// Část že třídy Postava
public Vlastnosti Vlastnosti { get; private set; }
public Schopnosti Schopnosti { get; private set; }
public Dovednosti Dovednosti { get; private set; }

Pak mohu krásně přistupovat k jednotlivým složkám

// Mimo třídu
Postava postava = new Postava();
int drevcove = postava.Dovednosti.DrevcoveZbrane;

Problém je samozřejmě v zapouzdření. Chci aby jen postava mohla měnit svoje dovednosti a zde je kámen úrazu. Jaký je nejlepší způsob na zapoudření?

Napadlo mě vytvořit Interface IVlastnosti, ISchopnosti, IDovednosti a místo tříd vracet je. Mohlo by to vypadat následovně:

// Část že třídy Postava
private Dovednosti dovednosti;
public IDovednosti Dovednosti { get { return dovednosti; } }

Vše funguje fantasticky, přesně tak, jak jsem chtěl. Je zde však jeden problém. Interface jde přetypovat zpět na třídu, tudíž se dá zapoudření obejít.

// Mimo třídu
((Dovednosti)postava.Dovednosti).DrevcoveZbrane++;

Ještě mě napadlo vytvořit speciální třídu DovednostiInfo, kterou bude postava vracet. To už mi ale připadá příliš komplikované.

Jaký je tedy nejlepší způsob zapouzdření pro tento příklad?
Pozn: doufám že tímto rozdělením nějak neporušuju logiku OOP

 
Odpovědět
16.2.2016 18:23
Avatar
Petr Čech
Tvůrce
Avatar
Odpovídá na Štěpán Wünsch
Petr Čech:16.2.2016 20:09

Normálně se tohle nepoužívá. Třeba takové kontrolky v WPF nebo WinForms mají také minimálně desítky vlastností a vše je prostě na 1 úrovni. Máš nějaký specifický důvod pro rozdělení vlastností do tříd?
Pokud bys to chtěl třeba z důvodu enumerace vlastností v jednotlivých "bloků" třeba jen zbraní, nejčistší by bylo udělat si vlastní atribut, který by tu skupinu specifikoval pomocí enum. Potom by sis udělal metodu nebo get-only property, která by pomocí reflexe mohla vrátit vlastnosti jen z dané skupiny. Jenže to bys asi musel udělat třídu navíc, která by reprezentovala jednotlivé vlastnosti, nejspíš generickou.
Pokud chceš kód, napiš mi. A použij prosím tlačítko odpovědět ;-)

Nahoru Odpovědět
16.2.2016 20:09
the cake is a lie
Avatar
coells
Tvůrce
Avatar
Odpovídá na Štěpán Wünsch
coells:16.2.2016 20:21

OOP je o způsobu návrhu aplikace, ne o bezpečnosti.
To znamená, že pokud máš instanci s rozhraním IDovednosti, tak to explicitně vyjadřuje, jak s danou instancí zacházet.
Dokonce i pokud bys nevěděl, jaká třída je ve skutečnosti instanciovaná, jsou v C# způsoby, jak změnit její interní stav.
Tím pádem si s tímhle nedělej hlavu a používej typy jako vyjádření způsobu komunikace s objektem.

A ohledně logiky OOP si nedělej hlavu, tvůj návrh na 100% neviděl OOP ani z dálky :-)
Návrh objektových aplikací musí začínat úplně jinak a vzniká na základě komunikace mezi objekty, ne na základě jejich vlastností.

 
Nahoru Odpovědět
16.2.2016 20:21
Avatar
Odpovídá na Štěpán Wünsch
sadlomaslox25:16.2.2016 20:44

v ramci C# stejne muzes pouzit reflexi a zmenit libovolny private field na jakekoli tride.

co se tyka prikladu tak staci abys tu tridu Dovednosti nastavil na private a vnoril do tridy Postava a mas to vyresene.

 
Nahoru Odpovědět
16.2.2016 20:44
Avatar
Petr Čech
Tvůrce
Avatar
Odpovídá na sadlomaslox25
Petr Čech:16.2.2016 21:07

Tady je to jedno, protože ten návrh je z hlediska návrhu špatně. Proto neexistují žádné fancy mechanismy, jak to udělat. Reflexe je sice zajímavá, ale nastavování privátních proměnných nepaří mezi best practices.

Nahoru Odpovědět
16.2.2016 21:07
the cake is a lie
Avatar
Odpovídá na Petr Čech
Štěpán Wünsch:17.2.2016 19:33

Děkuji všem za odpovědi.

Vlastnosti, Dovednosti a Schopnosti (V, D, S) jsem chtěl rozdělit do tříd především z toho důvodu, aby se k nim pohodlně přistupovalo. Po napsání slova postava. intellisense nenabízí milion proměnných ale pouze 3 (V, D, S) a po vybrání Dovedností zobrazí další např. JednorucniZbrane).

Petr Čech Něco o reflexích jsem našel až v pokročilém programování, já se zatím potloukám na nízké úrovni. Kdybys mi napsal nějaký příklad jak to zde využít, byl bych rád.

Enum se mi zdá pro tento příklad celkem vhodný. Napadlo mě to skladovat v privátní array kolekci a ven to pouštět přes metodu. Vypadá to celkem jednoduše a přistupuje se k tomu vcelku pohodlně.

enum Dovednosti { JednorucniZbrane, ObourucniZbrane, DrevcoveZbrane }; // atd

// Část že třídy Postava
private int[] dovednosti = new int[Enum.GetNames(typeof(Dovednosti)).Length];
public int VratDovednost(Dovednosti dovednost)
{
    return dovednosti[(int)dovednost];
}

// Mimo třídu
postava.VratDovednost(Dovednosti.DrevcoveZbrane);

coells To, že se dá nějak změnit private field na jákekoliv třídě jsem nevěděl. Děkuji za informaci.

 
Nahoru Odpovědět
17.2.2016 19:33
Avatar
Štěpán Wünsch:17.2.2016 21:12

Asi to nebudu řešit. Nejjednodušší bude, když prostě ve třídě Postava inicializuju všechny proměnné jako property {get; private set} s předponou např. DovednostDrev­coveZbrane nebo SchopnostAtletika

 
Nahoru Odpovědět
17.2.2016 21:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 7 zpráv z 7.