it-swarm-eu.dev

Co bych měl zvážit, když jsou principy DRY a KISS) nekompatibilní?

princip DRY někdy nutí programátory psát složité, obtížně udržovatelné funkce/třídy. Takový kód má tendenci se v průběhu času stávat složitější a obtížnější ho udržovat. Porušení princip KISS .

Například když více funkcí potřebuje něco podobného. Obvyklé řešení DRY ========================================================================.

Vzhůru nohama je zřejmé, DRY = jedno místo pro provedení změn atd.).

Nevýhodou a důvodem, proč to porušuje KISS) je to, že funkce jako jsou tyto mají tendenci se v průběhu času stávat komplexnějšími s čím dál tím více parametry. Nakonec se programátoři velmi bojí provádět jakékoli změny těchto funkcí nebo způsobí chyby v jiných případech použití funkce.

Osobně si myslím, že má smysl porušovat zásadu DRY, aby bylo dodrženo KISS).

Raději bych měl 10 super jednoduchých funkcí, které jsou podobné, než mít jednu super komplexní funkci.

Raději bych udělal něco únavného, ​​ale snadného (provést stejnou změnu nebo podobnou změnu na 10 místech), než provést velmi děsivou/obtížnou změnu na jednom místě.

Ideálním způsobem je, jak to udělat, KISS jak je to možné, aniž by došlo k porušení DRY.) Někdy se však zdá nemožné.

Jedna otázka, která se objeví, je „jak často se tento kód mění?“ což znamená, že pokud se často mění, je důležitější, aby byl SUCHÝ. Nesouhlasím, protože změnou této jedné komplexní funkce DRY======================================================================================.

Takže v zásadě si myslím, že obecně KISS>> SUCHÉ).

Co myslíš? V jakých případech si myslíte, že by DRY) mělo vždy vyhrát nad KISS a naopak? Co byste při rozhodování zvažovali? Jak se této situaci vyhnete?

71
user158443

KISS je subjektivní. DRY se snadno aplikuje. Oba mají za sebou dobré nápady, ale obě se snadno zneužívají. Klíčem je rovnováha.

KISS je opravdu v oku vašeho týmu. Nevíte, co KISS je. Váš tým ano. Ukažte jim svou práci a zjistěte, zda si myslí, že je to jednoduché. Jste o tom špatný soudce, protože už víte, jak to funguje. Zjistěte, jak těžký je váš kód pro ostatní.

DRY není o tom, jak váš kód vypadá. Nemůžete najít skutečné DRY problémy hledáním identického kódu.) Skutečným DRY problém může být, že řešíte stejný problém s úplně jiným pohledem kód na jiném místě. Neporušujete DRY, když používáte stejný kód k vyřešení jiného problému na jiném místě. Proč? Protože různé problémy se mohou nezávisle měnit. Nyní je třeba změnit a druhý ne.

Dělejte rozhodnutí o designu na jednom místě. Nerozšiřujte rozhodnutí. Ale neukládejte každé rozhodnutí, které se zdá, vypadat stejně hned teď na stejné místo. Je v pořádku mít x a y, i když jsou oba nastaveny na 1.

S touto perspektivou nikdy nedávám KISS nebo DRY nad druhou). Nevidím mezi nimi téměř napětí. Chráním se před zneužitím To jsou oba důležité principy, ale ani stříbrná střela.

144
candied_orange

O tom jsem psal již v komentář k další odpověď kandovaným_orange na podobná otázka a také jsem se ho poněkud dotkl v jiná odpověď , ale opakuje se:

DRY je roztomilá třípísmenná zkratka pro mnemotechnickou pomůcku „Don't Repeat Yourself“, která byla vytvořena v knize Pragmatický programátor , kde je celá část stránky 8,5 . Má také vícestránkové vysvětlení a diskusi na wiki .

Definice v knize je následující:

Každá znalost musí mít v systému jednu, jednoznačnou a autoritativní reprezentaci.

Upozorňujeme, že důrazně se jedná o ne o odstranění duplikace. Jedná se o identifikace který z duplikátů je kanonický. Pokud máte například mezipaměť, bude mezipaměť obsahovat hodnoty, které jsou duplikáty něčeho jiného. Nicméně, musí být jasně řečeno, že cache je ne kanonickým zdrojem.

Princip je ne tři písmena SUCHÉ. Je to těch asi 20 stránek v knize a wiki.

Princip je také úzce spjat s OAOO, což není tak roztomilá čtyřpísmenná zkratka pro „Once And Only Once“, což je zase princip eXtreme Programmingu, který má vícestránkové vysvětlení a diskuse) na wiki .

Na wiki stránce OAOO je velmi zajímavý citát od Ron Jeffries:

Jednou jsem viděl, jak Beck prohlásil dvě záplaty téměř úplně odlišného kódu za „duplikování“, změňte je tak, aby byly duplikáty, a pak odstraňte nově vloženou duplikaci, aby přišla s něčím zjevně lepším.

Na kterém rozpracovává:

Vzpomínám si, jak jsem jednou viděl, jak se Beck podíval na dvě smyčky, které byly docela odlišné: měly odlišné struktury a odlišný obsah, což je skoro nic duplikované, kromě slova „pro“ a skutečnosti, že opakovaly - jinak - přes to samé sbírka.

Změnil druhou smyčku na smyčku stejným způsobem jako ta první. To vyžadovalo změnu těla smyčky pro přeskočení položek ke konci kolekce, protože předchozí verze dělala pouze přední část kolekce. Teď pro prohlášení byla stejná. „Dobře, musíš tuto duplicitu odstranit, řekl, a přemístil druhé tělo do první smyčky a druhou smyčku úplně smazal.

Nyní měl v jedné smyčce dva druhy podobného zpracování. Našel tam nějaký druh duplikace, extrahoval metodu, udělal pár dalších věcí a voila! kód byl mnohem lepší.

Tento první krok - vytvoření duplikace - byl překvapující.

To ukazuje: můžete mít duplicitu bez duplicitního kódu!

A kniha ukazuje na druhou stranu mince:

V rámci online aplikace pro objednávání vína zachycujete a ověřujete věk uživatele spolu s množstvím, které si objednávají. Podle vlastníka webu by oba měli být čísla a obě větší než nula. Takže kódujete ověření:

def validate_age(value):
 validate_type(value, :integer)
 validate_min_integer(value, 0)

def validate_quantity(value):
 validate_type(value, :integer)
 validate_min_integer(value, 0)

Během kontroly kódu rezidentní know-all tento kód odrazí a tvrdí, že jde o porušení DRY): obě funkční těla jsou stejná.

Oni se mýlí. Kód je stejný, ale znalosti, které představují, se liší. Obě funkce potvrzují dvě oddělené věci, které prostě mají stejná pravidla. To je náhoda, ne duplicita.

Toto je duplicitní kód, který není zdvojováním znalostí.

Existuje velká anekdota o duplikaci, která vede k hlubokému nahlédnutí do podstaty programovacích jazyků: mnoho programátorů zná programovací jazyk Schéma a že je procedurálním jazykem v rodině LISP s prvotřídními a procedury vyššího řádu, lexikální rozsah, lexikální uzavření a zaměření na čistě funkční, referenčně transparentní kódové a datové struktury. Co však mnoho lidí neví, je to, že bylo vytvořeno za účelem studia objektově orientovaného programování a herních systémů (které autoři schématu považovali za úzce související, ne-li totéž).

Dva ze základních procedur ve schématu jsou lambda, který vytváří proceduru, a apply, který provádí proceduru. Tvůrci Schéma přidali další dva: alpha, který vytvoří a ctor (nebo objekt) a send , který odešle zprávu herci (nebo objektu).

Otravným důsledkem toho, že apply a send bylo, že elegantní syntaxe pro volání procedur již nefungovala. Ve schématu, jak ho známe dnes (a téměř v každém LISP), je jednoduchý seznam obvykle interpretován jako „interpretovat první prvek seznamu jako proceduru a apply do zbytku seznamu, interpretovat jako argumenty ". Takže můžete psát

(+ 2 3)

a to je ekvivalentní

(apply '+ '(2 3))

(Nebo něco blízkého, moje schéma je docela rezavé.)

Toto však již nefunguje, protože nevíte, zda apply nebo send (za předpokladu, že nechcete upřednostňovat jeden ze dvou, které tvůrci Schéma neučinili) Chtěli, aby obě paradigma byla stejná). … Nebo, co? Tvůrci Schéma si uvědomili, že ve skutečnosti musí jednoduše zkontrolovat typ objektu, na který se odkazuje symbol: if + je postup, vy apply, pokud + je herec, vy mu send posíláte zprávu. Ve skutečnosti nepotřebujete oddělené apply a send, můžete mít něco jako apply-or-send.

A to udělali: vzali kód dvou procedur apply a send a vložili je do stejného postupu jako dvě věty podmíněného.

Krátce nato také přepsali tlumočníka schématu, který byl do té doby psán ve velmi nízkoúrovňovém montážním jazyce pro převod registrů pro registrační stroj, ve schématu na vysoké úrovni. A všimli si něčeho úžasného: kód ve dvou větvích podmíněného stal se totožný. To si předtím nevšimli: tyto dva postupy byly napsány v různých časech (začaly s „minimálním LISP“ a poté přidaly OO)) a výřečnost a nízká úroveň shromáždění znamenalo, že byly skutečně psány docela jinak, ale po jejich přepsání v jazyce na vysoké úrovni bylo jasné, že udělali totéž.

To vedlo k hlubokému porozumění Actors a OO: provádění objektově orientovaného programu a provádění programu v procedurálním jazyce s lexikálními uzávěry a správným koncovým voláním, jsou stejná věc. Jediným rozdílem je, zda primitivy vašeho jazyka jsou objekty/aktéři nebo procedury. Ale operačně, je to stejné.

To také vede k další důležité realizaci, která bohužel ještě není dobře pochopena ani dnes: nemůžete udržovat objektově orientovanou abstrakci bez řádného volání ocasem, nebo řečeno agresivněji: jazyk, který prohlašuje, že je objektově orientovaný, ale nemá správné ocasní volání , není objektově orientovaný. (Bohužel to platí pro všechny mé oblíbené jazyky a není to akademické: Já mít ​​narazit na tento problém, že jsem musel přerušit zapouzdření, abych se vyhnul přetečení zásobníku.)

Toto je příklad, kdy velmi dobře skrytá duplikace zakrytá důležitá část znalostí, a objevování tato duplikace také odhalila znalosti.

39
Jörg W Mittag

V případě pochybností vždy vyberte nejjednodušší možné řešení, které problém vyřeší.

Pokud se ukáže, že jednoduché řešení bylo příliš jednoduché, lze jej snadno změnit. Příliš složité řešení na druhé straně je také obtížnější a riskantnější změnit.

KISS je opravdu nejdůležitější ze všech principů designu, ale často se přehlíží, protože naše vývojářská kultura klade velký důraz na to, aby byl chytrý a používal fantastické techniky. Ale někdy je if opravdu lepší než strategický vzorec .

Princip DRY) někdy nutí programátory psát složité, obtížně udržovatelné funkce/třídy.

Zastavte se přímo zde! Účelem principu DRY) je získat více spravovatelného kódu. Pokud by použití zásady v konkrétním případě vedlo na méně udržovatelný kód, pak by se tento princip neměl použít.

Mějte na paměti, že žádný z těchto principů není sám o sobě cílem. Cílem je vytvořit software, který splňuje svůj účel a který lze v případě potřeby upravit, rozšířit a rozšířit. KISS, DRY, SOLID a všechny ostatní principy jsou prostředky k dosažení tohoto cíle. Všechny však mají svá omezení a lze je aplikovat tak, aby fungovaly v rozporu s konečným cílem, kterým je psaní funkčního a udržovatelného softwaru.

8
JacquesB

IMHO: Pokud se přestanete soustředit na kód KISS/DRY a začnete se soustředit na požadavky, které kód řídí, najdete tu lepší odpověď, kterou hledáte.

Věřím:

  1. Musíme se navzájem povzbuzovat, aby zůstali pragmatičtí (jak to děláte)

  2. Nikdy nesmíme přestat podporovat důležitost testování

  3. Zaměření na požadavky více vyřeší vaše otázky.

TLDR

Pokud je vaším požadavkem, aby se součásti měnily nezávisle, udržujte funkce nezávislé tím, že nemáte pomocné funkce. Pokud jsou vaše požadavky (a jakékoli budoucí změny) stejné pro všechny funkce, přesuňte tuto logiku do pomocné funkce.

Myslím, že všechny naše dosavadní odpovědi vytvářejí Vennův diagram: my všichni říkáme to samé, ale podrobnosti dáváme do různých částí.

Také nikdo jiný nezmínil testování, což je částečně důvod, proč jsem napsal tuto odpověď. Myslím, že pokud se někdo zmíní, že se programátoři bojí dělat změny, pak je velmi nerozumné ne mluvit o testování! I když si myslíme, že problém je o kódu, může být skutečným problémem nedostatek testování. Objektivně lepší rozhodnutí se stanou realističtějšími, když lidé nejprve investovali do automatizovaného testování.

Za prvé, vyhýbat se strachu je moudrost - dobrá práce!

Zde je věta, kterou jste řekli: programátoři se velmi bojí provádět jakékoli změny těchto [pomocných] funkcí nebo způsobí chyby v jiných případech použití funkce

Souhlasím s tím, že tento strach je nepřítel, a musíte se nikdy držet zásad, pokud způsobují pouze strach z kaskádových chyb/práce/změn. Pokud je kopírování/vkládání mezi více funkcemi pouze způsobem, jak odstranit tento strach (což nevěřím - viz níže), pak byste to měli udělat.

Skutečnost, že cítíte tento strach ze změn a že se o to pokoušíte, z vás dělá lepšího profesionála než mnoho jiných, kteří se nezajímají o vylepšování kódu - prostě dělají to, co jim bylo řečeno a provést holé minimální změny, aby byla jejich jízdenka uzavřena.

Také (a mohu říci, že opakuji to, co již víte): dovednosti lidí trumf designové dovednosti. Pokud jsou skuteční lidé ve vaší společnosti naprosto špatní, nezáleží na tom, zda je vaše „teorie“ lepší. Možná budete muset učinit rozhodnutí, která jsou objektivně horší, ale víte, že lidé, kteří to budou udržovat, jsou schopni porozumět a pracovat s nimi. Mnozí z nás také rozumějí managementu, který (IMO) nás mikromanaguje a hledá způsoby, jak vždy odmítnout potřebné refaktoring.

Jako někdo, kdo je prodejcem, který píše kód pro zákazníky, musím na to neustále myslet. Možná bych chtěl použít kari a meta-programování, protože existuje argument, že je to objektivně lepší, ale v reálném životě vidím lidi, kteří jsou tímto kódem zmateni, protože to není vizuálně zřejmé co se děje.

Za druhé, lepší testování řeší více problémů najednou

Pokud (a pouze pokud) máte účinné, stabilní a osvědčené automatizované testy (jednotky a/nebo integrace), pak se vsadím, že uvidíte strach zmizet. Pro nováčky v automatizovaných testech může být velmi děsivé důvěřovat automatizovaným testům; nováčci mohou vidět všechny tyto zelené tečky a mají jen velmi malou důvěru, že tyto zelené tečky odrážejí produkci skutečného života. Pokud však vy osobně důvěřujete automatizovaným testům, můžete začít emotivně/relačně povzbuzovat ostatní, aby mu také důvěřovali.

Pro vás (pokud jste tak již neučinili) je prvním krokem zkoumat zkušební postupy, pokud tak ještě nemáte. Upřímně předpokládám, že už tyto věci znáte, ale protože jsem to ve vašem původním příspěvku neviděl, musím o tom mluvit. Protože automatické testy jsou tak důležité a relevantní pro vaši situaci, kterou jste předložili.

Nebudu se snažit jednou rukou zrušit všechny testovací postupy na jednom příspěvku, ale vyzval bych vás, abyste se zaměřili na myšlenku testů „odolných proti refaktorům“. Než provedete kód jednotky/integrace, zeptejte se sami sebe, zda existují platné způsoby, jak změnit CUT (testovaný kód), který by přerušil test, který jste právě napsali. Pokud je to pravda, pak (IMO) tento test smažte. Je lepší mít méně automatizovaných testů, které se zbytečně nezlomí, když refaktorujete, než aby vám něco řeklo, že máte vysoké pokrytí testem (kvalita před kvantitou). Koneckonců, snazší refaktoring je (IMO) hlavním cílem automatizovaných testů.

Když jsem postupem času přijala tuto filozofii „odolnou proti refaktorům“, dospěla jsem k následujícím závěrům:

  1. Automatizované integrační testy jsou lepší než testy jednotek
  2. Pro integrační testy, pokud potřebujete, napište "simulátory/padělky" s "kontrakty"
  3. Nikdy netestujte soukromé API - ať už jde o metody soukromé třídy nebo neexportované funkce ze souboru.

Reference:

Zatímco zkoušíte testovací postupy, možná budete muset udělat více času na napsání těchto testů sami. Jediným nejlepším přístupem je někdy neříkat nikomu, že to děláte, protože vás mikromanažují. Samozřejmě to není vždy možné, protože množství potřeby testování může být větší než potřeba dobré rovnováhy mezi prací a životem. Ale někdy jsou věci dostatečně malé, abyste se mohli vyhnout tajnému zdržení úkolu o den nebo dva, abyste mohli jednoduše napsat potřebné testy/kód. Vím, že to může být kontroverzní prohlášení, ale myslím si, že je to realita.

Kromě toho můžete samozřejmě být tak politicky obezřetní, jak můžete, abyste povzbudili ostatní, aby sami podnikli kroky k porozumění/psaní testů. Nebo možná jste technický vedoucí, který může uložit nové pravidlo pro kontrolu kódu.

Když hovoříte o testování se svými kolegy, doufejme, že výše uvedený bod č. 1 (být pragmatický) nám všem připomíná, abychom nejprve poslouchali, a ne aby byli hanební.

Za třetí, Zaměřte se na požadavky, ne na kodex

Příliš mnohokrát se zaměřujeme na náš kód a nerozumíme hlubšímu obrazu, který má náš kód řešit! Někdy musíte přestat hádat o tom, zda je kód čistý, a začít se ujistit, že dobře rozumíte požadavkům, které mají kód řídit.

Je důležitější, abyste udělali správnou věc, než že máte pocit, že váš kód je „hezký“ podle nápadů jako KISS/DRY. To je důvod, proč váhám, abych se o tyhle fráze staral, protože (v praxi) náhodou nutí vás soustředit se na váš kód, aniž by přemýšleli o tom, že požadavky jsou to, co poskytuje dobrý úsudek o dobré kvalitě kódu.


Pokud jsou požadavky dvou funkcí vzájemně závislé/stejné, vložte logiku implementace tohoto požadavku do pomocné funkce. Vstupy do této pomocné funkce budou vstupy do obchodní logiky pro tento požadavek.

Pokud jsou požadavky funkcí odlišné, zkopírujte/vložte mezi ně. Pokud by oba měli tentokrát stejný kód, ale by se mohl správně změnit nezávisle, pak pomocná funkce je špatná , protože je to ovlivňující jinou funkci, jejíž požadavek se má měnit nezávisle.

Příklad 1: Máte funkci nazvanou "getReportForCustomerX" a "getReportForCustomerY" a oba dotazují databázi stejným způsobem. Předpokládejme také, že existuje obchodní požadavek, kdy si každý zákazník může svou zprávu přizpůsobit doslova jakýmkoli způsobem. V tomto případě záměrně požadují zákazníci ve své zprávě jiná čísla. Pokud tedy máte nového zákazníka Z, který potřebuje sestavu, může být nejlepší zkopírovat/vložit dotaz od jiného zákazníka a poté zadat kód a přesunout ho. I když jsou dotazy přesně stejné, definiční bod těchto funkcí je oddělit změny od jednoho zákazníka ovlivňujícího jiného. V případech, kdy poskytnete novou funkci, kterou budou všichni zákazníci chtít ve své zprávě, pak ano: pravděpodobně budete psát stejné změny mezi všemi funkcemi.

Řekněme však, že se rozhodneme pokračovat a vytvoříme pomocnou funkci nazvanou queryData. Důvod, proč je to špatné, je proto, že bude více kaskádových změn zavedením pomocné funkce. Pokud je ve vašem dotazu klauzule „where“, která je stejná pro všechny zákazníky, pak jakmile jeden zákazník chce, aby se pole pro ně lišilo, pak místo 1) změna dotazu uvnitř funkce X, musíte 1 ) změňte dotaz na to, co chce zákazník X) 2) do dotazu přidejte podmínky , aby to pro ostatní neudělal. Přidání dalších podmínek do dotazu je logicky odlišné. Možná budu vědět, jak do dotazu přidat dílčí článek, ale to neznamená, že vím, jak učinit tento díl podmíněný, aniž by to ovlivnilo výkon těch, kteří jej nepoužívají.

Takže si všimnete, že použití pomocné funkce vyžaduje dvě změny namísto jedné. Vím, že je to vymyšlený příklad, ale podle mých zkušeností booleovská složitost udržovat roste více než lineárně. Proto se akt přidávání podmíněných hodnot považuje za „jednu další věc“, o kterou se lidé musí starat, a „jednu další věc“, která se pokaždé aktualizuje.

Tento příklad, který mi připadá, by mohl být jako situace, do které narazíte. Někteří lidé emocionálně krčí myšlenku kopírování/vkládání mezi těmito funkcemi a taková emoční reakce je v pořádku. Zásada „minimalizace kaskádových změn“ však objektivně rozpozná výjimky, kdy je kopírování/vkládání v pořádku.

Příklad 2: Máte tři různé zákazníky, ale jediná věc, kterou můžete mezi jejich přehledy odlišit, jsou názvy sloupců. Všimněte si, že tato situace je velmi odlišná. Naším obchodním požadavkem již není „poskytovat hodnotu zákazníkovi tím, že v sestavě umožní flexibilitu konkurovat“. Místo toho je obchodním požadavkem „vyvarujte se nadbytečné práce tím, že nedovolíte zákazníkům přizpůsobit přehled příliš“. V této situaci je jedinou možností, kdy byste kdykoli změnili logiku dotazu, kdy musíte také zajistit, aby každý jiný zákazník obdržel stejnou změnu. V tomto případě určitě chcete vytvořit pomocnou funkci s jedním polem jako vstupem - jaké jsou „tituly“ pro sloupce.

Pokud se majitelé produktů v budoucnu rozhodnou, že chtějí zákazníkům umožnit přizpůsobit si něco ohledně dotazu, přidáte do pomocné funkce další příznaky.

Závěr

Čím více se zaměříte na požadavky místo na kód, tím více bude kód izomorfní k doslovným požadavkům. přirozeně píšete lepší kód.

4
Alexander Bird

Pokuste se najít rozumnou střední půdu. Spíše než jedna funkce se spoustou parametrů a složitých podmíněností rozptýlených po celé, rozdělte ji na několik jednodušších funkcí. V volajících se bude opakovat, ale ne tolik, jako byste na prvním místě nepřesunuli společný kód na funkce.

Nedávno jsem se s tím setkal s nějakým kódem, na kterém pracuji na rozhraní s prodejnami aplikací Google a iTunes. Hodně z obecného toku je stejné, ale existuje dost rozdílů, že jsem nemohl snadno napsat jednu funkci, která by zapouzdřila všechno.

Kód je tedy strukturován takto:

Google::validate_receipt(...)
    f1(...)
    f2(...)
    some google-specific code
    f3(...)

iTunes::validate_receipt(...)
    some iTunes-specific code
    f1(...)
    f2(...)
    more iTunes-specific code
    f3(...)

Neobávám se příliš starostí, že volání f1 () a f2 () v obou validačních funkcích porušuje princip DRY), protože jejich kombinací by to bylo komplikovanější a neprovádělo by jediný, dobře definovaný úkol.

3
Barmar

Kent Beck prosazoval 4 pravidla jednoduchého designu, která se vztahují k této otázce. Podle slov Martina Fowlera jsou to:

  • Úspěšné testy
  • Odhaluje záměr
  • Žádné zdvojení
  • Nejméně prvků

O uspořádání středních dvou se hodně diskutuje, takže by o nich mohlo být užitečné myslet stejně jako o stejně důležitých.

DRY je třetí prvek v seznamu a KISS) lze považovat za kombinaci 2. a 4., nebo dokonce celého seznamu dohromady.

Tento seznam poskytuje alternativní pohled na DRY/KISS dichotomii. Odhaluje váš DRY kód) záměr? Je váš KISS kód?) Můžete vylepšit nebo méně duplikovat éterovou verzi?

Cílem není DRY nebo KISS, je to dobrý kód. DRY, KISS a tato pravidla jsou pouhými nástroji, jak se tam dostat.

3
Blaise Pascal