it-swarm-eu.dev

Konstruktor by obecně neměl volat metody

Kolegovi jsem popsal, proč konstruktér, který volá metodu, může být antipattern.

příklad (v mém rezavém C++)

class C {
public :
    C(int foo);
    void setFoo(int foo);
private:
    int foo;
}

C::C(int foo) {
    setFoo(foo);
}

void C::setFoo(int foo) {
    this->foo = foo
}

Chtěl bych tuto skutečnost lépe motivovat prostřednictvím vašeho dalšího příspěvku. Pokud máte příklady, odkazy na knihy, stránky blogů nebo názvy principů, byly by velmi vítány.

Edit: Mluvím obecně, ale kódujeme v pythonu.

12
Stefano Borini

Nezadali jste žádný jazyk.

V C++ musí konstruktor dávat pozor při volání virtuální funkce, takže skutečnou funkcí, kterou volá, je implementace třídy. Pokud se jedná o čistě virtuální metodu bez implementace, jedná se o narušení přístupu.

Konstruktor může volat nevirtuální funkce.

Pokud je váš jazyk Java, kde jsou funkce ve výchozím nastavení obvykle virtuální), je rozumné, že musíte být zvlášť opatrní.

Zdá se, že C # zvládne situaci tak, jak byste očekávali: v konstruktorech můžete volat virtuální metody a volá nejposlednější verzi. Takže v C # není anti-vzor.

Běžným důvodem pro volání metod z konstruktorů je to, že máte více konstruktorů, které chtějí volat běžnou metodu „init“.

Všimněte si, že destruktory budou mít stejný problém s virtuálními metodami, takže nemůžete mít virtuální metodu „vyčištění“, která sedí mimo váš destruktor a očekávat, že ji zavolá destruktor základní třídy.

Java a C # nemají destruktory, mají finalizátory. Neznám chování s Java.

Zdá se, že C # v tomto ohledu správně provádí čištění.

(Všimněte si, že ačkoli Java a C # mají sbírku odpadků, spravuje pouze alokaci paměti. Existuje i další vyčištění, které musí váš destruktor udělat, ale uvolnění paměti).

26
CashCow

Dobře, nyní, když zmatek ohledně metody třídy vs instance metody je vymazán, mohu dát odpověď :-)

Problém není s voláním instančních metod obecně z konstruktoru; je to s voláním virtuálních metod (přímo nebo nepřímo). A hlavním důvodem je, že , zatímco uvnitř konstruktoru, objekt ještě není zcela zkonstruován . A zejména její části podtřídy nejsou vůbec konstruovány, zatímco konstruktor základní třídy provádí. Takže jeho vnitřní stav je nekonzistentní v závislosti na jazyce, a to může způsobit různé jemné chyby v různých jazycích.

O C++ a C # již diskutovali ostatní. V Javě bude volána virtuální metoda nejvíce odvozeného typu, tento typ však dosud nebyl inicializován. Pokud tedy tato metoda používá některá pole z odvozeného typu, nemusí být tato pole v tomto okamžiku ještě správně inicializována. Tento problém je podrobně popsán v Efektivní Java 2. vydání , položka 17: Návrh a dokument pro dědičnost nebo jinak zakázat.

Všimněte si, že se jedná o zvláštní případ obecného problému publikování referencí na objekt předčasně . Metody instance mají implicitní parametr this, ale předávání this explicitně metodě může způsobit podobné problémy. Zejména v souběžných programech, kde je-li odkaz na objekt publikován předčasně na jiné vlákno, může toto vlákno na něm již zavolat metody před dokončením konstruktoru v prvním vláknu.

18
Péter Török

Nepovažoval bych zde volání metod za samo o sobě antipattern, spíše za vůni kódu. Pokud třída poskytuje metodu reset, která vrací objekt do původního stavu, pak volání reset() v konstruktoru je DRY. (Nedělám žádné prohlášení o metodách resetování).

Zde je článek, který může pomoci uspokojit vaši žádost o autoritu: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/

Nejde ve skutečnosti o metody volání, ale o konstruktéry, kteří dělají příliš mnoho. IMHO, metody volání v konstruktoru jsou vůně, které by mohly naznačovat, že konstruktor je příliš těžký.

To souvisí s tím, jak snadné je vyzkoušet váš kód. Důvody zahrnují:

  1. Testování jednotek zahrnuje spoustu tvorby a ničení - konstrukce by proto měla být rychlá.

  2. V závislosti na tom, co tyto metody dělají, může být obtížné testovat diskrétní jednotky kódu, aniž by se spoléhalo na některé (potenciálně netestovatelné) předpoklady nastavené v konstruktoru (např. Získávání informací ze sítě).

7
Paul Butcher

Filozoficky je záměrem konstruktoru přeměnit surový kus paměti v instanci. Během provádění konstruktoru objekt dosud neexistuje, proto je volání jeho metod špatným nápadem. Možná nevíte, co dělají interně, a mohou být oprávněně považováni za objekt, který alespoň existuje (duh!), Když jsou povoláni.

Technicky s tím nemusí být nic špatného, ​​v C++ a zejména v Pythonu je na vás, abyste byli opatrní.

Prakticky byste měli omezit volání pouze na takové metody, které inicializují členy třídy.

3
user17621

Nejedná se o obecný problém. Je to problém v C++, konkrétně při použití dědičnosti a virtuálních metod, protože konstrukce objektu se děje dozadu a vtable ukazatele se resetují s každou konstrukční vrstvou v hierarchii dědičnosti, takže pokud jste Když voláte virtuální metodu, nemusíte nakonec získat tu, která skutečně odpovídá třídě, kterou se pokoušíte vytvořit, která překonává celý účel použití virtuálních metod.

V jazycích s sane OOP, které nastavují vtable ukazatel od začátku správně) tento problém neexistuje.

2
Mason Wheeler

S voláním metody existují dva problémy:

  • volání virtuální metody, která může udělat něco nečekaného (C++) nebo použít části objektů, které ještě nebyly inicializovány
  • vyvolání veřejné metody (která by měla vynutit invarianty třídy), protože objekt ještě není nutně úplný (a proto jej invariant nemusí držet)

S voláním pomocné funkce není nic špatného, ​​pokud nespadá ve dvou předchozích případech.

2
Matthieu M.

To si nekoupím. V objektově orientovaném systému je volání metody téměř jediné, co můžete udělat. Ve skutečnosti je to více či méně definice „objektově orientovaného“. Takže pokud konstruktér nemůže volat žádné metody, tak co může to udělá?

1
Jörg W Mittag

V O.O.P. teorie, nemělo by na tom záležet, ale v praxi každý O.O.P. programovací jazyk zpracovává konstruktéry odlišně. Statické metody nepoužívám velmi často.

V C++ & Delphi, Kdybych měl dát některým vlastnostem („členům pole“) počáteční hodnoty a kód je velmi rozšířený, přidám některé sekundární metody jako rozšíření konstruktorů.

A nevolajte jiné metody, které dělají složitější věci.

Pokud jde o metody „getters“ a „setters“, obvykle k ukládání jejich stavu používám soukromé/chráněné proměnné, plus metody „getters“ a „setters“.

V konstruktoru přiřazuji "výchozí" hodnoty stavovým polím, BEZ volání "accessors".

0
umlcat