Code Review s Architektem, Round #1 - Návrh interface rubrika: Programování: .Net

2 Jaroslav Urban
položil/-a 25.12.2013
 
upravil/-a 26.12.2013

Pracujeme pro jednu firmu, kde se podílíme na vývoji jejich appky a jako součást iterace děláme vetší code review s SW Architektem. Na některých věcech se úplně neshodneme, na tom není ovšem nic špatného. Někdy mi však chybí smysluplné argumenty, na obouch stranách. Tak alespoň ne té mé bych s tím rád něco udělal. :-) Rád bych Vás tedy poprosil o Váš názor.

Zkusím časem přidávat další věci, které mě straší ve věži a třeba z toho vznikne pěkný seriál. Prosím zkuste k danému tématu přistupovat z rozmyslem a chladnou hlavou, ať z toho nemáme nějaký flameware. Stále se učím, tak mi prosím odpuste, že se sem tam objeví dirka v mých znalostech. Je to dáno nedokonalostí mého systému. Děkuji.

Budiž příklad z praxe, Round #1:

  • implementace pro ovládání kamery, která lze zapnout, vypnout, zoom in, zoom out, nastavit 4 hodnoty pro WhiteBalance, 3 hodnoty pro kontrast
  • limity: Net 4.0, WPF app, Castle Windsor, distribuovaný tým(DE, CZ), různý level znalostí (Jr,Sr).

Vedli jsme diskuzi, jak navrhnout interface pro přístup k výše definované funkcionalitě. Vcelku triviální téma, ale různé návrhy můžou značně změnit implementaci. Takže jaké jsou vaše argumenty pro a proti pro níže popsané konstrukty? Případně, přidali byste nějaký další?

1) metoda per action

public interface ICameraController{
    void SwitchOff();
    void SwitchOn();
    void ZoomIn();
    void ZoomOut();
    ...
}

2) Metody s argumenty

public interface ICameraController{
    void CameraEnabled(bool isEnabled);
    void Zoom(ZoomActionType zoomType );
    ...
}

3) Generic + commands

    public interface ICameraController
    {
        void RunCommand<TCommand>(TCommand command) where TCommand : ICameraCommand;
    }

Odpověd: Začnu od sebe, ať máte mustr.
Ad #1
Pro: Jednoduchost a přímočaré použití i pro začátečníka. Není nutné validovat žádné vstupní proměnné.
Proti: Mnoho metod. Při rozšíření funkčnosti nutné změnit interface. Duplikace kódu.
Ad #2
Pro: Stále jednoduché a pochopitelné pro začátečníka. Méně metod a méně kódu; Né pro všechny změny je nutno měnit interface, stačí třeba rozšíření enumerace vstupního parametru.
Proti: Pro některé typy nutná validace vstupní hodnoty, alespoň na null nebo invalid. Některé změny vyžadují změnu interface.

  • v tomto řešení vidím minimální přínos, osobně bych preferoval řešení 1 oproti 2.
    Ad #3
    Pro: Jednoduše rozšiřitelné. Lze využít immutable command, které lze třeba použít až k samotnému zařízení.
    Proti: Komplexní řešení vyžadující mnoho tříd. Težší na použití. Nutno vytvářet commandy. Začátečník může mít problémy pochopit plně implementaci a vidět všechny relevantní podpůrné třídy (validace, factory, etc).
  • Jo, je to určitě více rozšířitelné a univerzálnější, ale kódu napíši furt stejně. Navíc o dost komplexnejší použití.

Teším se na vaše plnohodnotné názory a případné návrhy, jak k danému problém přistupovat. Děkuji.

Pár otázek navíc k zamyšlení?
1) Kde je hranice, kdy interface má již mnoho metod? 10,20,50?
2) Jaký je argument pro použití proměnných oproti jednotlivým metodám?

Update

  • trochu více k tomu kde zhruba je ten interface použit. Jde o interface , který je použit v UI vrstvě v WPF appce postavené na MVVM, takže v modelu. V podstatě je to Fire and Forget implementace, alespoň v tom controlleru. Jelikož na tom dělá ale tým v DE, tak jsme jim to chtěli udělat co nejjednodušší. Proto jsme na začátek volili #1. Během review jsme ale řešili, že je tam dost duplicit a že by to šlo zjednodušit do #2. Což se mi mo moc nelibilo, ale předělávat to do #3 je dost práce na víc. Nic proti tomu, jen jsem si chtěl potvrdit, že to má smysl. Do budoucna se tento scénář jistě bude opakovat a tak podle mě má smysl kouknout kolem a vyřešit to pořádně.
  • Aleš nastínil zajímavé řešení v jednom komentáři, tak ho snad z něj vytáhnu.
odkaz
Anonym
odpověděl/-a 26.12.2013

Návrh by měl odpovídat požadavkům na systém. V tomto případě se jedná o ovládání zařízení napojených na HW sběrnici s vlastním protokolem. Ovládat se dají kamery a světla používaná na operačním sále (OR lights).

Stávající návrh obsahuje nízkoúrovňovou komponentu pro provedení příkazů a fasády pro každý typ zařízení.

Komponenta zajišťující vykonání příkazu má rozhraní podobné tomuto:

public interface IEquipmentController
{
    void Run(IEquipmentCommand command);
}

IEquipmentCommand obsahuje vše potřebné pro sestavení messages, které se posílají na HW sběrnici. Trochu si pomáháme reflexí pro přečtení mapovacích atributů, ale není to nic násilného. Generiku nepotřebujeme.

Pro potřeby UI (WPF, MVVM) jsme navrhli fasády, které jsou specifické pro dané moduly (cameras, OR lights). Jejich účelem je odstínit view-model od obecného IEquipmentController a jednoznačně definovat způsob použití controllingu daného zařízení. Rozhraní fasád odpovídá nejvíce #1. Souhlasím s připomínkami, že takové rozhraní je příliš velké a nesoudržné. Nabízí se možnost rozpadu do menších rozhraní pro logické skupiny akcí, např.:

public interface ICameraSwitchController
{
    void SwitchOn();
    void SwitchOff();
}
 
public interface ICameraZoomController
{
    void ZoomIn();
    void ZoomOut();
    void ZoomStop();
}
 
public interface ICameraWhiteBalanceController
{
    void SetWhiteBalance(WhiteBalanceType whiteBalanceType);
} 

Návrh jednotlivých rozhraní odpovídá požadavkům na ovládání. Např. White balance je přepínač mezi několika režimy, proto jedna parametrická metoda. Zoom funguje tak, že uživatel stiskne Zoom In a začne se měnit ohnisko, dokud nestiskne Zoom Stop. Jedná se tedy o různé uživatelské akce, proto samostatné metody. Co se týče zapínání a vypínání kamery, tak bych se raději vyhnul bool nebo dvouhodnotovému enumu jako parametru.

Za fasádami je ukryto vyvolání příslušné metody z IEquipmentCommandFactory, které vrací typovou instanci IEquipmentCommand. Kvůli testovatelnosti se snažíme vyhýbat použití "new" pro command mimo factory class. Instance se pošle do IEquipmentController.Run().

Komponenta pro provedení obecného příkazu zapouzdřuje WCF klientskou vrstvu (aplikace komunikující přímo s HW běží jako služba na stejném nebo vzdáleném počítači) a volání metody služby je asynchronní. UI je tedy dostatečně responzivní. Návratová hodnota nás nezajímá. Ve druhém směru (z HW směrem na UI) je totiž propagován aktuální status všech připojených zařízení při každé změně a následně překreslováno UI. Pokud dojde k provedení commandu a je změněn stav, dozvíme se to asynchronně přes událost s aktuálním stavem. Tyto události jsou filtrovány pro kameru a světla a směrovány do příslušného rozhraní modulu.

No silver bullet. ;)

Pro zobrazení všech 9 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.