Sdružené třídy (class clusters) C#   zodpovězená otázka

C#

Dobrý den všem,

plácám se s vytvořením sdružených tříd v C#, abych si vytvořil alternativu k abstraktním třídám.

Snad by pomohl nějaký jednoduchý příklad, příp. nasměrování...

Děkuji za případné odpovědi

nahlásit spamnahlásit spam 0 odpovědětodpovědět

C# concept označovaný jako "class cluster" přímo nepodporuje. Jde to obejít tím, že konkrétní podtřídy jsou private a nested k hlavní abstraktní třídě (nebo jsou internal pokud to nevadí).

Je ale potřeba vhodně navrhnout nějakou factory, která určí, kdy která konkrétní třída vznikne. To bude záležet na konkrétním scénáři, např. může být do statické factory metody předáván název podtřídy.

Příklad:

public abstract class Animal
{
    private sealed class Giraffe : Animal
    {
    }

    private sealed class Tiger : Animal
    {
    }

    public static Animal CreateFromString(string animal)
    {
        string typeName = "Sample.Animal+" + animal;
        Type type = typeof(Animal).Assembly.GetType(typeName, true, true);

        var obj = (Animal)Activator.CreateInstance(type, true);
        return obj;
    }
}

var giraffe = Animal.CreateFromString("Giraffe");
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Měl bych několik připomínek.

Za prvé, takto je Vám ta faktory metoda k ničemu, protože instanci Animal si nevytvoříte, abyste na ní poté mohl zavolat CreateFromString. Metoda samozřejmě musí být statická, ale řekl bych, že Vy to víte, jen jste to zapomněl napsat.

Za druhé, vnořené typy se nehledají přes tečkovou notaci, ale přes +. Zkuste si Vaší aplikaci spustit a podívat se, co vypíše

Assembly.GetExecutingAssembly().GetTypes()

a uvidíte

...
{Name = "Animal" FullName = "Sample.Animal"}
{Name = "Giraffe" FullName = "Sample.Animal+Giraffe"}
{Name = "Tiger" FullName = "Sample.Animal+Tiger"}
...

A za třetí, vnořené typy se používají v případě, když cítíte, že Vaše třída je zodpovědná za moc věcí a chcete jí trochu odlehčit, ale pořád se jedná o logiku privátní té dané třídě. Například pokud interně tvoříte složitou hierarchii dat a nechcete mít něco jako List<List<List>>, pak si vyvtoříte interní pomocné typy, které se chovají jako kolekce.

Možná jste jen zvolil špatný příklad, protože takto mi přijde přitažený za vlasy. Proč bych dělal konkrétní typy zvířat privátní a vnořené a jejich instance hledal pomocí reflexe? Tak jak je to napsané je jasné, že nové druhy zvířat budete přidávat Vy, protože pokud někdo jiný zdědí od Animal, stejně je mu ta factory metoda k ničemu. Takže třeba switch a nebo podle mě lepší statický Dictionary<string, Animal>.

Netuším, jakou definici pro class clusters používáte, já jsem si na netu našel pěkně hodnocenou následující

You want to provide a Factory (Cluster) interface that, with minimal description, manufactures and returns a specific concrete instance of a Factory Object that satisfies the behavior of the cluster family described by the Factory (Cluster) interface.

i s příkladem, který mě na private nested typy rozhodně nevede. Zdroj: http://stackoverflow.com/q/1844158/80900...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Děkuji, nezkoušel jsem to, ty dvě chybičky jsem opravil.

Ano tento design byl myšlen tak, že konkrétní třídy implementuje pouze autor třídy Animal a uživatel o nich navenek vůbec neví. To je vlastnost, kterou jsem našel, že by měl Class Cluster mít.

(Pod tou definicí co jste našel vy, je něco podobného:"Class clusters provide a single public interface to a group of concrete, private subclass implementations.")

Je ale dost dobře možné, že tazatel chce dosáhnout něčeho úplně jiného, čemu by v C# odpovídal design úplně jiný.

Jasný, že v mém příkladu by samozřejmě stačil obyčejný switch nebo cokoliv, ta reflexe by pomohla k tomu, že by se po doplnění konkrétní třídy nemusela factory metoda měnit.

Také by to chtělo vylepšit, aby nikdo jiný než implementátor třídy Animal nemohl tuto třídu podědit, například doplněním private konstruktoru:

private Animal() { }
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Proč chcete vytvářet alternativu k abstraktním třídám? Proč pro Vás nejsou dostačující? Jaký je Váš scénář? Vídám hodně programátorů, kteří přejdou na jinou platformu či jazyk, táhnout zvyky s sebou. To je ale blbost. Přece při přechodu z C na C# nebudete stále použivat ukazatele (pokud to není opravdu nutné nebo výhodné), atp. Class clusters není vzor vlastní C# a proto bych doporučil jej nepoužívat. Jestliže v .NET existuje řešení, použijte primárně to. Je to vhodné z několika důvodů. Nebudete mást lidi, kteří čtou Váš kód, vyhnete se trapným situacím, kdy něco prostě nepůjde udělat v kódu slušně a naučíte se dívat na problémy z jiného pohledu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Verim, ze sdruzene tridy jsou v obj. navrhem silnym principem a je skoda, ze neni C# vlastni. Konkretni tridy nejsou soucasti rozhrani. Tim si vystacim s jedinym, nekomplikuju system trid a utuzim zapouzdrenost. Vas kolega by tak nemusel studovat radu trid a vystaci si s jedinou. Ty plky na konci jsem liny komentovat. Tomasi diky, chtel jsem vydet implementaci od nekoho zkusenyho. Diky i za opravu chybek

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak pak je rozumný nápad vystavit pouze interface a konkrétní implementace mít privání či interní v dané assembly, tak nějak jak Tomáš vysvětluje definici vzoru. Tím máme vyřešenou stranu public API. Co se týče vnitřní logiky, která je známá pouze Vám, rozhodně bych se nevydal cestou zapouzdřených konkrétních tříd v abstraktní třídě, ale normálně interface : implementace. Abstraktní třída je abstraktní proto, že nemá vědět, jak je používaná. Má mít nějaké předpoklady a podle toho poskytovat členy na přepsání, ale nemá znát detaily o každé své implementaci.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Souhlasim. Chci normalne vytvaret instance verejne tridy, ve ktere implementuji spolecne sluzby a ta potaji za horizontem zapouzdreni (pomoci statickych metod) vytvari instance konkretnich podtrid, ktere dedi od spolecne nadtridy. Pouziju priklad s ucty. Verejna nadtrida Ucet implemetuje spolecne sluzby vybrat, ulozit, zustatek. Konkretni skryte podtridy sluzby dedi a implementuji konkretni sluzbu urok. Me se nedari pomoci statickych metod vytvaret instanci konkretni podtridy, abych mel k dispozici vsechny sluzby.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud třída účet nemá žádnou vlastní logiku, jen definuje služby, může to klidně být rozhraní. Pro účty to bude určitě abstraktní třída, protože každý účet má svoje číslo, atd. Ale ať je to jednodušší ...

public interface IUcet
{
    decimal Vybrat();
    void Ulozit(decimal castka);
    decimal Zustatek { get; }
}

Pak máme ty konkrétní implementace, které známe jenom my.

internal class BeznyUcet : IUcet
{
    public decimal Vybrat() { return 1; }
    public void Ulozit(decimal castka) { }
    public decimal Zustatek { get { return 0; } }
}

internal class SporiciUcet : IUcet
{
    public decimal Vybrat() { return 2; }
    public void Ulozit(decimal castka) { }
    public decimal Zustatek { get { return 0; } }
}

Pak statickou třídu na vytváření účtů, ať už veřejnou, nebo privátní, která obsahuje logiku pro zakládání nových účtů.

public static class TovarnaNaUcty
{
    public static IUcet ZalozitNovyUcet(string id)
    {
        if (id.StartsWith("ABC"))
            return new BeznyUcet();
	if (id.StartsWith("XYZ"))
	    return new SporiciUcet();
			
	throw new ArgumentException("Spatny kod uctu.");
    }	
}

A nakonec konzumenta tohoto API, který zná jenom rozhraní IUcet a případně má přístup k továrně, ale ani to nemusí být. Jeho nezajímají konkrétní typy účtů, jen potřebuje rozhraní pro výběr a uložení peněz.

IUcet ucet = TovarnaNaUcty.ZalozitNovyUcet("ABC123456789DEF");
decimal vybranePenizeNevimOdkud = ucet.Vybrat();

Souhlasíte? Máte něco podbného? Kde přesně je teď problém?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Princip je v podstate prosty. Jak jsem uz uvadel - verejna nadtrida implememuje logiku, ktera je spolecna pro vsechny podtridy, ty tuto logiku dedi. Pouze konkretni metody jsou implementovany v podtridach. Diky za Vas cas.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback