Chyby v návrhu: duplikace znalostí

Od Petr Zemek, 2014-12-01

Existuje mnoho druhů duplikace. Nejznámějším typem je duplikování kódu. Mezi další typ patří strukturální duplikace, o kterém jsem psal nedávno (Deméteřin zákon). Dneska se podíváme na další typ: duplikace znalostí a s tím související princip DRY.

(De)motivační příklad

Kód budu psát pro jednoduchost a přehlednost v Pythonu. Hlavní myšlenka je ale na jazyce nezávislá.

Mějme systém pro konverzi dokumentů do PDF formátu. Funguje to tak, že uživatel do systému nahraje soubor ke konverzi (např. dokument ve Wordu). Systém onen soubor uloží, provede konverzi a poskytne výstup uživateli.

Konverze může být reprezentována takto:

class Conversion:
    def __init__(id, date, file_name):
        self.id = id
        self.date = date
        self.file_name = file_name

Dále mějme v konfiguračním souboru uvedenu cestu k adresáři s konverzemi. Do tohoto adresáře se budou vkládat konverze s příslušnými vstupními a výstupními soubory, každá do svého vlastního adresáře. Jedna ze tříd v systému bude mít na starost získání vstupního souboru a jeho uložení:

# Získej požadavek na konverzi.
# ...
 
# Vytvoř nový adresář pro uložení souborů.
conversion_dir = os.path.join(config['CONVERSIONS_DIR'], conversion.id)
os.mkdir(conversion_dir)
 
# Ulož do vytvořeného adresáře vstupní soubor pod názvem conversion.file_name.
# ...

Adresář pojmenujeme stejně jako identifikátor konverze, tedy využijeme jeho ID. Další třída následně provede konverzi:

file_to_convert = os.path.join(config['CONVERSIONS_DIR'],
    conversion.id, conversion.file_name)
 
# Proveď konverzi a ulož výsledek do file_to_convert + '.pdf'.
# ...

Poslední třída zpřístupní zkonvertovaný soubor:

converted_file = os.path.join(config['CONVERSIONS_DIR'],
    conversion.id, conversion.file_name + '.pdf')
 
# Poskytni soubor uživateli.
# ...

V čem je problém?

Všimněte si, že následující znalosti jsou v systému uloženy na několika místech:

  • ID se využívá jako název adresáře. Co když v budoucnu přijde požadavek na to, aby se z důvodu usnadnění vyhledávání do názvu adresáře zakomponovalo i datum či jiná informace? Bude pak potřeba změnit veškerý kód, který implicitně předpokládá, že název adresáře == ID.
  • Adresáře s konverzemi jsou umístěny přímo v kořenovém adresáři pro konverze. Co když bude v budoucnu potřeba použít meziadresáře, např. datum? Veškerý kód implicitně předpokládá, že se adresář pro konverzi nachází přímo v config['CONVERSIONS_DIR'].
  • Název výstupního souboru je tvořen vstupním souborem s příponou .pdf. Co když dojde ke změně tohoto požadavku, např. má dojít k odstranění původní přípony? Opět je potřeba změnit kód, který implicitně předpokládá, že název výstupního souboru má určitý specifický formát.

Co s tím?

Duplikací znalostí se týká pravidlo DRY, z anglického Don't repeat yourself. Toto pravidlo říká:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

To v našem systému rozhodně neplatí. Abychom z kódu tuto duplikaci znalostí odstranili, rozšíříme třídu Conversion:

class Conversion:
    # Metoda __init__() zůstane stejná.
    # ...
 
    # Poznámka: Při použití metod níže se jako base_dir předá config['CONVERSIONS_DIR'].
 
    def get_conversion_dir(self, base_dir):
        return os.path.join(base_dir, conversion.id)
 
    def get_file_to_convert(self, base_dir):
        return os.path.join(self.get_conversion_dir(base_dir), self.file_name)
 
    def get_converted_file(self, base_dir):
        return self.get_file_to_convert(base_dir) + '.pdf'

Stane se tak autoritou, která dané znalosti explicitně obsahuje a poskytuje je třídám, které ji používají.

Další čtení

Níže uvedu jen pár odkazů. K tomuto pravidlu toho bylo napsáno opravdu hodně, takže stačí pohledat.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
15 + 4 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.