vytvoření skutečně modulárního kódu bez závislostí

vývoj softwaru je skvělý, ale … myslím, že se všichni shodneme, že to může být trochu emocionální horská dráha. Na začátku je všechno skvělé. Nové funkce přidáváte jeden po druhém za několik dní, ne-li hodin. Jsi na řadě!

rychlý posun vpřed o několik měsíců a vaše rychlost vývoje klesá. Je to proto, že nepracujete tak tvrdě jako předtím? Ani ne. Pojďme rychle vpřed o několik měsíců a vaše vývojová rychlost dále klesá. Práce na tomto projektu už není zábavná a stala se přetažením.

zhoršuje se to. Začnete objevovat více chyb v aplikaci. Řešení jedné chyby často vytvoří dvě nové. V tomto okamžiku můžete začít zpívat:

99 malé chyby v kódu.99 malých brouků.Vezměte jednu dolů, opravte ji,

… 127 malých chyb v kódu.

jak se cítíte o práci na tomto projektu nyní? Pokud jste jako já, pravděpodobně začnete ztrácet motivaci. Je to jen bolest vyvinout tuto aplikaci, protože každá změna stávajícího kódu může mít nepředvídatelné důsledky.

tato zkušenost je běžná ve světě softwaru a může vysvětlit, proč tolik programátorů chce zahodit svůj zdrojový kód a přepsat vše.

důvody, proč se vývoj softwaru v průběhu času zpomaluje

takže jaký je důvod tohoto problému?

hlavní příčinou je rostoucí složitost. Z mé zkušenosti je největším přispěvatelem k celkové složitosti skutečnost, že v naprosté většině softwarových projektů je vše propojeno. Kvůli závislostem, které má každá třída, pokud změníte nějaký kód ve třídě, která odesílá e-maily, uživatelé se najednou nemohou zaregistrovat. Proč? Protože váš registrační kód závisí na kódu, který odesílá e-maily. Nyní nemůžete nic změnit bez zavedení chyb. Je to prostě není možné sledovat všechny závislosti.

Tak tady to máte; skutečnou příčinou našich problémů je zvyšování složitosti přicházející ze všech závislostí, které náš kód má.

velká koule bláta a jak ji snížit

legrační je, že tento problém je znám již léta. Je to běžný anti-vzor zvaný ” velká koule bláta.”Viděl jsem tento typ architektury téměř ve všech projektech, na kterých jsem v průběhu let pracoval ve více různých společnostech.

co je tedy tento anti-vzor přesně? Jednoduše řečeno, dostanete velkou kouli bláta, když má každý prvek závislost na jiných prvcích. Níže vidíte graf závislostí ze známého open-source projektu Apache Hadoop. Chcete-li vizualizovat velkou kouli bláta (nebo spíše velkou kouli příze), nakreslíte kruh a rovnoměrně na něj umístíte třídy z projektu. Stačí nakreslit čáru mezi každou dvojicí tříd, které na sobě závisí. Nyní můžete vidět zdroj vašich problémů.

vizualizace velké koule bahna Apache Hadoopa s několika desítkami uzlů a stovkami linií, které je navzájem spojují.

Apache Hadoop je “velká koule bláta”

Řešení s Modulární Kód

Tak jsem se zeptal sám sebe na otázku: bylo By možné snížit složitost a stále bavit, jako na začátku projektu? Po pravdě řečeno, nemůžete odstranit všechny složitosti. Pokud chcete přidat nové funkce, budete vždy muset zvýšit složitost kódu. Nicméně složitost může být přesunuta a oddělena.

jak ostatní průmyslová odvětví řeší tento problém

Přemýšlejte o strojírenském průmyslu. Když nějaký malý mechanický obchod vytváří stroje, koupí sadu standardních prvků, vytvoří několik vlastních a dá je dohromady. Mohou tyto komponenty vyrobit zcela samostatně a na konci vše sestavit, dělat jen pár vylepšení. Jak je to možné? Vědí, jak každý prvek do sebe zapadne stanovenými průmyslovými standardy, jako jsou velikosti šroubů, a přední rozhodnutí, jako je velikost montážních otvorů a vzdálenost mezi nimi.

technické schéma fyzického mechanismu a jak jeho kusy do sebe zapadají. Kusy jsou očíslovány v pořadí, v jakém se připojí další, ale toto pořadí zleva doprava jde 5, 3, 4, 1, 2.

každý prvek ve výše uvedené sestavě může být poskytnut samostatnou společností, která nemá žádné znalosti o konečném produktu nebo jeho dalších kusech. Dokud bude každý modulární prvek vyroben podle specifikací, budete moci vytvořit konečné zařízení podle plánu.

můžeme to replikovat v softwarovém průmyslu?

jistě můžeme! Pomocí rozhraní a inverze řídicího principu; Nejlepší na tom je skutečnost, že tento přístup lze použít v jakémkoli objektově orientovaném jazyce: Java, C#, Swift, TypeScript, JavaScript, PHP—seznam pokračuje dál a dál. K použití této metody nepotřebujete žádný efektní rámec. Stačí se držet několika jednoduchých pravidel a zůstat disciplinovaní.

Inverze řízení je váš přítel

když jsem poprvé slyšel o inverzi řízení, okamžitě jsem si uvědomil, že jsem našel řešení. Je to koncept převzetí existujících závislostí a jejich převrácení pomocí rozhraní. Rozhraní jsou jednoduché deklarace metod. Neposkytují žádnou konkrétní realizaci. V důsledku toho mohou být použity jako dohoda mezi dvěma prvky o tom, jak je propojit. Mohou být použity jako modulární konektory, Chcete-li. Dokud jeden prvek poskytuje rozhraní a další prvek poskytuje implementaci, mohou spolupracovat, aniž by o sobě něco věděli. Je to geniální.

podívejme se na jednoduchém příkladu, jak můžeme oddělit náš systém a vytvořit modulární kód. Níže uvedené diagramy byly implementovány jako jednoduché Java aplikace. Najdete je na tomto úložišti GitHub.

problém

předpokládejme, že máme velmi jednoduchou aplikaci sestávající pouze z třídy Main, tří služeb a jedné třídy Util. Tyto prvky na sobě závisí několika způsoby. Níže vidíte implementaci pomocí přístupu “big ball of mud”. Třídy si jednoduše volají. Jsou pevně spojeny a nemůžete jednoduše vyjmout jeden prvek, aniž byste se dotkli ostatních. Aplikace vytvořené pomocí tohoto stylu vám umožní zpočátku rychle růst. Věřím, že tento styl je vhodný pro projekty proof-of-concept, protože si můžete snadno pohrát s věcmi. Nicméně, to není vhodné pro produkční-ready řešení, protože i údržba může být nebezpečná a každý změnit můžete vytvořit nepředvídatelné chyby. Níže uvedený diagram ukazuje tuto velkou kouli bahenní architektury.

Hlavní využívá služby A, B A C, které každý používá Util. Služba C využívá také službu a.

proč Dependency Injection to všechno špatně

při hledání lepšího přístupu můžeme použít techniku zvanou dependency injection. Tato metoda předpokládá, že všechny komponenty by měly být používány prostřednictvím rozhraní. Četl jsem tvrzení, že odděluje prvky,ale je to opravdu, ačkoli? Č. Podívejte se na níže uvedený diagram.

předchozí architektura, ale s injekcí závislosti. Nyní hlavní používá rozhraní služby a, B A C, které jsou implementovány jejich odpovídajícími službami. Služby a A C používají rozhraní B a rozhraní Util, které je implementováno Util. Služba C také používá službu rozhraní a. každá služba spolu s jejím rozhraním je považována za prvek.

jediný rozdíl mezi současnou situací a velká koule bahna je fakt, že teď, místo toho, volat třídy přímo, říkáme jim přes jejich rozhraní. Mírně zlepšuje oddělovací prvky od sebe. Pokud, například, chcete znovu použít Service A v jiném projektu, můžete to udělat tím, že Service A sám, spolu s Interface A, stejně jako Interface B a Interface Util. Jak vidíte, Service A stále závisí na dalších prvcích. Výsledkem je, že stále máme problémy se změnou kódu na jednom místě a zmatením chování na jiném místě. Stále vytváří problém, že pokud upravíte Service B a Interface B, budete muset změnit všechny prvky, které na něm závisí. Tento přístup nic neřeší; podle mého názoru pouze přidává vrstvu rozhraní na prvky. Nikdy byste neměli vkládat žádné závislosti, ale místo toho byste se jich měli jednou provždy zbavit. Hurá za nezávislost!

řešení pro modulární kód

přístup, který věřím, řeší všechny hlavní bolesti hlavy závislostí, to dělá tím, že vůbec nepoužívá závislosti. Vytvoříte komponentu a její posluchače. Posluchač je jednoduché rozhraní. Kdykoli potřebujete volat metodu mimo aktuální prvek, jednoduše přidáte metodu k posluchači a místo toho ji zavoláte. Element smí používat pouze soubory, metody volání v rámci svého balíčku a používat třídy poskytované main frameworkem nebo jinými použitými knihovnami. Níže vidíte schéma aplikace upravené pro použití architektury prvků.

schéma aplikace upraveno tak, aby používalo architekturu prvků. Hlavní použití Util a všechny tři služby. Main také implementuje posluchače pro každou službu, který je používán touto službou. Posluchač a služba společně jsou považovány za prvek.

Vezměte prosím na vědomí, že v této architektuře má více závislostí pouze třída Main. Spojuje všechny prvky dohromady a zapouzdřuje obchodní logiku aplikace.

služby jsou naproti tomu zcela nezávislé prvky. Nyní můžete z této aplikace vyjmout každou službu a znovu je použít někde jinde. Nejsou závislí na ničem jiném. Ale počkejte, je to lepší: tyto služby už nikdy nemusíte upravovat, pokud nezměníte jejich chování. Dokud tyto služby dělají to, co mají dělat, mohou zůstat nedotčeny až do konce času. Mohou být vytvořeny profesionální softwarový inženýr, nebo poprvé kodér ohrožena nejhorší špagety kód někdo někdy vařené s goto prohlášení smíšené. Na tom nezáleží, protože jejich logika je zapouzdřená. Jakkoli to může být hrozné, nikdy se nevylije do jiných tříd. To také vám dává možnost rozdělit práci na projektu mezi více vývojáři, kde každý vývojář může pracovat na své vlastní komponenty samostatně bez nutnosti přerušit druhého, nebo dokonce věděl o existenci jiných vývojářů.

konečně můžete začít psát nezávislý kód ještě jednou, stejně jako na začátku vašeho posledního projektu.

vzor prvku

definujme vzor konstrukčního prvku, abychom jej mohli vytvořit opakovatelným způsobem.

nejjednodušší verze prvku se skládá ze dvou věcí: Třída hlavních prvků a posluchač. Pokud chcete použít prvek, musíte implementovat posluchače a volat do hlavní třídy. Zde je schéma nejjednodušší konfigurace:

schéma jednoho prvku a jeho posluchače v aplikaci. Stejně jako dříve aplikace používá prvek, který používá svého posluchače, který je implementován aplikací.

je zřejmé, že budete muset přidat více složitosti do prvku nakonec, ale můžete tak učinit snadno. Jen se ujistěte, že žádná z vašich logických tříd nezávisí na jiných souborech v projektu. Mohou používat pouze hlavní rámec, importované knihovny a další soubory v tomto prvku. Pokud jde o soubory aktiv, jako jsou obrázky, pohledy, zvuky atd., měly by být také zapouzdřeny v prvcích, aby se v budoucnu snadno znovu použily. Můžete jednoduše zkopírovat celou složku do jiného projektu a tam to je!

níže vidíte ukázkový graf zobrazující pokročilejší prvek. Všimněte si, že se skládá z pohledu, který používá, a nezávisí na žádných jiných souborech aplikace. Pokud chcete znát jednoduchou metodu kontroly závislostí, stačí se podívat do sekce import. Existují nějaké soubory mimo aktuální prvek? Pokud ano, musíte tyto závislosti odstranit buď přesunutím do prvku, nebo přidáním příslušného volání posluchači.

jednoduchý diagram složitějšího prvku. Zde se větší smysl slova "prvek" skládá ze šesti částí: pohled; logika A, B A C; prvek; a posluchač prvků. Vztahy mezi nimi dvěma, a Aplikace jsou stejné jako předtím, ale vnitřní Prvek rovněž využívá Logiky a a C. Logika C používá Logiky a a B. Logika A používá Logiku, B a Zobrazení.

podívejme se také na jednoduchý příklad “Hello World” vytvořený v Javě.

public class Main { interface ElementListener { void printOutput(String message); } static class Element { private ElementListener listener; public Element(ElementListener listener) { this.listener = listener; } public void sayHello() { String message = "Hello World of Elements!"; this.listener.printOutput(message); } } static class App { public App() { } public void start() { // Build listener ElementListener elementListener = message -> System.out.println(message); // Assemble element Element element = new Element(elementListener); element.sayHello(); } } public static void main(String args) { App app = new App(); app.start(); }}

zpočátku definujeme ElementListener, abychom určili metodu, která vytiskne výstup. Samotný prvek je definován níže. Při volání sayHello na prvek jednoduše vytiskne zprávu pomocí ElementListener. Všimněte si, že prvek je zcela nezávislý na implementaci metody printOutput. Lze jej vytisknout do konzoly, fyzické tiskárny nebo efektního uživatelského rozhraní. Prvek nezávisí na této implementaci. Díky této abstrakci lze tento prvek snadno znovu použít v různých aplikacích.

nyní se podívejte na hlavní třídu App. Implementuje posluchače a sestavuje prvek společně s konkrétní implementací. Teď ji můžeme začít používat.

tento příklad můžete také spustit v JavaScriptu zde

Architektura prvků

pojďme se podívat na použití vzoru prvků ve velkých aplikacích. Jedna věc je ukázat to v malém projektu-druhá je aplikovat na skutečný svět.

struktura webové aplikace s plným zásobníkem, kterou rád používám, vypadá následovně:

src├── client│ ├── app│ └── elements│ └── server ├── app └── elements

ve složce zdrojového kódu jsme nejprve rozdělili soubory klienta a serveru. Je to rozumná věc, protože běží ve dvou různých prostředích: prohlížeč a back-end server.

pak rozdělíme kód v každé vrstvě do složek s názvem app a elements. Prvky se skládají ze složek s nezávislými komponenty, zatímco složka aplikace spojuje všechny prvky dohromady a ukládá veškerou obchodní logiku.

Že způsob, prvky mohou být znovu použity mezi různými projekty, zatímco všechny aplikace-specifické složitosti je zapouzdřen v jedné složce a docela často snížena na jednoduché volání prvky.

praktický příklad

věřit, že praxe vždy trumfuje teorii, podívejme se na příklad skutečného života vytvořený v uzlu.js a strojopis.

Real Life Example

je to velmi jednoduchá webová aplikace, která může být použita jako výchozí bod pro pokročilejší řešení. Sleduje architekturu prvků a používá značně strukturální vzor prvků.

z zvýraznění můžete vidět, že hlavní stránka byla rozlišena jako prvek. Tato stránka obsahuje vlastní pohled. Takže když například chcete znovu použít, můžete jednoduše zkopírovat celou složku a vložit ji do jiného projektu. Stačí vše spojit dohromady a jste nastaveni.

je to základní příklad, který ukazuje, že můžete začít zavádět prvky ve své vlastní aplikaci ještě dnes. Můžete začít rozlišovat nezávislé komponenty a oddělit jejich logiku. Nezáleží na tom, jak chaotický je kód, na kterém právě pracujete.

Rozvíjet Rychleji, Opakovaně Používat Častěji!

doufám, že s touto novou sadou nástrojů budete moci snadněji vyvinout kód, který je udržovatelnější. Než skočíte do používání vzoru prvků v praxi, rychle shrneme všechny hlavní body:

  • mnoho problémů v softwaru se děje kvůli závislostem mezi více komponentami.

  • změnou na jednom místě můžete zavést nepředvídatelné chování někde jinde.

Tři běžné architektonické přístupy jsou:

  • velká koule bláta. Je to skvělé pro rychlý vývoj, ale ne tak skvělé pro stabilní výrobní účely.

  • závislost injekce. Je to napůl pečené řešení, kterému byste se měli vyhnout.

  • Architektura prvků. Toto řešení umožňuje vytvářet nezávislé komponenty a znovu je používat v jiných projektech. Je udržovatelný a skvělý pro stabilní produkční vydání.

základní prvek vzor se skládá z hlavní třídy, která má všechny hlavní metody, stejně jako posluchač, že je jednoduché rozhraní, které umožňuje komunikaci s vnějším světem.

Chcete – li dosáhnout architektury prvků full-stack, nejprve oddělte front-end od back-endového kódu. Pak vytvoříte složku v každé PRO aplikaci a prvky. Složka elements se skládá ze všech nezávislých prvků, zatímco složka app spojuje vše dohromady.

nyní můžete jít a začít vytvářet a sdílet své vlastní prvky. Z dlouhodobého hlediska vám pomůže vytvořit snadno udržovatelné produkty. Hodně štěstí a dejte mi vědět, co jste vytvořili!

také, pokud zjistíte, že předčasně optimalizujete svůj kód, přečtěte si, jak se vyhnout prokletí předčasné optimalizace kolegou Toptalerem Kevinem Blochem.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.