it-swarm-eu.dev

Jak byste vnořili refactor IF prohlášení?

Cestoval jsem po programovací blogosféře, když jsem se stal na tomto příspěvku o GOTO:

http://giuliozambon.blogspot.com/2010/12/programmers-tabu.html

Zde spisovatel hovoří o tom, jak „člověk musí dojít k závěru, že existují situace, kdy GOTO vytvářejí čitelnější a lépe udržovatelný kód“, a dále ukazuje příklad podobný tomuto:

if (Check#1)
{
    CodeBlock#1
    if (Check#2)
    {
        CodeBlock#2
        if (Check#3)
        {
            CodeBlock#3
            if (Check#4)
            {
                CodeBlock#4
                if (Check#5)
                {
                    CodeBlock#5
                    if (Check#6)
                    {
                        CodeBlock#6
                        if (Check#7)
                        {
                            CodeBlock#7
                        }
                        else
                        {
                            rest - of - the - program
                        }
                    }
                }
            }
        }
    }
}

Spisovatel poté navrhuje, aby použití GOTO umožnilo tento kód mnohem snadněji číst a udržovat.

Osobně mohu vymyslet alespoň 3 různé způsoby, jak jej vyrovnat a učinit tento kód čitelnějším, aniž by se uchýlil k průlomovým GOTO. Zde jsou moje dvě oblíbené položky.

1 - Vnořené malé funkce. Vezměte každý blok if a jeho kód a proměňte jej v funkci. Pokud booleovský šek selže, stačí se vrátit. Pokud projde, zavolejte další funkci v řetězci. (Chlapče, to zní hodně jako rekurze, můžete to udělat v jedné smyčce s funkčními ukazateli?)

2 - Sentinální proměnná. Pro mě je to nejjednodušší. Stačí použít proměnnou blnContinueProcessing a zkontrolovat, zda je stále ve vaší kontrole if. Pokud kontrola selže, nastavte proměnnou na false.

Kolik různých způsobů lze tento problém s kódováním refaktorizovat, aby se snížilo vnoření a zvýšila se udržovatelnost?

34
saunderl

Je opravdu těžké říci, aniž bychom věděli, jak různé kontroly interagují. Přísné refactorování může být v pořádku. Vytváření topologie objektů, které provádějí správný blok v závislosti na jejich typu, může být také způsobeno strategickým vzorem nebo stavovým vzorcem.

Aniž bych věděl, co dělat nejlépe, zvážil bych dvě možná jednoduchá refactorings, která by mohla být dále refactored extrakcí více metod.

První, který se mi opravdu nelíbí, protože se mi vždy líbí metoda výstupů v malém výstupu (nejlépe jeden)

if (!Check#1)
{ 
    return;
}
CodeBlock#1

if (!Check#2)
{
    return;
}
CodeBlock#2
...

Druhé odebrat je více návratů, ale také přidává spoustu šumu. (v zásadě pouze odstraní vnoření)

bool stillValid = Check#1
if (stillValid)
{
  CodeBlock#1
}

stillValid = stillValid && Check#2
if (stillValid)
{
  CodeBlock#2
}

stillValid = stillValid && Check#3
if (stillValid)
{
  CodeBlock#3
}
...

Tento poslední může být pěkně přepracován do funkcí a když jim dáte dobrá jména, může být výsledek rozumný ';

bool stillValid = DoCheck1AndCodeBlock1()
stillValid = stillValid && DoCheck2AndCodeBlock2()
stillValid = stillValid && DoCheck3AndCodeBlock3()

public bool DoCheck1AndCodeBlock1()
{
   bool returnValid = Check#1
   if (returnValid)
   {
      CodeBlock#1
   }
   return returnValid
}

Celkově vzato jsou pravděpodobně lepší možnosti

33
KeesDijk

Tomu se říká „Šipkový kód“ kvůli tvaru kódu se správným odsazením.

Jeff Atwood měl dobrý blogový příspěvek na Coding Horror o tom, jak vyrovnat šípy:

vyrovnávací kód šipky

Přečtěte si článek pro úplné ošetření, ale zde jsou hlavní body ..

  • Nahraďte podmínky ochrannými doložkami
  • Rozložte podmíněné bloky na samostatné funkce
  • Převeďte negativní kontroly na pozitivní kontroly
  • Vždy příležitostně se co nejdříve vraťte z funkce
40
JohnFx

Vím, že někteří lidé budou argumentovat, že je to goto, ale return; Je zřejmý refaktoring, tj.

if (!Check#1)
{ 
        return;
}
CodeBlock#1
if (!Check#2)
{
    return;
}
CodeBlock#2
.
.
.
if (Check#7)
{
    CodeBlock#7
}
else
{
    rest - of - the - program
}

Pokud je to opravdu jen banda strážných před spuštěním kódu, pak to funguje dobře. Pokud je to komplikovanější než to, bude to jen o něco jednodušší a budete potřebovat jedno z dalších řešení.

26
Richard Gadsden

Tento špagetový kód se jeví jako dokonalý kandidát pro refactoring do státního stroje.

10
Pemdas

Možná to už bylo zmíněno, ale moje odpověď „go-to“ (zamýšlená slovní hříčka) na „antipattern šípu“, která je zde zobrazena, je zvrátit ifs. Otestujte opak aktuálních podmínek (dostatečně snadné u operátora, který není operátorem) a vraťte se k metodě, pokud je to pravda. Žádná gotos (ačkoli pedanticky řečeno, neplatný návrat je o něco víc než skok na linii volajícího kódu, s triviálním krokem navíc vyhodit rámec ze zásobníku).

Příklad: zde je originál:

if (Check#1)
{
    CodeBlock#1
    if (Check#2)
    {
        CodeBlock#2
        if (Check#3)
        {
            CodeBlock#3
            if (Check#4)
            {
                CodeBlock#4
                if (Check#5)
                {
                    CodeBlock#5
                    if (Check#6)
                    {
                        CodeBlock#6
                        if (Check#7)
                        {
                            CodeBlock#7
                        }
                        else
                        {
                            rest - of - the - program
                        }
                    }
                }
            }
        }
    }
}

Refactored:

if (!Check#1) return;

CodeBlock#1

if (!Check#2) return;

CodeBlock#2

if (!Check#3) return;

CodeBlock#3

if (!Check#4) return;

CodeBlock#4

if (!Check#5) return;

CodeBlock#5

if (!Check#6) return;

CodeBlock#6

if (Check#7)
    CodeBlock#7
else
{
    //rest of the program
}

V každém případě v zásadě kontrolujeme, zda bychom měli pokračovat. Funguje přesně stejným způsobem jako originál s pouze jednou úrovní vnoření.

Pokud je něco mimo konec tohoto úryvku, který by měl být také spuštěn, rozbalte tento kód do své vlastní metody a zavolejte jej odkudkoli, kde tento kód aktuálně žije, než přejdete ke kódu, který by po tomto úryvku přišel. Samotný úryvek je dostatečně dlouhý, vzhledem k dostatečné skutečné LOC v každém kódovém bloku, aby ospravedlnil rozdělení několika dalších metod, ale odbočuji.

6
KeithS

Pokud jste běžně dostali logiku, která ve skutečnosti vyžaduje tuto pyramidu kontrol if, pravděpodobně (metaforicky) používáte klíč na kladivo hřebíků. Bylo by lepší, kdybyste udělali takový druh zkroucené a komplikované logiky v jazyce, který podporuje tento druh zkroucené a komplikované logiky s lepšími strukturami než lineární if/else if/else - konstrukty stylů.

Jazyky, které by mohly být vhodnější pro tento druh struktury, mohou zahrnovat SNOBOL4 (se svým bizarním větvením ve stylu dual-GOTO) nebo logické jazyky jako Prolog a Mercury (s jejich sjednocením a zpětným sledováním, nemluvě o jejich DCG pro spíše stručné vyjádření složitých rozhodnutí) .

Samozřejmě, pokud to není možnost (protože většina programátorů je tragicky, nikoli polygloty), nápady, které ostatní přišli, jsou dobré jako použití různých OOP - struktury nebo rozdělit klauzule na funkce nebo dokonce, pokud jste zoufalí, a nevadí vám, že většina lidí je považuje za nečitelná, pomocí státní stroj .

Moje řešení však stále hledá jazyk, který vám umožní vyjádřit logiku, kterou se snažíte vyjádřit (za předpokladu, že je to ve vašem kódu běžné) jednodušším a čitelnějším způsobem.

Od zde :

Poměrně často, když se dívám na soubor pac-manů, zjistím, že když jen načrtnu něco jako tabulku pravdy o všech zúčastněných podmínkách, mohu vypracovat mnohem lepší cestu k vyřešení problému.

Tímto způsobem můžete také posoudit, zda existuje lepší metoda, jak byste ji mohli dále rozebrat a (a to je velký s tímto druhem kódu), zda existují nějaké díry v logice.

Když to uděláte, pravděpodobně to můžete rozdělit na několik příkazů pro přepnutí a několik metod a zachránit dalšího chudáka, který musí projít kódem, spoustu problémů.

1
Jim G.

Osobně se mi líbí zalamovat tyto příkazy if do samostatných funkcí, které v případě úspěšnosti funkce vrátí bool.

Struktura vypadá takto:


if (DoCheck1AndCodeBlock1() && 
    DoCheck2AndCodeBlock2() && 
    DoCheck3AndCodeBlock3()) 
{
   // ... you may perform the final operation here ....
}

Jedinou nevýhodou je, že tyto funkce budou muset vydávat data pomocí atributů předávaných referenčními nebo členskými proměnnými.

1
gogisan

Použití OO) přístupu ke složenému vzoru, v němž je list jednoduchý stav a součást, spojení jednoduchých prvků činí tento druh kódu rozšiřitelným a přizpůsobitelným

1
guiman

Pokud chcete možné řešení orientované na objekt, kód vypadá jako kanonický příklad pro refactorování pomocí návrhový vzor řetězce odpovědnosti .

0
FinnNk

Jejich rozdělení na funkce může pomoci?

Vyvoláte první funkci a po jejím dokončení bude volat další, první věc, kterou by funkce udělala, je otestování kontroly této funkce.

0
Toby

Hodně záleží na velikosti každého kódového bloku a celkové velikosti, ale mám sklon rozdělit se buď přímou "extrakční metodou" na blok, nebo přesunutím nepodmíněných částí do samostatných metod. Ale uvedené výhrady @ KeesDijk platí. První přístup vám dává řadu funkcí, které se vám líbí

if (Check#1)
{
    CodeBlock#1
    NextFunction
}

Což může dobře fungovat, ale vede k nadýmání kódu a proti metodě „metoda používaná pouze jednou“. Obecně tedy preferuji tento přístup:

if (CheckFunctionOne)
{
    MethodOneWithDescriptiveName
    if (CheckFunctionTwo)
    {
        MethodTwoWithDescriptiveName
        ....

Při správném použití soukromých proměnných a předávání parametrů to lze provést. Zde se můžete podívat na gramotné programování.

0
Мסž