Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: anonym 02. 06. 2018, 14:25:35
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
-
Dej vsecko do konstruktoru, usetris dalsi metody :-)
-
Pokud si třída drží stav a inicializuje nějaké další objekty/proměnné, které se používají ve více metodách, tak to potřebuješ inicializovat před voláním těch metod – což se typicky dělá v konstruktoru.
Jsou i další možnosti: můžeš použít líné vyhodnocování a ke všem těm instančním proměnným přistupovat přes gettery a inicializovat je při prvním použití. Teoreticky ti to může ušetřit nějaké zdroje (co není potřeba, to se neinicializuje). Ale na druhou stranu si musíš dát pozor na pořadí (aby na sobě jednotlivé inicializace nezávisely nebo se volaly ve správném pořadí) a taky to vede k hůře odhalitelným chybám – když se něco vysype, tak ne při volání konstruktoru (kde by to člověk čekal, protože tam zadal ty parametry, které způsobily chybu), ale až někdy později, při volání nějaké metody, která s tím nesouvisí. Můžeš mít objekt ve špatném/nepoužitelném stavu a nevědět o tom (přijde se na to až při volání jeho metod).
Další možnost je návrhový vzor stavitel (builder) a případně i fluent interface. Pomocí volání různých metod postupně naparametrizuješ/postavíš daný objekt a až voláním poslední metody k němu získáš přístup – v tu chvíli budeš mít plně funkční objekt.
Nicméně (alespoň v jednodušších případech) bych nezavrhoval tu úplně obyčejnou inicializaci v konstruktoru a nedělal to složitější, než je potřeba. Při rozhodování je dobré si položit otázky:
- Jaký je očekávaný budoucí rozvoj dané třídy?
- Používá se jen v rámci daného projektu/modulu, nebo slouží jako knihovna/API pro ostatní týmy a nezávislé projekty/moduly? Tzn. můžu danou věc refaktorovat, aniž bych se musel domlouvat s někým dalším? Pokud ano, tak bych se držel co nejjednodušších řešení – složitěji to totiž můžu udělat kdykoli později. Naopak pokud vystavuji veřejné API, na které se ostatní budou napojovat a zvyknou si na něj, tak je lepší to dělat rovnou nějakým robustnějším způsobem (s ohledem na budoucí požadavky a změny), aby se rozhraní už nemuselo měnit a aby ses na těchto změnách nemusel domlouvat s ostatními (ta koordinace a komunikace sežere dost času a energie a někdy možnost změny úplně pohřbí).
-
Konstruktor by měl uvést objekt do výchozího konzistentního stavu. Zpravidla to vypadá tak, že pouze umístí předané parametry do lokálních atributů a hotovo. Obvykle ta data ani nevaliduji, protože v tu chvíli nevím, která pravidla k tomu budu potřebovat.
-
Trošku OT, ale téma mi připomnělo:
Stížnost mého kolegy, programátora:
KURVA! KURVA! KURVA! Predchozi programator vyrobil specialni tridu jen na prevadeni rychlosti z kilometru/sec. na metry/sec., dalsi specialni tridu na vypocet, jak dlouho trva presun danou rychlosti po usecce dane delky (jedina metoda teto tridy, ktera to vypocita, navic prijima rychlost v m/s, vzdalenost v metrech a vraci milisekundy)... ale uz si nezkontroluje, jestli mu nekdo do vstupnich souboru nepodhodil nulovou rychlost vozidla!
Takze klidne vypocita "potrebny cas", podeli nulou, vyjimka se zignoruje, prevede to nekonecno na long (to ty milisekundy), vyjde mu cas prekracujici stari vesmiru, klidne ho vrati dal, ten cas se pouzije jako simulacni cas, kdy ma prijit udalost "dosazeno cile", pricte se k nemu aktualni simulacni cas, pretece to, vyjde minus .. ten minus se pak prevede na dny a pak to zkolabuje na tom, ze vysledek neni z rozsahu 0..6, coz jsou platne dny v tydnu ...
kdo tohle pouzije na modelovani realne dopravy, ten nebude do smrti veselej ...
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Ano, setkal jsem se s tímto trvrzením. Za mě mohu říct, že není správně. A doufám, že se moc nerozšíří.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Ano, setkal jsem se s tímto trvrzením. Za mě mohu říct, že není správně. A doufám, že se moc nerozšíří.
V C++ to podle me plati, protoze v konstruktoru by se nemely volat virtualni metody. Cili pokud konstruktor neco pocita a potomek by chtel pocitat neco jineho, tak smula.
-
best practice je nepsat konstruktory vůbec, nechat je generovat automaticky. Validace a inicializace definovat pro jednotlivé atributy. Potom máte u všech objektů standardizované parametry a chybové hlášky. V Pythonu používáme všude knihovnu attrs (http://www.attrs.org/en/stable/).
-
Trošku OT, ale téma mi připomnělo:
Stížnost mého kolegy, programátora:
KURVA! KURVA! KURVA! Predchozi programator vyrobil specialni tridu jen na prevadeni rychlosti z kilometru/sec. na metry/sec., dalsi specialni tridu na vypocet, jak dlouho trva presun danou rychlosti po usecce dane delky (jedina metoda teto tridy, ktera to vypocita, navic prijima rychlost v m/s, vzdalenost v metrech a vraci milisekundy)... ale uz si nezkontroluje, jestli mu nekdo do vstupnich souboru nepodhodil nulovou rychlost vozidla!
Takze klidne vypocita "potrebny cas", podeli nulou, vyjimka se zignoruje, prevede to nekonecno na long (to ty milisekundy), vyjde mu cas prekracujici stari vesmiru, klidne ho vrati dal, ten cas se pouzije jako simulacni cas, kdy ma prijit udalost "dosazeno cile", pricte se k nemu aktualni simulacni cas, pretece to, vyjde minus .. ten minus se pak prevede na dny a pak to zkolabuje na tom, ze vysledek neni z rozsahu 0..6, coz jsou platne dny v tydnu ...
kdo tohle pouzije na modelovani realne dopravy, ten nebude do smrti veselej ...
Modelování dopravy a vůbec cokoliv důležitého by měli psát jen aspoň průměrně inteligentní lidé s odpovídajícím vzděláním. Kdo na to pustí pologramotné retardované tele, nemůže se divit.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Ano, setkal jsem se s tímto trvrzením. Za mě mohu říct, že není správně. A doufám, že se moc nerozšíří.
Já vždycky když jsem sloučil inicializaci a výpočet do konstruktoru, dostal jsem se do slepé uličky - jednak v kódu a jeho struktuře samotné, tak při unit testování. Ani validaci bych nedělal v konstruktoru, jak říká Kit. Když potřebuju validovat nějaká data, udělám z té třídy pojo a validaci té třídy dám bokem do jiné třídy.
-
Viz to Unit testování: třída bude mít private metody a já je budu muset mockovat. Jak budeš mockovat, když už při zavolání konstruktoru se ti spustí výpočetní logika?
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Ano, setkal jsem se s tímto trvrzením. Za mě mohu říct, že není správně. A doufám, že se moc nerozšíří.
V C++ to podle me plati, protoze v konstruktoru by se nemely volat virtualni metody. Cili pokud konstruktor neco pocita a potomek by chtel pocitat neco jineho, tak smula.
Výpočty klidně, ale hodně s mírou a člověk musí vědět co dělá. Což je u C(C++) stejně povinnost.
U C++ navíc přibývá problém s tím, že u statických class probíhá volání konstruktorů ještě před main. Teď už sice platí, že okolí už je jinak plně inicializované (výjimky, thread variable, new, malloc ...) (možná jen nějaký starý překladač by mohl mít problémy), ale ani pořadí volání jednotlivých statických konstruktorů není definované (občas překladač nabízí, ale bývá to i verze od verze jiné).
A vyloučit možnost, že někdy někdo (nebo i já, pokud zapomenu) použije class jako static, tak úplně nejde. Pokud vím o závislostech a počítat a využívat je musím, pak už zbývá jen to nějak obejít. Třeba lazy initialization, nifty counter / Schwarz counter, ... ale to bývá cesta do pekel.
P.S.: NEsouhlasím se zpracováním osobních údajů v rozsahu v odkazovaném textu u checkboxu, který jde NAD rámec údajů nezbytných pro technické zpracování
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Ano, setkal jsem se s tímto trvrzením. Za mě mohu říct, že není správně. A doufám, že se moc nerozšíří.
Já vždycky když jsem sloučil inicializaci a výpočet do konstruktoru, dostal jsem se do slepé uličky - jednak v kódu a jeho struktuře samotné, tak při unit testování. Ani validaci bych nedělal v konstruktoru, jak říká Kit. Když potřebuju validovat nějaká data, udělám z té třídy pojo a validaci té třídy dám bokem do jiné třídy.
Rozumím. Domnívám se, že ten problém bude jinde.
Inicializace a výpočet klidně do konstruktoru, pokud to zajistí, že ten objekt bude mět méně možnejch stavů.
To, zda budeš validovat nějaké data přímo, nebo si na to vytvoříš spešl objekt je fuk, ale stejně to hodím čím dřív tím líp, takže do konstruktoru.
Pokud máš pocit, že jsi se dostal do slepé uličky, tak si nějak poznamenej konkrétní případ, a nabídni to k diskusi, jak by to řešili ostatní. Pokud jde o mě, tak vyvozuješ zbrklej závěr, který nepovede k lepšímu kódu.
-
Viz to Unit testování: třída bude mít private metody a já je budu muset mockovat. Jak budeš mockovat, když už při zavolání konstruktoru se ti spustí výpočetní logika?
Jak chceš mockovat privátní metody?
Jak to souvisí s tím, že při konstruktoru se ti spustí výpočtení logika?
Jak to jakkoliv komplikuje jednotkové testování?
Nechápu.
-
...
Hezky sepsáno.
-
...
S tim vsim souhlasim, moc pekne napsano...
...
Chtel bych ale k tomuto poznamenat, ze osobne mam opacny nazor. Samozrejme neni vhodne vsechno dogmaticky odmitat, nebo prijimat a jsou ruzne situace, kdy se hodi i jine reseni(napriklad kvuli rychlosti), nicmene:
(Pozn: Toto je muj nazor, neminim vam ho tady nutit.)
Ve vetsine pripadu, pokud jsi se rozhodl pouzivat v programu vyjimky, je vhodne je vyhazovat hned pri konstrukci, pokud zjistis, ze vstupni data jsou nekonzistentni, nebo proste neodpovidaji pravidlum(pokud neni konstruktor privatni, tak idealne v nem, pripadne v nejake factory metode). Nadrazene funkce maji pak moznost rychle a precizne reagovat na vzniklou chybu. Do budoucna je toto mnohem lepe udrzovatelne(Vsechny API by mely byt co nejprimocarejsi a nejintuitivneji pouzitelne - pokud musis po konstrukci objektu explicitne validovat stav uz zkonstruovaneho objektu, tak to neni intuitivni. Mnohem lepsi je vubec nedovolit zkonstruovat nevalidni objekt.)
Co je ovsem dulezite je, ze validace i vypocty se musi vztahovat opravdu k dane tride. V konstruktoru samozrejme nevolat(nekde to lze, byvaji z toho logicky velke problemy, kdyz potomek jeste neni inicializovany) virtualni metody. Tj. je treba si uvedomit co vlastne konstruujete a validovat/pocitat presne pouze to, co dana trida predstavuje. Validovat/dopocitavat prvky, ktere jsou az kontrakt potomka, je treba delat az v nem a hlavne je treba nepredpokladat, co bude potomek potrebovat validovat/dopocitavat, protoze zasadne vzdy neco opomenete anebo naopak nastavite prilis striktni podminky. Silne dodrzovani tohoto (tak nejak za rucicku) prirozene vede i k lepsimu navrhu - muze to byt takova pomocna berlicka, ze ktere vypadne lepsi hierarchie trid, vcetne abstraktnich trid, atd...
-
Konkrétně v Javě je best practice nedávat do konstruktoru co tam není nutné. Konstruktor má vrátit objekt v konzistentním stavu, ale nic víc.
Problém nastává v případě, kdy konstruktor volá virtuální (overridovatelné) metody. Pak se může stát, že se zavolá implementace potomka, kterému ale konstruktor ještě neproběhl a tedy jeho pole nejsou nainicializována.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Pokial clovek nechce pouzivat lazy inicializaciu, tak je v konstruktore vhodne robit nejake vypocty. Dovodov, preco nepouzivat lazy inicializaciu je viacero, nebudem to tu rozoberat. Plati to najma u objektov, ktory maju dlhsiu zivotnost a su nakladnejsie na vyrobenie. Frameworky maju za tymto ucelom "init" metodu, tak ale je to prast ako uhod, len je o moznost viac, pouzit objekt v nekonzistentnom stave.
-
Viz to Unit testování: třída bude mít private metody a já je budu muset mockovat. Jak budeš mockovat, když už při zavolání konstruktoru se ti spustí výpočetní logika?
Jak chceš mockovat privátní metody?
Jak to souvisí s tím, že při konstruktoru se ti spustí výpočtení logika?
Jak to jakkoliv komplikuje jednotkové testování?
Nechápu.
Ok ještě jinak: máš třídu A a ta má vnitřní závislost na třídu B, která spustí laser, co vypálí díru do zdi. Ta B se nijak nesetuje zvenčí, je vytvářená uvnitř. No a ty sloučíš inicializaci i výpočet do konstruktoru. Jak to budeš potom testovat? Když si v testu zavoláš new A(), jsi v zadeki, protože se ti zavolá B a ty to nijak nemůžeš ovlivnit. Proto už z hlediska testování je na nic, když se v konstruktoru spustí nějaký návazný proces - ten musí být v metodě. A než přemýšlet, co se stane, tak raději budu dávat do konstruktoru pouze inicializace objektu. Když objekt nejprve inicializuju, pak v něm můžu mockovat potřebné věci. A pak terpve spouštím metodu, kterou chci testovat.
-
Frameworky maju za tymto ucelom "init" metodu, tak ale je to prast ako uhod, ...
Právě, že není. Alespoň v Javě platí, že konstruktor jako první věc musí zavolat konstruktor rodiče. U init metody žádné takové omezení není.
-
best practice je nepsat konstruktory vůbec, nechat je generovat automaticky. Validace a inicializace definovat pro jednotlivé atributy. Potom máte u všech objektů standardizované parametry a chybové hlášky. V Pythonu používáme všude knihovnu attrs (http://www.attrs.org/en/stable/).
Ano to je další možnost. V javě se na to používá Lombok.
-
Problém nastává v případě, kdy konstruktor volá virtuální (overridovatelné) metody. Pak se může stát, že se zavolá implementace potomka, kterému ale konstruktor ještě neproběhl a tedy jeho pole nejsou nainicializována.
Potvrzuji, že to je v Javě možné. V C++ to možné není. Pokud se volá virtuální metoda v konstruktoru, volá se verze ze stejné třídy, jako by nebyla virtuální.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Dospěl jsem ke stejnému závěru. Podobně jako jsem dospěl k eliminaci dědičnosti ve vlastním kódu a jejímu částečnému "nahrazení" pomocí skládání jednoduchých, jednoúčelových objektů. Přesné vysvětlení, jak jsem k tomu došel, bohužel nemám. Napadá mě jen:
Složitý konstruktor (vlastně jakýkoli neautomatický) konstruktor podle mě porušuje Single Responsibility principle, kdy každá třída by měla mít jen jeden účel, tedy umožnit konkrétní instanci dělat nějakou práci. Konstrukce objektu stojí v tomto pojetí jaksi mimo. Stejně mimo jako jakákoli (statická) metoda třídy, která nepracuje s konkrétním objektem.
Příčinou mnoha zbytečně složitých - a rádoby chytře děděných - konstruktorů, které jsem viděl, je pouze neschopnost najít místo zcela mimo třídu (najít jméno), kam by autor tento kód mohl umístit. Vznikají pak případy, kdy objekt pracuje s nějakou vlastností, ale parametrem konstruktoru je konfigurační soubor a konstruktor třídy vlastnost z konfiguračního souboru "šikovně" vytáhne a tato vytažení se pak "chytře" dědí, respektive sdílí přes nějaké role.
-
Když máte složitou logiku konstrukce objektů, vytvořte statickou metodu s příhodným názvem, třeba LoadFromConfigurationFile.
-
Když máte složitou logiku konstrukce objektů, vytvořte statickou metodu s příhodným názvem, třeba LoadFromConfigurationFile.
Pokud myslíte statickou metodu ve stejné třídě, tak i s tímto přístupem silně nesouhlasím. V rámci jedné třídy se pak míchají dvě zcela odlišná rozhraní - jednak "obecný" interface objektu a dvak způsob konstrukce objektu, který je zcela poplatný konkrétní aplikaci ve které se objekt používá a jejím pravidlům - formátu konfigurace, mapování konfigurace na parametry, apod. Nehledě na to, že většinou nechci, aby mi konstruktor každého dílčího objektu načítal znovu a znovu konfigurační soubor a řešil problémy s tím spojené, konfigurační soubor načtu na začátku aplikace jednou a v případě problémů se do konstrukce objektů, které jsou na něm závislé, vůbec nepouštím.
Nesouhlasím s tím přesto, že se uvedený postup asi občas využívá - LoadFromConfigurationFile, NewFromCommandLineOptions, ....
-
Já vždycky když jsem sloučil inicializaci a výpočet do konstruktoru, dostal jsem se do slepé uličky - jednak v kódu a jeho struktuře samotné, tak při unit testování. Ani validaci bych nedělal v konstruktoru, jak říká Kit. Když potřebuju validovat nějaká data, udělám z té třídy pojo a validaci té třídy dám bokem do jiné třídy.
Je velmi jednoduché validaci do takového objektu injektovat přes některou z jeho metod.
$object->validate($validator);
-
Já vždycky když jsem sloučil inicializaci a výpočet do konstruktoru, dostal jsem se do slepé uličky - jednak v kódu a jeho struktuře samotné, tak při unit testování. Ani validaci bych nedělal v konstruktoru, jak říká Kit. Když potřebuju validovat nějaká data, udělám z té třídy pojo a validaci té třídy dám bokem do jiné třídy.
Je velmi jednoduché validaci do takového objektu injektovat přes některou z jeho metod.
$object->validate($validator);
Jaký je důvod, aby validaci řídil objekt, který je validován a ne validátor? Tzn. proč bys dal metodu validate() do objektu a ne do validátoru?
Ze sémantického hlediska mi přijde přirozenější a správnější říct: „validátore, zkontroluj mi tenhle objekt“ a ne „objekte, běž se nechat zkontrolovat k validátorovi a pak mi řekni, jak jsi dopadl“.
Důvodem, proč to dělat obráceně by mohla být viditelnost polí, ale to podle mého není platný argument, protože by to narušovalo zapouzdření – pokud se objekt navenek (tzn. public a protected pole a metody) chová správně, tak bychom ho měli považoval za validní – co si dělá uvnitř v privátních polích a metodách, to je jeho věc a nás to nemusí zajímat.
-
Když si v testu zavoláš new A(), jsi v zadeki, protože se ti zavolá B a ty to nijak nemůžeš ovlivnit.
Ano, protože voláš B když ho volat nechceš. S mockováním to nesouvisí s testováním také ne.
Proto už z hlediska testování je na nic, když se v konstruktoru spustí nějaký návazný proces - ten musí být v metodě.
Když bude v metodě, tak z hlediska testování bude úplně stejný problém. Z hlediska mockování taky. Vůbec nic jsi tím nevyřešil.
A než přemýšlet, co se stane, tak raději budu dávat do konstruktoru pouze inicializace objektu.
Je mi líto, ale programování je o přemejšlení.
Když objekt nejprve inicializuju, pak v něm můžu mockovat potřebné věci. A pak terpve spouštím metodu, kterou chci testovat.
Ne, nemůžeš. Tím, že jsi to přestěhoval z konstruktoru do metody jsi vůbec nic nevyřešil. Furt ti to vypálí díru do zdi, furt to nemůžeš mockovat, furt to nejde testovat. Ten problém je totiž jinde.
Jestli ty se jen blbě nevyjadřuješ.
Dávat logiku do konstruktoru je IMHO naprosto v pořádku. Samozřejmě je nesmysl, dát do konstruktoru logiku "pal", když tu logiku chceš vykonat až zavoláním metody inst.pal(). Ale to absolutně nijak nesouvisí s pravidlem, který jsi postuloval na začátku. To považuju za zcela chybné.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Dospěl jsem ke stejnému závěru. Podobně jako jsem dospěl k eliminaci dědičnosti ve vlastním kódu a jejímu částečnému "nahrazení" pomocí skládání jednoduchých, jednoúčelových objektů. Přesné vysvětlení, jak jsem k tomu došel, bohužel nemám. Napadá mě jen:
Složitý konstruktor (vlastně jakýkoli neautomatický) konstruktor podle mě porušuje Single Responsibility principle, kdy každá třída by měla mít jen jeden účel, tedy umožnit konkrétní instanci dělat nějakou práci. Konstrukce objektu stojí v tomto pojetí jaksi mimo. Stejně mimo jako jakákoli (statická) metoda třídy, která nepracuje s konkrétním objektem.
Příčinou mnoha zbytečně složitých - a rádoby chytře děděných - konstruktorů, které jsem viděl, je pouze neschopnost najít místo zcela mimo třídu (najít jméno), kam by autor tento kód mohl umístit. Vznikají pak případy, kdy objekt pracuje s nějakou vlastností, ale parametrem konstruktoru je konfigurační soubor a konstruktor třídy vlastnost z konfiguračního souboru "šikovně" vytáhne a tato vytažení se pak "chytře" dědí, respektive sdílí přes nějaké role.
Na jedné staně extrém, kdy v konstruktoru jen nasetujeme atributy bez jakékoliv logiky.
Na druhé straně, logiku, kterou potřebujeme nemusíme nutně implementovat v konstruktoru. Můžeme ji mět v nějakých pomocných třídách, statických metodách - každopádně se ale provede v konstruktoru. Protože kdykoliv později je pozdě.
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Dospěl jsem ke stejnému závěru. Podobně jako jsem dospěl k eliminaci dědičnosti ve vlastním kódu a jejímu částečnému "nahrazení" pomocí skládání jednoduchých, jednoúčelových objektů. Přesné vysvětlení, jak jsem k tomu došel, bohužel nemám. Napadá mě jen:
Složitý konstruktor (vlastně jakýkoli neautomatický) konstruktor podle mě porušuje Single Responsibility principle, kdy každá třída by měla mít jen jeden účel, tedy umožnit konkrétní instanci dělat nějakou práci. Konstrukce objektu stojí v tomto pojetí jaksi mimo. Stejně mimo jako jakákoli (statická) metoda třídy, která nepracuje s konkrétním objektem.
Příčinou mnoha zbytečně složitých - a rádoby chytře děděných - konstruktorů, které jsem viděl, je pouze neschopnost najít místo zcela mimo třídu (najít jméno), kam by autor tento kód mohl umístit. Vznikají pak případy, kdy objekt pracuje s nějakou vlastností, ale parametrem konstruktoru je konfigurační soubor a konstruktor třídy vlastnost z konfiguračního souboru "šikovně" vytáhne a tato vytažení se pak "chytře" dědí, respektive sdílí přes nějaké role.
Já si "dědičnost" zreformuloval na "rozšiřování třídy". Slovo dědičnost v OOP tak, jak je popisováno v literatuře (ala zvířátka v ZOO), mi přijde silně zavádějící a aplikace tohoto typu dědičnosti vede k problémům v kódu. Proto když přemýšlím nad dědičností, používám vždycky vztah Rozšiřovat/Specializace, a nikoliv Dědit/Potomek. Protože Dědit/Potomek vede intuitivně ke vztahům typu Zvíře<-Chlupaté<-Tygr, nebo Otec<-Syn<-Vnuk, Obdelník<-Čtverec, což jsou na OOP nesmyslné/neaplikovatelné vazby z reálného a velice složitého světa. Oproti tomu Rozšiřovat/Specializace je mnohem konkrétnější a vztahuje se více k praktické podobě programového kódu.
Osobně si (zatím) myslím, že ty snahy úplně dát pryč Rozšiřívání tříd jsou druhý extrém. Je to podobné, jako když vznikají jazyky typu Go, že jako OOP je moc složité a nepotřebné a proto budoucnost patříd jazykům, kde se dají dělat pouze funkce.
-
Když si v testu zavoláš new A(), jsi v zadeki, protože se ti zavolá B a ty to nijak nemůžeš ovlivnit.
Ano, protože voláš B když ho volat nechceš. S mockováním to nesouvisí s testováním také ne.
Proto už z hlediska testování je na nic, když se v konstruktoru spustí nějaký návazný proces - ten musí být v metodě.
Když bude v metodě, tak z hlediska testování bude úplně stejný problém. Z hlediska mockování taky. Vůbec nic jsi tím nevyřešil.
A než přemýšlet, co se stane, tak raději budu dávat do konstruktoru pouze inicializace objektu.
Je mi líto, ale programování je o přemejšlení.
Když objekt nejprve inicializuju, pak v něm můžu mockovat potřebné věci. A pak terpve spouštím metodu, kterou chci testovat.
Ne, nemůžeš. Tím, že jsi to přestěhoval z konstruktoru do metody jsi vůbec nic nevyřešil. Furt ti to vypálí díru do zdi, furt to nemůžeš mockovat, furt to nejde testovat. Ten problém je totiž jinde.
Jestli ty se jen blbě nevyjadřuješ.
Dávat logiku do konstruktoru je IMHO naprosto v pořádku. Samozřejmě je nesmysl, dát do konstruktoru logiku "pal", když tu logiku chceš vykonat až zavoláním metody inst.pal(). Ale to absolutně nijak nesouvisí s pravidlem, který jsi postuloval na začátku. To považuju za zcela chybné.
Podle mě nemáš pravdu a přijde mi dost podivné to, co píšeš - jako kdybys to nikdy nedělal. Třídu B, která vypálí díru do zdi, budu mít jako private field v A. V testu v konstruktoru dojde pouze k inicializaci třídy A. Následně si namockuju tu třídu B v A (vytáhnu si ten private field reflexí a dám mu tam namockovanou verzi). Až potom zavolám a.hvezdaSmrti321Ted();, která uvnitř zavolá fake objekt this.b.lazerPal();.
-
Když si v testu zavoláš new A(), jsi v zadeki, protože se ti zavolá B a ty to nijak nemůžeš ovlivnit.
Ano, protože voláš B když ho volat nechceš. S mockováním to nesouvisí s testováním také ne.
Proto už z hlediska testování je na nic, když se v konstruktoru spustí nějaký návazný proces - ten musí být v metodě.
Když bude v metodě, tak z hlediska testování bude úplně stejný problém. Z hlediska mockování taky. Vůbec nic jsi tím nevyřešil.
A než přemýšlet, co se stane, tak raději budu dávat do konstruktoru pouze inicializace objektu.
Je mi líto, ale programování je o přemejšlení.
Když objekt nejprve inicializuju, pak v něm můžu mockovat potřebné věci. A pak terpve spouštím metodu, kterou chci testovat.
Ne, nemůžeš. Tím, že jsi to přestěhoval z konstruktoru do metody jsi vůbec nic nevyřešil. Furt ti to vypálí díru do zdi, furt to nemůžeš mockovat, furt to nejde testovat. Ten problém je totiž jinde.
Jestli ty se jen blbě nevyjadřuješ.
Dávat logiku do konstruktoru je IMHO naprosto v pořádku. Samozřejmě je nesmysl, dát do konstruktoru logiku "pal", když tu logiku chceš vykonat až zavoláním metody inst.pal(). Ale to absolutně nijak nesouvisí s pravidlem, který jsi postuloval na začátku. To považuju za zcela chybné.
Podle mě nemáš pravdu a přijde mi dost podivné to, co píšeš - jako kdybys to nikdy nedělal. Třídu B, která vypálí díru do zdi, budu mít jako private field v A. V testu v konstruktoru dojde pouze k inicializaci třídy A. Následně si namockuju tu třídu B v A (vytáhnu si ten private field reflexí a dám mu tam namockovanou verzi). Až potom zavolám a.hvezdaSmrti321Ted();, která uvnitř zavolá fake objekt this.b.lazerPal();.
Už ti rozumím. K tomu ti mohu říct jediné - nedělej to tak.
Private field je private. Z pohledu zvenčí neexistuje. Tudíž se netestuje. Ani reflexí, ani nijak. Tudíž to co děláš je špatné, ne proto, že by byla špatná logika v konstruktoru, ale proto, že je špatné pokoušet se nějak nabourávat do privátních fieldů, které jsi jakkoliv nasetovat - ať už v konstruktoru, nebo jinak.
Vlastně máš pravdu - tohle jsem nikdy nedělal. Ani by mě to nenapadlo :-) (Dobře, kecám, určitě kdysi dávno mě to napadlo, a možná jsem to i zkusil, ale pak mě klusic sežrali, co že to dělám za prasečiny.)
-
Mám pocit, že se tu anonym poněkud nešťastně vyjadřuje. Takže jen uvedu, co si představuju pod logikou v konstruktoru:
class A:
private engine
constructor(strategy, opts):
if strategy AND len(opts) > 1:
this.engine = strategy.many(opts)
elif strategy AND len(opts) = 1:
parser = new Parser()
this.engine = strategy.many(parser.parse(opts))
elif strategy AND empty(opts):
throw new Exception()
else:
this.engine = new D()
invoke(args):
this.engine.invoke(args)
Sorry, nenapadlo mě teď z fleku nic složitějšího. Ale třeba anonym uvede, co si představuje, že za logiku do toho konstruktoru nepatří.
-
Konstruktor by nemel obsahovat zadnou logiku (prestoze obcas je jednodussi podlehnout lennosti a neco do nej dopsat). Od konstruktoru se ocekava, ze inicializuje objekt, tj. priradi objekty privatnim fieldum a zkontruluje, ze tyto fieldy nejsou null, kdyz byt nemaji. Kdyz se pisou nejdrive testy a teprve pote implementace (napr. TDD), primo vas to k tomu vede. Idealni stav je, kdyz se do kontruktoru skrze rozhrani injektuji konkretni implementace z vnejsku. Sam objekt potom neni zavisly na konkretnich implementacich jednotlivych fieldu. Je to dobry zpusob, jak psat udrzitelny kod.
Situaci, kterou popisujete v prikladu (class A), lze resit takto:
1) interface A - pouze public rozhrani
2) class AbstractA (private, nebo package private - viditelna pouze ve factory)
3) class FactoryA, ktera v ruznych metodach vraci konkretni parametrizace uplne nastavene A.
Vhodnym pojmenovanim metod FactoryA, ktere budou poskytovat plne inicializovane rozhrani A, poskytnete uzivatelum factory prehledny a snadno pouzitelny nastroj pro vytvoreni instanci A. Dana Factory muze skryvat ruzne strategie parametrizace puvodni tridy.
Povzdech: Je skoda, ze se na skolach dodnes neuci technologie programovani. Potrebne zkusenosti clovek ziska az po delsi dobe: praci na legaci kodu, kde nejprve vidi priklady a dusledky spatne psaneho kodu, pote novych projektech, pod vedenim kvalitnich team-leaderu.
-
$object->validate($validator);
Jaký je důvod, aby validaci řídil objekt, který je validován a ne validátor? Tzn. proč bys dal metodu validate() do objektu a ne do validátoru?
Ze sémantického hlediska mi přijde přirozenější a správnější říct: „validátore, zkontroluj mi tenhle objekt“ a ne „objekte, běž se nechat zkontrolovat k validátorovi a pak mi řekni, jak jsi dopadl“.
Důvodem, proč to dělat obráceně by mohla být viditelnost polí, ale to podle mého není platný argument, protože by to narušovalo zapouzdření – pokud se objekt navenek (tzn. public a protected pole a metody) chová správně, tak bychom ho měli považoval za validní – co si dělá uvnitř v privátních polích a metodách, to je jeho věc a nás to nemusí zajímat.
Objekt si uvnitř zavolá ten validátor a předá mu takové hodnoty, které chce zvalidovat. Klidně i několikrát.
Analogická je i státní kontrola. Nekontroluje přímo, ale uloží povinnost provozovateli, aby si nechal zkontrolovat vozidlo a poskytne mu k tomu seznam STK.
Podobně funguje i formátování výstupu:
$object->format($style);
-
Když máte složitou logiku konstrukce objektů, vytvořte statickou metodu s příhodným názvem, třeba LoadFromConfigurationFile.
Pokud myslíte statickou metodu ve stejné třídě, tak i s tímto přístupem silně nesouhlasím. V rámci jedné třídy se pak míchají dvě zcela odlišná rozhraní - jednak "obecný" interface objektu a dvak způsob konstrukce objektu, který je zcela poplatný konkrétní aplikaci ve které se objekt používá a jejím pravidlům - formátu konfigurace, mapování konfigurace na parametry, apod. Nehledě na to, že většinou nechci, aby mi konstruktor každého dílčího objektu načítal znovu a znovu konfigurační soubor a řešil problémy s tím spojené, konfigurační soubor načtu na začátku aplikace jednou a v případě problémů se do konstrukce objektů, které jsou na něm závislé, vůbec nepouštím.
Nesouhlasím s tím přesto, že se uvedený postup asi občas využívá - LoadFromConfigurationFile, NewFromCommandLineOptions, ....
Jestli se vám to nelíbí, můžete si samozřejmě vytvořit jakoukoli další třídu, takže budete volat třeba MyClassBuilder.LoadFromConfigurationFile. Kdyby mi to vývojář napsal, asi bych se ho zeptal na praktický význam (žádný není), ale nijak bych mu v tom nebránil. Nikdy jsem zatím nenarazil na žádné reálné potíže s umístěním podobných metod přímo do třídy, definující můj objekt.
-
Konstruktor by nemel obsahovat zadnou logiku (prestoze obcas je jednodussi podlehnout lennosti a neco do nej dopsat). Od konstruktoru se ocekava, ze inicializuje objekt, tj. priradi objekty privatnim fieldum a zkontruluje, ze tyto fieldy nejsou null, kdyz byt nemaji.
Tomuto já říkám legaci kód :-)
-
Podle mě nemáš pravdu a přijde mi dost podivné to, co píšeš - jako kdybys to nikdy nedělal. Třídu B, která vypálí díru do zdi, budu mít jako private field v A. V testu v konstruktoru dojde pouze k inicializaci třídy A. Následně si namockuju tu třídu B v A (vytáhnu si ten private field reflexí a dám mu tam namockovanou verzi). Až potom zavolám a.hvezdaSmrti321Ted();, která uvnitř zavolá fake objekt this.b.lazerPal();.
Už ti rozumím. K tomu ti mohu říct jediné - nedělej to tak.
Private field je private. Z pohledu zvenčí neexistuje. Tudíž se netestuje. Ani reflexí, ani nijak. Tudíž to co děláš je špatné, ne proto, že by byla špatná logika v konstruktoru, ale proto, že je špatné pokoušet se nějak nabourávat do privátních fieldů, které jsi jakkoliv nasetovat - ať už v konstruktoru, nebo jinak.
Vlastně máš pravdu - tohle jsem nikdy nedělal. Ani by mě to nenapadlo :-) (Dobře, kecám, určitě kdysi dávno mě to napadlo, a možná jsem to i zkusil, ale pak mě klusic sežrali, co že to dělám za prasečiny.)
Už se přesně tady na tento problém vedla samostatná diskuze. Ano, private field zvenčí neexistuje, ale doprčic, NE PRO UNIT TESTY! To je úplně normální a NUTNÉ, že máš třídu a v ní jako private atribut něco, co musíš namockovat. To nemusí být laserpal, stačí aby to byl třeba Socket nebo zápis do souboru na disku!
-
Ano, private field zvenčí neexistuje, ale doprčic, NE PRO UNIT TESTY! To je úplně normální a NUTNÉ, že máš třídu a v ní jako private atribut něco, co musíš namockovat.
Privátní field unit testy netestují a ani by neměly. Testuješ kontrakt - veřejné rozhraní a mockuješ okolí třídy: parametry konstruktoru, parametry veřejných metod, případně veřejné rozhraní jiných tříd.
-
Ano, private field zvenčí neexistuje, ale doprčic, NE PRO UNIT TESTY! To je úplně normální a NUTNÉ, že máš třídu a v ní jako private atribut něco, co musíš namockovat.
Privátní field unit testy netestují a ani by neměly. Testuješ kontrakt - veřejné rozhraní a mockuješ okolí třídy: parametry konstruktoru, parametry veřejných metod, případně veřejné rozhraní jiných tříd.
Ano, jenže tohle ti poruší zapouzdřenost. Řekněme, že máš třídu, ve které budeš mít metodu
private zapisSpecialneDoSouboru(String info) {
// nejaky kod
this.bufferedWriter.write(str);
// nejaky kod
}
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu.
Asi máte na mysli inicializaci.
Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Konstruktor je (měla by být) metoda jako každá jiná, liší se jen v tom, že vytváří instanci třídy. Že je při inicializaci třeba něco dopočítat, je běžné. Jak byste si to jinak představoval, závisejí-li vnitřní stavy na vnitřní logice?
-
Konstruktor by měl uvést objekt do výchozího konzistentního stavu. Zpravidla to vypadá tak, že pouze umístí předané parametry do lokálních atributů a hotovo. Obvykle ta data ani nevaliduji, protože v tu chvíli nevím, která pravidla k tomu budu potřebovat.
Nemusí, ale je to nejjednodušší řešení.
Ale taky může inicializace znamenat složitý výpočet, na to žádné omezení neexistuje.
Neověřením vstupů se dostáváte do té samé situace, jako když objekt neinicializujete a ponecháte jej v nějakém bordelstavu (který pak musíte pochopitelně nějak řešit).
-
best practice je nepsat konstruktory vůbec, nechat je generovat automaticky. Validace a inicializace definovat pro jednotlivé atributy. Potom máte u všech objektů standardizované parametry a chybové hlášky. V Pythonu používáme všude knihovnu attrs (http://www.attrs.org/en/stable/).
:O
Ono je vůbec nejlepší nepsat třídy vůbec a nechat je generovat celé, potom mají standardizované parametry(???) a chybové hlášky(???).
-
...Když potřebuju validovat nějaká data, udělám z té třídy pojo a validaci té třídy dám bokem do jiné třídy.
A vynalezl jste to sám, nebo někde odkoukal? Pocamrejte se. https://en.wikipedia.org/wiki/Anemic_domain_model (https://en.wikipedia.org/wiki/Anemic_domain_model)
Takže jde to, ale stojí to přesně proti myšlence OOP.
-
Viz to Unit testování: třída bude mít private metody a já je budu muset mockovat. Jak budeš mockovat, když už při zavolání konstruktoru se ti spustí výpočetní logika?
Mock je objektem, který navenek slyší na stejný protokol, který má zastupovaný objekt. Jeho vnitřek je černou krabkou, tudíž mockování se skytými metodami původního objektu nemá nic společného! Asi proto tu druhou větu vůbec nechápu.
-
Ok ještě jinak: máš třídu A a ta má vnitřní závislost na třídu B, která spustí laser, co vypálí díru do zdi. Ta B se nijak nesetuje zvenčí, je vytvářená uvnitř. No a ty sloučíš inicializaci i výpočet do konstruktoru. Jak to budeš potom testovat? Když si v testu zavoláš new A(), jsi v zadeki, protože se ti zavolá B a ty to nijak nemůžeš ovlivnit...
Zjevně děláte něco špatně. Má-li třída A zadrátováno, že si vytváří a užívá třídu B, je to její vnitřní vlastností a je nesmyslné se divit, jaktože nejde vytvořit mock.
Řešení je více:
Takže buďto vyrobíte mock i pro třídu A, a to buďo zcela znovu, což může být zbytečné, nebo stačí do A metoda, která např. vrací třídu k vytvoření, nebo vrací rovnou vyrobenou instanci. Pak MockA vytvoříte dědicem s překrytím oné metody.
Nebo vytvoříte A tak, že když nechcete MockB strkat do instance třídy A zvenku, nainicializujete inst. A třídou, kterou budete uvnitř tvořit, nebo pomocí dependency injection instanci A vložíte objekt či uzávěru, která MockB vyrobí.
Není na tom nic složitého.
-
...Alespoň v Javě platí, že konstruktor jako první věc musí zavolat konstruktor rodiče. U init metody žádné takové omezení není.
To je jednou z domrdávek u prasojazyků, že konstruktor paušálně nejdřív volá svého předka, i kdybyste nechtěli. Potřebujete-li předtím něco nastavit, abyste mohli využít logiky předků, nebo nevolat vůbec, protože to potřebujete úplně jinak, jste v pr-deli. Dá se to jednoduše ojebat voláním inicializační metody z konstruktoru a překrývat vždy ji, ale už je to práce navíc.
Tento "bonus" jsem zaznamenal např. u C#.
Takže implementace "OOP" jsou různé, a ne vždy je to dobré.
-
https://en.wikipedia.org/wiki/Call_super (https://en.wikipedia.org/wiki/Call_super)
-
Když máte složitou logiku konstrukce objektů, vytvořte statickou metodu s příhodným názvem, třeba LoadFromConfigurationFile.
https://en.wikipedia.org/wiki/Softcoding (https://en.wikipedia.org/wiki/Softcoding)
-
Já si "dědičnost" zreformuloval na "rozšiřování třídy". Slovo dědičnost v OOP tak, jak je popisováno v literatuře (ala zvířátka v ZOO), mi přijde silně zavádějící a aplikace tohoto typu dědičnosti vede k problémům v kódu. Proto když přemýšlím nad dědičností, používám vždycky vztah Rozšiřovat/Specializace, a nikoliv Dědit/Potomek. ... Oproti tomu Rozšiřovat/Specializace je mnohem konkrétnější a vztahuje se více k praktické podobě programového kódu.
Termíny generalizace-specializace nejsou v čistém OOP novinkou, přičemž to první je slučování společných vlastností do nadtříd/nadobjektů, to druhé rozdělování rozdílných vlastností do podtříd/podobjektů.
Pozor, specializace často neznamená rozšiřování, ale obecně odlišení fungování!
Osobně si (zatím) myslím, že ty snahy úplně dát pryč Rozšiřívání tříd jsou druhý extrém. Je to podobné, jako když vznikají jazyky typu Go, že jako OOP je moc složité a nepotřebné a proto budoucnost patříd jazykům, kde se dají dělat pouze funkce.
Pochopitelně. Dědičnost může být jednoduchým prostředkem odvozování tříd, ale ne jediným či povinným, i když u prasojazyků je to těžší, protože jsou založeny na podtypovém polymorfismu, což je kamenem úrazu (řeší to částečně interfaces).
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
Celou dobu jsem mel podezreni na to, zes jeste neobjevil DI a IoC a tady tim jsi mi to potvrdil.
Jak uz tu psali jini, konstruktor by mel objekt inicializovat do konzistentniho stavu, jestli se pritom ma pouzit vypocet nebo jen nastavit hodnoty je podruzna zalezitost.
Tvuj problem patrne tkvi v tom, ze se ti v konstruktoru zacne objevovat komplexni logika a tim padem se ti narusi dobry zvyk, ze kazda metoda (konstruktor v tomto pripade lze chapat taky jako metodu) dela pouze jednu vec.
Jedno reseni je pouzit statickou (factory) metodu.
Ja si myslim, ze v tvem pripade by bylo jeste zahodno zvazit pristup oznacovany jako dependency injection, kde vsechny slozitejsi objekty, ktere v konstruktoru vytvaris, se vytvori vne a pak je vlozis pomoci konstruktoru. Prinos je troji: kod bude jednodussi, testovatelnejsi a pruznejsi.
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat?
Ale takhle se to bezne dela. Akorat neuvadis nazev souboru, ale obecny writer.
Mistoprivate zapisSpecialneDoSouboru(String info) {
pouzijes
private zapis(Writer wr) {
Jednou jako writer predas FileWriter pro zapis do souboru. Jindy predas StringWriter pro zapis do bufferu, ktery pouzijes pro testovani. A pokud prijde zakaznik s nejakym uchylnym pozadavkem, muzes tam dat Writer, ktery bude posilat data po siti nebo zapisovat libovolnym zpusobem.
To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy.
Zadne vnirtnosti tridy nevystavujes, takze zapouzdrenost je neporusena. To paradoxne delas s tou reflexi.
Prostě uděláš to, že bufferedWriter reflexí namockuješ!
To je neskutecna prasarna.
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
A jak se tam ta instance dostane? Snad nebudes delat new na neco, co potrebujes?!
-
Už se přesně tady na tento problém vedla samostatná diskuze. Ano, private field zvenčí neexistuje, ale doprčic, NE PRO UNIT TESTY! To je úplně normální a NUTNÉ, že máš třídu a v ní jako private atribut něco, co musíš namockovat. To nemusí být laserpal, stačí aby to byl třeba Socket nebo zápis do souboru na disku!
Ale houby! Účelem jednotkových testů není nic jiného než otestovat správnost objektu na jeho rozhraní. Jak si to vevnitř pořeší, je jeho problémem. Přeloženo: Reaguje-li objekt dle požadavku, je v pořádku. Z tohoto pohledu je nezajímavé, že vnitřně záleží na dalších objektech! Chcete-li testovat i je, musíte to udělat extra a jinde.
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
A jak se tam ta instance dostane? Snad nebudes delat new na neco, co potrebujes?!
Jak to myslíš?
-
Ano, jenže tohle ti poruší zapouzdřenost. Řekněme, že máš třídu, ve které budeš mít metodu
private zapisSpecialneDoSouboru(String info) {
// nejaky kod
this.bufferedWriter.write(str);
// nejaky kod
}
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
Samozřejmě že budete. Buďto v parametru, nebo taky v objektu. Jestliže to nemáte, pak jste vytvořit SPECIALIZOVANÝ objekt s metodou, který umí zapisovat VÝHRADNĚ přes buffered writer, a to ještě danou instanci, tj. objekt bez obecnějšího použití. Žádné vnitřnosti nevystavujete, instanci zapisovače může znát pouze generátor tohoto objektu.
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
A jak se tam ta instance dostane? Snad nebudes delat new na neco, co potrebujes?!
Jak to myslíš?
Ze konstruovat si zavislost uvnitr objektu (tedy i v konstruktoru) je skoro vzdy spatne.
-
Prostě uděláš to, že bufferedWriter reflexí namockuješ!
To je neskutecna prasarna.
Jo, na toto jsem se chtěl zeptat: Co to jako má být to "mockování reflexí"???
-
Ano, jenže tohle ti poruší zapouzdřenost. Řekněme, že máš třídu, ve které budeš mít metodu
private zapisSpecialneDoSouboru(String info) {
// nejaky kod
this.bufferedWriter.write(str);
// nejaky kod
}
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
Samozřejmě že budete. Buďto v parametru, nebo taky v objektu. Jestliže to nemáte, pak jste vytvořit SPECIALIZOVANÝ objekt s metodou, který umí zapisovat VÝHRADNĚ přes buffered writer, a to ještě danou instanci, tj. objekt bez obecnějšího použití. Žádné vnitřnosti nevystavujete, instanci zapisovače může znát pouze generátor tohoto objektu.
Přesně! Je to objekt bez obecného použití! Konečně! Jsme obklopeni objekty bez obecného použití!
1. Třeba tohle je přesně ten shit, který nechci dělat:
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
2. Proč, když to jde takhle jednoduše:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
A že není 2 tak obecná? Dpč, 99,5% případů užití je jen pro ten příklad č. 2!
-
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
Uz jenom to new XmlDocument() smrdi...
-
Přesně! Je to objekt bez obecného použití! Konečně! Jsme obklopeni objekty bez obecného použití!
1. Třeba tohle je přesně ten shit, který nechci dělat:
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
2. Proč, když to jde takhle jednoduše:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
A že není 2 tak obecná? Dpč, 99,5% případů užití je jen pro ten příklad č. 2!
Pak si ale nestěžujte, že nemůžete objekt použít i s jinými závislostmi, např. právě tím mockem při testech (kdy test je jen speciálním použitím obecného objektu).
-
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
Uz jenom to new XmlDocument() smrdi...
Satai tak teď sis naběhl, vždyť to je z .NETu :D A pochybuju, že budeš lepší designeři .NETu, to budou lidi jako je Joshua Bloch, chytří a zkušení jako sfiňa.
-
Ano, private field zvenčí neexistuje, ale doprčic, NE PRO UNIT TESTY! To je úplně normální a NUTNÉ, že máš třídu a v ní jako private atribut něco, co musíš namockovat.
Privátní field unit testy netestují a ani by neměly. Testuješ kontrakt - veřejné rozhraní a mockuješ okolí třídy: parametry konstruktoru, parametry veřejných metod, případně veřejné rozhraní jiných tříd.
Ale jo mely.
Akceptacni testy testuji rozhrani a tim padem overuji pouzitelnost v kontextu aplikace.
Unit testy - testuji jednotku. tudiz vnitrni implementaci a strukturu testovaneho objektu.
Casto se to plete dohromady.
https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/ (https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/)
Doznani: Unit testy nepisu. Je to moc slozite(A jeste slozitejsi udrzovat). Akceptacni testy vetsinou staci(aspon u mych projektu to tak bylo vzdy)
-
Došel jsem ze zkušeností k tomu, že konstruktor je třeba výlučně používat pouze k nasetování stavu objektu. Je chyba v konstruktoru provádět jakékoliv výpočty atp., vždycky jsem se tím dostal do problémů a musel jsem to refaktorovat. Máte stejný poznatek? A pokud ano, jak jste na to přišli? Nečetl jsem o tom nikdy v žádné literatuře a přitom mi to přijde jako důležité pravidlo.
Mate pravdu. V Jave napr. treba prihliadat aj na suvislosti, ktore vyplyvaju z JMM - garancia immutability atd.
Inak povedane, v konstruktore by sa nemalo diat nic, co sposobuje side effecty, t.j. I/O, zmeny shareovaneho stavu, potencialne leakovania nedoinicializovanych objektov inym vlaknam atd.
-
Satai tak teď sis naběhl, vždyť to je z .NETu :D A pochybuju, že budeš lepší designeři .NETu, to budou lidi jako je Joshua Bloch, chytří a zkušení jako sfiňa.
Joshua Bloch v Efektive Java doporučuje faktory metody.
-
Přesně! Je to objekt bez obecného použití! Konečně! Jsme obklopeni objekty bez obecného použití!
1. Třeba tohle je přesně ten shit, který nechci dělat:
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
2. Proč, když to jde takhle jednoduše:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
A že není 2 tak obecná? Dpč, 99,5% případů užití je jen pro ten příklad č. 2!
Pak si ale nestěžujte, že nemůžete objekt použít i s jinými závislostmi, např. právě tím mockem při testech (kdy test je jen speciálním použitím obecného objektu).
A mne by se libilo:
Document doc = XmlDocument.loadFrom(xmlFile)
:)
-
A mne by se libilo:
Document doc = XmlDocument.loadFrom(xmlFile)
:)
Mně zase:
Document doc = new XmlDocument(xmlFile);
-
A mne by se libilo:
Document doc = XmlDocument.loadFrom(xmlFile)
:)
Mně zase:
Document doc = new XmlDocument(xmlFile);
Jestli se ti to líbí, tak to ti dám radu: vyhni se Javě obloukem.
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
Jak to vyřešíš, je druhořadé. Jednotkové testování samo o sobě vede k tomu, že ti pomáhá s dobrým návrhem kódu.
Proč je reflexe nesmysl si dobře uvědomíš v okamžiku, kdy si uvědomíš, že testováním testuješ případy užití té třídy (a správné chování v nich). Případ užití pro reflexi nějakých interních atributů (jak můžeš v jedné větě s reflexí zmiňovat zapouzdřenost?!) není, tudíž ho ani netestuješ.
-
Tak DOPRČIC, přece nebudeš mít v konstruktoru třídy parametr BufferedWriter jenom proto, abys to potom mohl otestovat? To je úplná kravina, porušuje to zapouzdřenost, vystavuješ ven vnitřnosti třídy. Prostě uděláš to, že bufferedWriter reflexí namockuješ!
Jak to vyřešíš, je druhořadé. Jednotkové testování samo o sobě vede k tomu, že ti pomáhá s dobrým návrhem kódu.
Proč je reflexe nesmysl si dobře uvědomíš v okamžiku, kdy si uvědomíš, že testováním testuješ případy užití té třídy (a správné chování v nich). Případ užití pro reflexi nějakých interních atributů (jak můžeš v jedné větě s reflexí zmiňovat zapouzdřenost?!) není, tudíž ho ani netestuješ.
Moje poslední reakce na tebe v tomhle vláknu, protože to nemá smysl. Integrační testy testují pípady užití té třídy, ne Unit testy! Unit testy, o kterých je tu řeč, testují metody té třídy izolovaně od okolí, pitvají tu třídu! Člověk, se kterým má smysl na tohle vést řeč, by to měl vědět.
-
1. Třeba tohle je přesně ten shit, který nechci dělat:
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
2. Proč, když to jde takhle jednoduše:
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFile);
A že není 2 tak obecná? Dpč, 99,5% případů užití je jen pro ten příklad č. 2!
A co na tom nechceš dělat? Ty příklady jsou zcela zaměnitelný, použij co ti přijde hezčí. Neřeší to nic z toho, co popisuješ.
Případ, kdy objekt zpracovává soubor:
class A:
private DocumentBuilder parser
constructor():
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
this.parser = dbFactory.newDocumentBuilder();
parse(file):
doc = this.parser.parse(file)
-- další logika, kdy zpracovávám doc
Případ, kdy soubor konfiguruje objekt:
class B:
private config
constructor(configfile):
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
parser = dbFactory.newDocumentBuilder();
doc = parser.parse(configfile)
this.config = unifiq(doc)
foo(data):
-- další logika, kdy zpracovávám config
-
Integrační testy testují pípady užití té třídy, ne Unit testy! Unit testy, o kterých je tu řeč, testují metody té třídy izolovaně od okolí, pitvají tu třídu!
Jednotkové testy testují jednotku. Pitvavé testy pitvají třídu.
Integrační testy testují integraci třídy do systému. Proto se jmenují integrační.
Netřeba v tom hledat nějakou složitost. Prostě jen se drž názvosloví a nevymejšlej blbosti.
Člověk, se kterým má smysl na tohle vést řeč, by to měl vědět.
Jsi si jist, že máš na to posuzovat znalosti někoho jiného? Vzhledem k tomu, co tu uvádíš za bludy?
Slovy klasika, že nezkušení jsou si tak jistí, a zkušení plni pochybností.
Přeji ti hodně štěstí, už se nebudu obtěžovat ti něco vysvětlovat.
-
Unit testy - testuji jednotku. tudiz vnitrni implementaci a strukturu testovaneho objektu.
Casto se to plete dohromady.
https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/ (https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/)
Testují chování implementaci vůči veřejnému rozhraní. Testují, zda se to chová jak bylo domluveno. Ale netestují žádné privátní fieldy, protože to jaksi není veřejné rozhraní, a jaksi to ani nikdy nepoužiješ. Vývojář to kdykoliv může přepsat, privátní fieldy zahodit, privátní pomocné metody sloučit, rozdělit, nebo přepsat, a testy musí stále projít. Protože se nezměnilo chování ale pouze implementace. Od toho ty testy jsou.
Testovat implementaci pro implementaci jaksi nemá smysl, že jo.
Ostatně ten článek neříká nic o tom, že by se mělo testovat privátní fieldy, nebo pitvat třídu, jak si to tu anonym představoval.
-
A mne by se libilo:
Document doc = XmlDocument.loadFrom(xmlFile)
:)
Mně zase:
Document doc = new XmlDocument(xmlFile);
Jestli se ti to líbí, tak to ti dám radu: vyhni se Javě obloukem.
Proč?
-
Kit: protože v Javě se na všechno preferuje ten ukecaný složitější způsob.
-
Unit testy - testuji jednotku. tudiz vnitrni implementaci a strukturu testovaneho objektu.
Casto se to plete dohromady.
https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/ (https://www.lucassaldanha.com/unit-tests-vs-acceptance-tests/)
Testují chování implementaci vůči veřejnému rozhraní. Testují, zda se to chová jak bylo domluveno. Ale netestují žádné privátní fieldy, protože to jaksi není veřejné rozhraní, a jaksi to ani nikdy nepoužiješ. Vývojář to kdykoliv může přepsat, privátní fieldy zahodit, privátní pomocné metody sloučit, rozdělit, nebo přepsat, a testy musí stále projít. Protože se nezměnilo chování ale pouze implementace. Od toho ty testy jsou.
Testovat implementaci pro implementaci jaksi nemá smysl, že jo.
Ostatně ten článek neříká nic o tom, že by se mělo testovat privátní fieldy, nebo pitvat třídu, jak si to tu anonym představoval.
Ja nedavam za pravdu anonymum... Jen mi prijde, ze se prilis casto unit testy zamenuji za akceptacni.
Podle me to co popisujes jsou opravdu akceptacni testy. Jednim z poznavacich znaku je i to, ze pokud se nezmeni rozhrani, ale jen implementace tak na akceptacni testy nemusim sahat.
Oproti tomu unit testy obvykle zmenit musim i pri zmene implementace (beze zmeny rozhrani). Protoze unit testy by mely opravdu testovat tu implementaci.
Kdysi jsem poslouchal nejaky podcast, ale uz si nevzpomenu jaky. A tam vyvojar vysvetloval, ze v ramci unit testu testuje jaky konkretni typ kolekce je pouzit pro privatni field nejakeho objektu. Byla to nejaka life-critical aplikace (software kardiostimulatoru nebo neco podobneho) a oni pomoci tech unit testu vlastne vynucovali hlubsi zamysleni pri zmene implementace. Takze nejaky smysl to asi ma. Ale jak jsem psal sam to nedelam (moje aplkace nejsou life-critical). Pisu jen akceptacni testy a rikam jim akceptacni testy. Stydel bych se je nazyvat unit testy...
-
Ja nedavam za pravdu anonymum... Jen mi prijde, ze se prilis casto unit testy zamenuji za akceptacni.
Podle me to co popisujes jsou opravdu akceptacni testy. Jednim z poznavacich znaku je i to, ze pokud se nezmeni rozhrani, ale jen implementace tak na akceptacni testy nemusim sahat.
No tak to se má samosebou, ne? Unittesty jsou na tu menší část, akceptační jsou skoro u klienta. Když rozbiješ unit, tak rozbiješ akceptační, když nerozbiješ akceptační, tak jsi nerozbil unit.
Oproti tomu unit testy obvykle zmenit musim i pri zmene implementace (beze zmeny rozhrani). Protoze unit testy by mely opravdu testovat tu implementaci.
Co je myšleno tou implementací?
Mám objekt. Jednotkou testování je tento objekt (přesněji, volání metody, nebo řady metod nad jednou instancí objektu). Proto je to jednotkové testování. Ten objekt mi naparsuje text na 70znakové kousky. Pokud by byla pravda, že unittesty testují, zda ten objekt používá utf8-supported string parser, nebo ne, tak k čemu je mi takový test vlastně dobrý? Se ti nedivím, že je pak nepíšeš.
Naopak, já budu testovat, zda mi zohledňuje ty háčky a čárky. Jestli to dělá pomocí funkce str(), nebo random(), nebo magic() to je mě upřímně šumák. Neřeším, a není důvod řešit. Nepřináší to vůbec žádný užitek.
Akceptační testování je například to, že mám systém, který zpracovává utf8. Bude nám tenhle objekt, který utf8 neumí stačit? (Klidně může, když jsme v té části všechny háčky odstranili.)
Jak vím, zda umí utf8? Podle unittestů, nebo podle toho, že volá funkci str_parser_UTF8()? A jak poznám, že str_parser_UTF8() skutečně umí utf8? A tak dále.
Unittesty testují chování jednotky. Implementace zajímá vývojáře.
Kdysi jsem poslouchal nejaky podcast, ale uz si nevzpomenu jaky. A tam vyvojar vysvetloval, ze v ramci unit testu testuje jaky konkretni typ kolekce je pouzit pro privatni field nejakeho objektu. Byla to nejaka life-critical aplikace (software kardiostimulatoru nebo neco podobneho) a oni pomoci tech unit testu vlastne vynucovali hlubsi zamysleni pri zmene implementace. Takze nejaky smysl to asi ma.
Já netvrdím, že se takovéhle pitvační testy nemohou v některých, extrémních případech hodit. Ale nejsou to unittesty, a unittesty nejsou akceptační.
V některých případech je psaní unittestů náročné. A tak se buď napíší akceptační testy, které nejsou důsledné, ale stačí, nebo klidně se napíše takováhle prasárna, pak to sice furt není důsledné, ale alespoň je to otestované.
-
Kit: protože v Javě se na všechno preferuje ten ukecaný složitější způsob.
Houby - pokial nie nejde spravit cez stream a lambdy, tak to nie je JAVA. Lambdy su buducnost.
-
Kit: protože v Javě se na všechno preferuje ten ukecaný složitější způsob.
Houby - pokial nie nejde spravit cez stream a lambdy, tak to nie je JAVA. Lambdy su buducnost.
Lambda sondu si strč do řiti.
-
Kit: protože v Javě se na všechno preferuje ten ukecaný složitější způsob.
Houby - pokial nie nejde spravit cez stream a lambdy, tak to nie je JAVA. Lambdy su buducnost.
Lambda sondu si strč do řiti.
Agresivita je zbytocna, lambdam skor ci neskor podlahnete. Je to elegantne a netreba potom riesit malichernosti s konstruktormi.
-
Agresivita je zbytocna, lambdam skor ci neskor podlahnete. Je to elegantne a netreba potom riesit malichernosti s konstruktormi.
Tak lambda je defakto ad-hoc objekt bez setterů, mající právě jednu metodu :-) Co si budem povídat, geniální koncept.
-
A realita je taková, jaká je, používá se OOP a možná do toho trochu streamy/linq na zpracování dat.
No jasně. A ty streamy a linq vůbec nejsou funkcionální programování, že? ;D
-
A realita je taková, jaká je, používá se OOP a možná do toho trochu streamy/linq na zpracování dat.
No jasně. A ty streamy a linq vůbec nejsou funkcionální programování, že? ;D
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
-
A realita je taková, jaká je, používá se OOP a možná do toho trochu streamy/linq na zpracování dat.
No jasně. A ty streamy a linq vůbec nejsou funkcionální programování, že? ;D
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
Az na to, ze ten Prolog neni funkcionalni... Protoze ne vsechno deklarativni je funkcionalni.
-
Já netvrdím, že se takovéhle pitvační testy nemohou v některých, extrémních případech hodit. Ale nejsou to unittesty, a unittesty nejsou akceptační.
Těm pitvačním testům se říká developer test a běžně se používají. V Javě nejlépe statickou vnitřní třídou, v ostatních jazycích vloženou metodou, která se pak obvykle musí odstranit (u Dlang nemusí). Jednotkové, integrační, systémové a akceptační testy však zůstávají jako součást životního cyklu projektu.
-
Az na to, ze ten Prolog neni funkcionalni... Protoze ne vsechno deklarativni je funkcionalni.
Myslím, že Prolog patří hlavně do kategorie Logických jazyků. Ale spíše také, páč deklarativní je.
-
Tak si myslim, ze mame neshodu v terminologii a nicem jinem.
Tomu cemu ty rikas pitvacni ja rikam unit testy.
Tomu cemu rikas unit testy ja rikam akceptacni (i na urovni testovani jedine metody/funkce).
Snazil jsem se najit pro svou terminologii nejakou solidnejsi oporu ale nenasel jsem. Spis mi prijde, ze terminologie jeste neni ustalena a kazdy si to nazyva jak chce.
Narazil jsem na tohle: https://testing.googleblog.com/2010/12/test-sizes.html (https://testing.googleblog.com/2010/12/test-sizes.html)
Coz vypada, ze i u google meli v terminologii gulas a tak zavedli uplne "novou": small, medium, large...
-
A realita je taková, jaká je, používá se OOP a možná do toho trochu streamy/linq na zpracování dat.
No jasně. A ty streamy a linq vůbec nejsou funkcionální programování, že? ;D
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
Prolog není funkcionální. Ale někdo už vymyslel λ-prolog.
-
Tak si myslim, ze mame neshodu v terminologii a nicem jinem.
Tomu cemu ty rikas pitvacni ja rikam unit testy.
Tomu cemu rikas unit testy ja rikam akceptacni (i na urovni testovani jedine metody/funkce).
Snazil jsem se najit pro svou terminologii nejakou solidnejsi oporu ale nenasel jsem. Spis mi prijde, ze terminologie jeste neni ustalena a kazdy si to nazyva jak chce.
Narazil jsem na tohle: https://testing.googleblog.com/2010/12/test-sizes.html (https://testing.googleblog.com/2010/12/test-sizes.html)
Coz vypada, ze i u google meli v terminologii gulas a tak zavedli uplne "novou": small, medium, large...
Tak může být.
Já jsem se zatím setkal s tím, že programátoři píší testy, kdy testují chování jednotek, to jest tříd, nebo funkcí. Většinou jsme tomu říkali jednotkové testování. JUnit, NUnit, ... Cílem bylo zjistit, zda všechny stavy toho objektu dělají to co chceme. Tím že se to zapíše do testu se navíc toto chování zakonzervuje. V některých jazycích takových testů potřebuješ méně (Haskell), v některých musíš testovat úplně všechno (Javascript, Python).
Pak jsem se setkal s tím, že máš nějakou velkou legaci obludu, a začíná se ti to rozpadat pod rukama. Tak se dělají akceptační testy třeba na web pomocí Selenia. Nebo jsem psal testy na konzolovou aplikaci, což znamenalo, že jsem si testem vytvořil prostředí, spustit apku s vhodnými argumenty, a pak zjišťoval, co vypotila.
Rozdíl oproti těm předchozím je v tom, že jen obtížně pokryješ všechny možné scénáře. Což v legaci kódu už jen pár testů pomůže. A vůbec obecně netestuješ jednu jednotku, ale spíše harmonii všech těch jednotek, které tvoří aplikaci.
Ano, dalo by se uvažovat tak, že ty akceptační jsou vlastně jen ty jednotkové ve větším, protože když testuješ objekt, tak on taky jen deleguje práci na další své závislosti a tak. Rozdíl ale vidím v tom, že nikdo soudný nevytváří objekty v těch objektech ručně, ale předává je jako závislost - tudíž testuješ čistě a pouze interakci toho objektu s těmi závislostmi. A při testech jsou ty závislosti namockované. Zatímco v těch akceptačních testech testuješ, zda skutečně proběhl zápis do databáze, vytvořil se soubor, odeslal se mail. Testuješ, zda proběhnou všechny ty interakce v té hromadě provázaných objektů. V jednotkovém testu testuješ jen zda se správně zeptal té namockované závislosti.
Jak tomu pak říkat, je jiná věc :-)
-
Ja nedavam za pravdu anonymum... Jen mi prijde, ze se prilis casto unit testy zamenuji za akceptacni.
Podle me to co popisujes jsou opravdu akceptacni testy. Jednim z poznavacich znaku je i to, ze pokud se nezmeni rozhrani, ale jen implementace tak na akceptacni testy nemusim sahat.
Oproti tomu unit testy obvykle zmenit musim i pri zmene implementace (beze zmeny rozhrani). Protoze unit testy by mely opravdu testovat tu implementaci.
Kdysi jsem poslouchal nejaky podcast, ale uz si nevzpomenu jaky. A tam vyvojar vysvetloval, ze v ramci unit testu testuje jaky konkretni typ kolekce je pouzit pro privatni field nejakeho objektu. Byla to nejaka life-critical aplikace (software kardiostimulatoru nebo neco podobneho) a oni pomoci tech unit testu vlastne vynucovali hlubsi zamysleni pri zmene implementace. Takze nejaky smysl to asi ma. Ale jak jsem psal sam to nedelam (moje aplkace nejsou life-critical). Pisu jen akceptacni testy a rikam jim akceptacni testy. Stydel bych se je nazyvat unit testy...
Obávám se, že to, co popisujete, je jen čísi interpretací. Původní SUnit vznikl pro účely TDD, které samo o sobě metodicky a z podstaty testuje objekty pouze zvenku. Tolik k původní koncepci.
-
Tak lambda je defakto ad-hoc objekt bez setterů, mající právě jednu metodu :-) Co si budem povídat, geniální koncept.
To nemusí být vůbec pravdou, záleží na implementaci. Mimoto je docela obvyklým nedostatkem nerozlišovat lambdy a uzávěry.
-
Tak lambda je defakto ad-hoc objekt bez setterů, mající právě jednu metodu :-) Co si budem povídat, geniální koncept.
To nemusí být vůbec pravdou, záleží na implementaci. Mimoto je docela obvyklým nedostatkem nerozlišovat lambdy a uzávěry.
To rozlišování je pochybné, je to porovnávání jablek s hruškami.
-
Tak lambda je defakto ad-hoc objekt bez setterů, mající právě jednu metodu :-) Co si budem povídat, geniální koncept.
To nemusí být vůbec pravdou, záleží na implementaci. Mimoto je docela obvyklým nedostatkem nerozlišovat lambdy a uzávěry.
Bylo by zajímavé, když by si rozvedl rozdíly. Jinak je to o ničem.
-
Pokud je mi známo, např. Jávka řeší uzávěry vznikem anonymní funkce při překladu. Smalltalk má na uzávěry instance třídy BlockClosure.
Rozdíl lambdy a uzávěry asi slyšet nechcete...
-
Pokud je mi známo, např. Jávka řeší uzávěry vznikem anonymní funkce při překladu. Smalltalk má na uzávěry instance třídy BlockClosure.
Rozdíl lambdy a uzávěry asi slyšet nechcete...
Chceme. Jak to bude v monádách?
-
Pokud je mi známo, např. Jávka řeší uzávěry vznikem anonymní funkce při překladu. Smalltalk má na uzávěry instance třídy BlockClosure.
Rozdíl lambdy a uzávěry asi slyšet nechcete...
Tak já jsem psal, co je lambda z takového toho filozofického hlediska. Jak je to reálně implementováno není to, co jsem řešil a tudíž jsem si to samozřejmě zjednodušil a rozdíl mezi lambdou a uzávěrem jsem zanedbal.
Takže pokud máš nějakou pointu z tohoto hlediska...
-
"What is the difference between a 'closure' and a 'lambda'?"
https://stackoverflow.com/a/36878651 (https://stackoverflow.com/a/36878651)
-
Tak já jsem psal, co je lambda z takového toho filozofického hlediska. Jak je to reálně implementováno není to, co jsem řešil a tudíž jsem si to samozřejmě zjednodušil a rozdíl mezi lambdou a uzávěrem jsem zanedbal.
Lambda je jen anonymní funkce. Uzávěr vznikne vhodnou implementací lambdy tak, aby nesl stav.
-
Tak já jsem psal, co je lambda z takového toho filozofického hlediska. Jak je to reálně implementováno není to, co jsem řešil a tudíž jsem si to samozřejmě zjednodušil a rozdíl mezi lambdou a uzávěrem jsem zanedbal.
Lambda je jen anonymní funkce. Uzávěr vznikne vhodnou implementací lambdy tak, aby nesl stav.
Ne nutně.
-
Tak já jsem psal, co je lambda z takového toho filozofického hlediska. Jak je to reálně implementováno není to, co jsem řešil a tudíž jsem si to samozřejmě zjednodušil a rozdíl mezi lambdou a uzávěrem jsem zanedbal.
Lambda je jen anonymní funkce. Uzávěr vznikne vhodnou implementací lambdy tak, aby nesl stav.
Nerozumím, proč mi to píšeš.
-
A realita je taková, jaká je, používá se OOP a možná do toho trochu streamy/linq na zpracování dat.
No jasně. A ty streamy a linq vůbec nejsou funkcionální programování, že? ;D
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
Tvrdit to můžou, ale málokdo skutečně píše funkcionálně.
-
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
Tvrdit to můžou, ale málokdo skutečně píše funkcionálně.
Málokdo programuje v Javě objektově, to je fakt těžké. Java je prostě multiparadigmatickým jazykem a na každou vrstvu aplikace se hodí jiný přístup.
-
Ty vole já nevím co lidi s tím funkcionálním programováním mají, když pořád tvrdí, že tak programujou, a přitom jen používají lambdu a streamy. Seber se, běž si zaprogramovat do Prologu a pak znova tvrď, že programuješ funkcionálně v Javě.
Tvrdit to můžou, ale málokdo skutečně píše funkcionálně.
Málokdo programuje v Javě objektově, to je fakt těžké. Java je prostě multiparadigmatickým jazykem a na každou vrstvu aplikace se hodí jiný přístup.
V Javě to je především v podstatě nemožné.
-
Pokud je mi známo, např. Jávka řeší uzávěry vznikem anonymní funkce při překladu. Smalltalk má na uzávěry instance třídy BlockClosure.
Rozdíl lambdy a uzávěry asi slyšet nechcete...
Chceme. Jak to bude v monádách?
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
Monády neznám, takže ani netuším, zda s tím mají něco společného. Ale vzhledem k tomu, že je tu řeč o OOP, tak je to asi jedno, ne?
-
Pokud je mi známo, např. Jávka řeší uzávěry vznikem anonymní funkce při překladu. Smalltalk má na uzávěry instance třídy BlockClosure.
Rozdíl lambdy a uzávěry asi slyšet nechcete...
Chceme. Jak to bude v monádách?
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
To je ale blbost, z držení kontextu neplyne stavovost. Navíc libovolný uzávěr (sic!) jde emulovat prostým lambda výrazem.
-
Motate to oba. Lambda vyraz a uzaver jsou dva odlisne, vzajemne ortogonalni pojmy.
Na LISPu nebo podobnych jazycich to jde videt mnohem lip.
Lambda vyraz je vyraz, jenz se vyhodnoti na funkci. Tecka.
(lambda (x) (* x x)) -> funkce jednoho paremetru pocitajici druhou mocninu.
Vsimnete si, ze zadny uzaver nevznikl, protoze telo lambda-vyrazu neobsahuje volne promenne.
(let ((y 10))
(lambda (x) (* x y)))
V tomto pripade je vracena funkce jednoho parametru (vracejici nasobek desiti) a v tomto pripade se jedna o uzaver, protoze symbol "y" je v lexikalne nadrazenem prostredi.
Na druhou stranu, aby vznikl uzaver, neni potreba lambda vyrazu:
def a():
y = 10
def b(x):
return x * y
return b
a()(20) ==> 200
-
Motate to oba. Lambda vyraz a uzaver jsou dva odlisne, vzajemne ortogonalni pojmy.
Na LISPu nebo podobnych jazycich to jde videt mnohem lip.
Lambda vyraz je vyraz, jenz se vyhodnoti na funkci. Tecka.
(lambda (x) (* x x)) -> funkce jednoho paremetru pocitajici druhou mocninu.
Vsimnete si, ze zadny uzaver nevznikl, protoze telo lambda-vyrazu neobsahuje volne promenne.
(let ((y 10))
(lambda (x) (* x y)))
V tomto pripade je vracena funkce jednoho parametru (vracejici nasobek desiti) a v tomto pripade se jedna o uzaver, protoze symbol "y" je v lexikalne nadrazenem prostredi.
Na druhou stranu, aby vznikl uzaver, neni potreba lambda vyrazu:
def a():
y = 10
def b(x):
return x * y
return b
a()(20) ==> 200
"Krásné" nepochopení toho, co je lambda-výraz...
-
(lambda (x) (* x x)) -> funkce jednoho paremetru pocitajici druhou mocninu.
Vsimnete si, ze zadny uzaver nevznikl, protoze telo lambda-vyrazu neobsahuje volne promenne.
Jen pro pořádek, co myslíš, že to * je zač? Proměnná (v lispu se říká přesněji symbol). Takže jako příklad poněkud nešťastné.
-
(lambda (x) (* x x)) -> funkce jednoho paremetru pocitajici druhou mocninu.
Vsimnete si, ze zadny uzaver nevznikl, protoze telo lambda-vyrazu neobsahuje volne promenne.
Jen pro pořádek, co myslíš, že to * je zač? Proměnná (v lispu se říká přesněji symbol). Takže jako příklad poněkud nešťastné.
Za chvíli ti tu někdo vysvětlí, že v FP proměnné nejsou (což je sice blbost, ale někteří to omílají furt dokola). Jinak to * nemá vůbec žádný vliv na uzávěrovost, ale to už zacházíme k příliš pokročilým aspektům (v kontextu roota), tak se děti přestaňte hádat.
-
Za chvíli ti tu někdo vysvětlí, že v FP proměnné nejsou (což je sice blbost, ale někteří to omílají furt dokola). Jinak to * nemá vůbec žádný vliv na uzávěrovost, ale to už zacházíme k příliš pokročilým aspektům (v kontextu roota), tak se děti přestaňte hádat.
Máš pocit, že se tě někdo na něco ptal?
-
Za chvíli ti tu někdo vysvětlí, že v FP proměnné nejsou (což je sice blbost, ale někteří to omílají furt dokola). Jinak to * nemá vůbec žádný vliv na uzávěrovost, ale to už zacházíme k příliš pokročilým aspektům (v kontextu roota), tak se děti přestaňte hádat.
Máš pocit, že se tě někdo na něco ptal?
Nejen že to píše bláboly, ani se to neumí slušně chovat...
-
bot("Onestone") Off ?
Hele nevíte někdo prosím, jak se ti boti vypínají?
-
"Krásné" nepochopení toho, co je lambda-výraz...
A neco k tematu by nebylo?
Jen pro pořádek, co myslíš, že to * je zač? Proměnná (v lispu se říká přesněji symbol). Takže jako příklad poněkud nešťastné.
Tak si tam dosad treba:
(lambda (f g x) (f (g x))
Ano, bude to presnejsi, ale prinos pro porozumeni bude vyrazne mensi.
Ale vlastne to neni podstatou diskuzi na rootu, nekomu neco vysvetlit, aby to pochopil. Dulezite je pohonit ego.
-
Jen pro pořádek, co myslíš, že to * je zač? Proměnná (v lispu se říká přesněji symbol). Takže jako příklad poněkud nešťastné.
Tak si tam dosad treba:
(lambda (f g x) (f (g x))
Ano, bude to presnejsi, ale prinos pro porozumeni bude vyrazne mensi.
Nedalo by se říct, že lambda je ta anonymní funkce, a uzávěr je celej ten kontext kolem ní, který si musí táhnout sebou, aby tam byly ty navázaný proměnný se správnou hodnotou? Plus pak je tam ještě ta sranda s tím, že ve většině jazycích se zafixovávaj, v některých (CLisp) ne (tuším, že se tomu snad říká dynamic scoping).
-
Ano, bude to presnejsi, ale prinos pro porozumeni bude vyrazne mensi.
Mě by ten tvůj původní příklad zmátl :-)
-
Nedalo by se říct, že lambda je ta anonymní funkce,
Asi dalo, ale muze to byt matouci. Radsi pouzivam pojem "lambda-vyraz", coz je vyraz, ktery se vyhodnoti na funkci. Tu muzes zavolat a pracovat s ni jako s jakoukoliv jinou funkci neho hodnotou. (Odpada tam problem s tim, jestli tou anonymni funkci je jeji deklarace nebo vysledna hodnota.)
uzávěr je celej ten kontext kolem ní, který si musí táhnout sebou
Uzaver je ta funkce + odkaz na lexikalne nadrazene prostredi. Kam se funkce diva na hodnoty volnych promennych (symbolu), kdyz je zavolana (vyhodnocena).
Plus pak je tam ještě ta sranda s tím, že ve většině jazycích se zafixovávaj
To dela JVM jako optimalizaci. U uzaveru nedrzi odkaz na nadrazene prostredi, ale vsechny pouzite hodnoty nakopiruje do uzaveru. LISPy to nedelaji a je s tim vetsi sranda.
(tuším, že se tomu snad říká dynamic scoping)
To je neco uplne jineho.
-
Nedalo by se říct, že lambda je ta anonymní funkce,
Asi dalo, ale muze to byt matouci. Radsi pouzivam pojem "lambda-vyraz", coz je vyraz, ktery se vyhodnoti na funkci. Tu muzes zavolat a pracovat s ni jako s jakoukoliv jinou funkci neho hodnotou. (Odpada tam problem s tim, jestli tou anonymni funkci je jeji deklarace nebo vysledna hodnota.)
uzávěr je celej ten kontext kolem ní, který si musí táhnout sebou
Uzaver je ta funkce + odkaz na lexikalne nadrazene prostredi. Kam se funkce diva na hodnoty volnych promennych (symbolu), kdyz je zavolana (vyhodnocena).
Plus pak je tam ještě ta sranda s tím, že ve většině jazycích se zafixovávaj
To dela JVM jako optimalizaci. U uzaveru nedrzi odkaz na nadrazene prostredi, ale vsechny pouzite hodnoty nakopiruje do uzaveru. LISPy to nedelaji a je s tim vetsi sranda.
Takže jak jsem to psal.
(tuším, že se tomu snad říká dynamic scoping)
To je neco uplne jineho.
Dynamický versus lexikální rozsah platnosti tomu říkaj.
-
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
To je ale blbost, z držení kontextu neplyne stavovost. Navíc libovolný uzávěr (sic!) jde emulovat prostým lambda výrazem.
Obecně ano, protože nemůžete zajistit neměnnost prostředí/kontextu.
Tu emulaci mi ukažte, to mě zajímá.
-
Motate to oba. Lambda vyraz a uzaver jsou dva odlisne, vzajemne ortogonalni pojmy.
Na LISPu nebo podobnych jazycich to jde videt mnohem lip.
Lambda vyraz je vyraz, jenz se vyhodnoti na funkci. Tecka.
(lambda (x) (* x x)) -> funkce jednoho paremetru pocitajici druhou mocninu.
Vsimnete si, ze zadny uzaver nevznikl, protoze telo lambda-vyrazu neobsahuje volne promenne.
(let ((y 10))
(lambda (x) (* x y)))
V tomto pripade je vracena funkce jednoho parametru (vracejici nasobek desiti) a v tomto pripade se jedna o uzaver, protoze symbol "y" je v lexikalne nadrazenem prostredi.
Na druhou stranu, aby vznikl uzaver, neni potreba lambda vyrazu:
def a():
y = 10
def b(x):
return x * y
return b
a()(20) ==> 200
Dědo Kenedy, kurva, v podstatě to samé jsem napsal. A jestli říkáte uzávěra i definicím funkce, tak to už jde mimo mě. Tento kus diskuse tu řeším proto, protože se to všude hemží uzávěrama, ale všichni tomu říkají lambdy, aniž by věděli, co to je a že nejsou.
-
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
z držení kontextu neplyne stavovost. Navíc libovolný uzávěr (sic!) jde emulovat prostým lambda výrazem.
Obecně ano, protože nemůžete zajistit neměnnost prostředí/kontextu.
To nesouvisí s uzávěry, funkce (lambda) bez kontextu si taky může držet stav.
-
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
To je ale blbost, z držení kontextu neplyne stavovost. Navíc libovolný uzávěr (sic!) jde emulovat prostým lambda výrazem.
Tu emulaci mi ukažte, to mě zajímá.
Tzv. uzávěry jsou syntaktická záležitost (vztahují se k lexikálnímu kontextu). Uzávěr je (teoreticky, ale často i implementačně, od Lispu po Javu) uzavřený výraz (λ), kde tzv. kontext (lexikální) je reprezentován vázanými proměnnými s aplikací v době překladu, kdežto “parametry” se aplikují za běhu. Zní to složité, ale je to triviální - názorně to je vidět třeba ve zdrojáku JVM. V jazycích, kde se nerozlišuje mezi dobou překladu a běhu, se ten rozdíl stírá.
-
Lamba je samostatným, bezestavovým předpisem. Uzávěra je předpisem, který obsahuje závislosti na svém kontextu, tudíž stavová.
To je ale blbost, z držení kontextu neplyne stavovost. Navíc libovolný uzávěr (sic!) jde emulovat prostým lambda výrazem.
Obecně ano, protože nemůžete zajistit neměnnost prostředí/kontextu.
Tu emulaci mi ukažte, to mě zajímá.
P.S. Viz ještě C++, kde se vázané proměnné aplikované za překladu explicitně uvádějí - Meyers to někde rozepisoval jako λ-výrazy, když vysvětloval, jak to vnitřně funguje. Je poučné se nad tím zamyslet například v porovnání s Haskellem (na to je tu expert balki, jistě rád vysvětlí, až podojí a obslouží ovce).