pondělí 30. července 2012

Tell, Don't Ask

... aneb snižujte závislosti mezi třídami

Jedna z možných definic návrhového principu Tell, Don't Ask (TDA) zní takto:

"Každé rozhodování zcela závislé na vnitřním stavu objektu by mělo být prováděno uvnitř tohoto objektu."

Princip hovoří o tom, že bychom měli navrhovat třídy tak, aby se volající strana nemusela dotazovat na záležitosti týkající se vnitřního stavu volané třídy.

Ukázka porušení TDA

Mějme zjednodušený autobusový rezervační systém:

class Bus
{
    private int numberOfSeats;
    private IList<Passenger> passengers = new List<Passenger>();

    public Bus(int numberOfSeats)
    {
        this.numberOfSeats = numberOfSeats;
    }

    public bool HasFreeSeat
    {
        get
        {
            return numberOfSeats > passengers.Count;
        }
    }

    public void AddPassenger(Passenger passenger)
    {
        passengers.Add(passenger);
    }
}

class BusReservation
{
    public void AddPassengerToBus(Bus bus, Passenger passenger)
    {
        if (bus.HasFreeSeat)
        {
            bus.AddPassenger(passenger);
        }
    }
}

Proč je uvedené řešení problematické:

  • Základním principem OOP je zapouzdřenost interních dat objektu a jeho chování. Třída Bus však zbytečně zveřejňuje informaci o volných sedadlech a nutí volající stranu, aby s touto informací pracovala a zohledňovala ji před voláním metody AddPassenger.
  • Třída BusReservation je závislá na třídě Bus na dvou místech. Dotazováním na vlastnost HasFreeSeat a voláním metody AddPassenger. Každá nadbytečná závislost mezi třídami zvyšuje komplexitu návrhu a zhoršuje vlastnosti systému.
  • Třída Bus nutí volající stranu, aby měla znalost o tom, že před zavoláním AddPassenger si musí nejprve sama ověřit splnění kontraktů přidání cestujícího. S tím souvisí i závislost na správném pořadí volání. Samostatné volání HasFreeSeat nedává smysl.
  • Co když se v budoucnu změní podmínky, za kterých je možné přidat cestujícího? Nyní je přidání závislé pouze na volném sedadle, ale nově může přibýt podmínka typu "je autobus pojízdný". Pak bude nutné doplnit test na vlastnost IsMobile na všechna místa volání AddPassenger.
  • Každá nadbytečná závislost zvyšuje komplexitu jednotkových testů. Pokud chceme testovat třídu BusReservation v izolaci, musíme mockovat třídu Bus (nebo lépe rozhraní). V našem problematickém případě musíte přidat chování mocku pro vlastnost HasFreeSeats a metodu AddPassenger.

Vhodnější řešení

Odstraníme závislost volající třídy na dotazování se na volné sedadlo. Veškeré testy na proveditelnost akce přidání cestujícího jsou zapouzdřeny uvnitř metody AddPassenger. Pokud některá z podmínek není splněna, je vrácena výjimka, kterou zpracuje volající strana. Volající straně se situace zjednoduší.

class Bus
{
    private int numberOfSeats;
    private IList<Passenger> passengers = new List<Passenger>();

    public Bus(int numberOfSeats)
    {
        this.numberOfSeats = numberOfSeats;
    }

    private bool HasFreeSeat
    {
        get
        {
            return numberOfSeats > passengers.Count;
        }
    }

    public void AddPassenger(Passenger passenger)
    {
        bool isFull = !HasFreeSeat;

        if (isFull)
        {
            throw new Exception("Bus is full.");
        }

        passengers.Add(passenger);
    }
}

class BusReservation
{
    public void AddPassengerToBus(Bus bus, Passenger passenger)
    {
        bus.AddPassenger(passenger);
    }
}

Vezměte si na pomoc Adapter

Pokud využíváte rozhraní, které nemůžete měnit (komponenta třetí strany) a které porušuje TDA, můžete si pomoci návrhovým vzorem Adapter (Wrapper). Např. v jazyce C# je za příkazem foreach schován adaptér, který zjednodušuje práci se vším, co zveřejňuje metodu IEnumerator GetEnumerator(). Z IEnumerator pak postupně volá metodu bool MoveNext() a dotazuje se na vlastnost object Current { get; }. Více na stackoverflow.com.

Závěrem

Tak jako většina principů a doporučení objektového návrhu, tak i TDA není možné bez rozmyslu aplikovat dogmaticky ve všech situacích. Záleží na zodpovědnosti navrhované třídy, zda-li poskytuje příkazy (commands) k vykonání nějaké akce nebo dotazy (queries) vracející vnitřní stavy objektu. V případě příkazů byste však měli o dodržování principu TDA usilovat.

pondělí 9. července 2012

Peníze až na čtvrtém místě

Richard Florida ve své knize The Rise of the Creative Class zveřejnil zajímavé výsledky výzkumu o motivování lidí. Více jak 20 000 pracovníků z IT mělo z celkem 38 různých faktorů vybrat ty, které je nejvíce ovlivňují a motivují v jejich práci. Top 10 vypadá takto:

  1. Náročnost úkolů a zodpovědnost
  2. Flexibilita
  3. Stabilní pracovní prostředí
  4. Peníze
  5. Profesní rozvoj
  6. Uznání kolegů
  7. Motivující spolupracovníci a nadřízení
  8. Vzušující náplň práce
  9. Firemní kultura
  10. Umístění a komunita

Pokud na výsledky pohlížíte z perspektivy zaměstnance, snáze pochopíte, v jakém prostředí může vyniknout Váš kreativní potenciál. Máte-li na výběr, hledejte firmu, která nabízí zajištění co nejvíce motivátorů ze seznamu. Pokud ve Vaší stávající firmě není splněna většina věcí ze začátku seznamu, měli byste se vážně zamyslet, zda-li v takové firmě chcete nadále ztrácet svůj čas.

Průzkum by měl být přínosný také pro zaměstnavatele a vedoucí týmů. Pokud chtějí z lidí dostat maximum a zajistit jejich spokojený pracovní život, měli by realizovat opatření, která napomáhají motivovat. Nerespektování těchto faktů obvykle zabíjí inovaci, zvyšuje fluktaci lidí a v konečném důsledku snižuje úspěšnost firmy.

Florida nám předkládá potvrzení faktu, že peníze nejsou dostatečným dlouhodobým motivátorem u kreativních lidí. V určité fázi pracovního vývoje totiž můžete zjistit, že přestože vyděláváte slušný balík, nejste spokojeni. Dostali jste se zřejmě na úroveň, kdy základní potřeby včetně finančních jsou uspokojeny, ale potřebujete navíc uspokojit Vaše seberealizační potřeby nejvyšší úrovně (viz. 4. a 5. patro Maslowovy pyramidy potřeb).

Pokud Vás zajímá problematika podpory kreativity v současné éře inovativní ekonomiky, můžete si přečíst knížku Nepostradatelní od Jan Melvil publishing. Seth Godin na mně působil zpočátku trochu jako otravný provokatér, ale s postupem času jsem na jeho hru přistoupil a začalo se mi to líbit. Doporučuji.