Sprievodca solídnymi zásadami

1. Úvod

V tejto príručke budeme diskutovať solídne princípy objektovo orientovaného dizajnu.

Najprv začneme skúmanie dôvodov, pre ktoré prišli, a prečo by sme ich mali brať do úvahy pri navrhovaní softvéru. Potom načrtneme každý princíp spolu s príkladom kódu, aby sme zdôraznili pointu.

2. Dôvod princípov SOLID

Princípy SOLID boli prvýkrát koncipované Robertom C. Martinom v jeho dokumente z roku 2000, Princípy návrhu a vzory návrhu. Na týchto konceptoch neskôr pracoval Michael Feathers, ktorý nás oboznámil so skratkou SOLID. A za posledných 20 rokov týchto 5 princípov prinieslo revolúciu vo svete objektovo orientovaného programovania a zmenilo spôsob, akým píšeme softvér.

Čo je teda SOLID a ako nám pomáha pri písaní lepšieho kódu? Zjednodušene povedané, Martin a Feathers Princípy návrhu nás povzbudzujú k vytváraniu udržovateľnejšieho, zrozumiteľnejšieho a flexibilnejšieho softvéru. V dôsledku toho s rastom veľkosti našich aplikácií môžeme znížiť ich zložitosť a zachránime si veľa ďalších bolestí hlavy ďalej po ceste!

Nasledujúcich 5 konceptov tvorí naše SOLID princípy:

  1. Scelková zodpovednosť
  2. Opero / zatvorené
  3. Ľiskov Striedanie
  4. Jasegregácia povrchu
  5. Dependency inverzia

Aj keď niektoré z týchto slov môžu pôsobiť odstrašujúco, dá sa im ľahko porozumieť pomocou niekoľkých jednoduchých príkladov kódu. V nasledujúcich častiach sa podrobne ponoríme do toho, čo ktorýkoľvek z týchto princípov znamená, spolu s rýchlym príkladom Javy na ilustráciu každého z nich.

3. Jednotná zodpovednosť

Začnime veci princípom jednej zodpovednosti. Ako by sme mohli očakávať, táto zásada hovorí, že trieda by mala mať iba jednu zodpovednosť. Ďalej by to malo mať iba jeden dôvod na zmenu.

Ako nám táto zásada pomáha vytvárať lepší softvér? Pozrime sa na niekoľko jeho výhod:

  1. Testovanie - Trieda s jednou zodpovednosťou bude mať oveľa menej testovacích prípadov
  2. Spodná spojka - Menej funkčnosti v jednej triede bude mať menej závislostí
  3. Organizácia - Menšie a dobre organizované triedy sa hľadajú ľahšie ako tie monolitické

Vezmime si napríklad triedu, ktorá predstavuje jednoduchú knihu:

public class Book {private String name; súkromný autor reťazcov; súkromný textový reťazec; // konštruktor, getri a nastavovatelia}

V tomto kóde ukladáme meno, autora a text spojený s inštanciou a Kniha.

Teraz pridajme niekoľko metód na dopytovanie textu:

public class Book {private String name; súkromný autor reťazcov; súkromný textový reťazec; // konštruktor, getri a nastavovatelia // metódy, ktoré sa priamo týkajú vlastností knihy public String replaceWordInText (reťazcové slovo) {return text.replaceAll (slovo, text); } public boolean isWordInText (String word) {return text.contains (word); }}

Teraz, náš Kniha triedy funguje dobre a do našej aplikácie môžeme uložiť toľko kníh, koľko sa nám páči. Na čo je však dobré ukladať informácie, ak nemôžeme text odoslať na našu konzolu a prečítať ju?

Vrhnime opatrnosť na vietor a pridajme metódu tlače:

verejná trieda Book {// ... void printTextToConsole () {// náš kód na formátovanie a tlač textu}}

Tento kódex však porušuje zásadu jedinej zodpovednosti, ktorú sme načrtli vyššie. Aby sme napravili náš neporiadok, mali by sme implementovať samostatnú triedu, ktorá sa zaoberá iba tlačením našich textov:

verejná trieda BookPrinter {// metódy výstupu textu void printTextToConsole (text reťazca) {// náš kód na formátovanie a tlač textu} void printTextToAnotherMedium (text reťazca) {// kód na zápis na akékoľvek iné miesto ..}}

Úžasné. Nielenže sme vyvinuli triedu, ktorá zmierňuje Kniha jeho tlačiarenských povinností, ale môžeme využiť aj naše BookPrinter triedy na odoslanie nášho textu na iné médiá.

Či už ide o e-mail, protokolovanie alebo čokoľvek iné, máme samostatnú triedu venovanú tejto jednej skupine.

4. Otvorené pre rozšírenie, zatvorené pre zmenu

Teraz je čas na „O“ - formálnejšie známe ako princíp otvoreno-zatvorené. Jednoducho povedané, triedy by mali byť otvorené pre rozšírenie, ale uzavreté pre modifikáciu.Pritom smeprestaneme modifikovať existujúci kód a spôsobovať potenciálne nové chyby v inak šťastnej aplikácii.

Samozrejme, že jedna výnimka z pravidla je pri opravovaní chýb v existujúcom kóde.

Pozrime sa ďalej na tento koncept na príklade rýchleho kódu. V rámci nového projektu si predstavte, že sme implementovali Gitara trieda.

Je plnohodnotný a dokonca má gombík hlasitosti:

public class Guitar {private String make; súkromný reťazcový model; objem súkromných int; // Konštruktéri, zakladatelia a zakladatelia}

Spúšťame aplikáciu a všetci ju milujú. Po niekoľkých mesiacoch sme sa však rozhodli Gitara je trochu nudná a dá sa robiť s úžasným vzorom plameňa, aby vyzerala trochu „rock and roll“.

V tomto okamihu by mohlo byť lákavé iba otvoriť Gitara triedy a pridať plameňový vzor - ale ktovie, aké chyby by sa v našej aplikácii mohli vyskytnúť.

Namiesto toho, poďme držať sa princípu otvoreného a zatvoreného a jednoducho rozšíriť naše Gitara trieda:

public class SuperCoolGuitarWithFlames rozširuje Guitar {private String flameColor; // konštruktor, getri + nastavovatelia}

Rozšírením Gitara triedy si môžeme byť istí, že naša existujúca aplikácia nebude ovplyvnená.

5. Striedanie v tíme Liskov

Ďalším na našom zozname je substitúcia Liskov, ktorá je pravdepodobne najkomplexnejším z 5 princípov. Jednoducho povedané, ak trieda A je podtyp triedy B, potom by sme mali byť schopní nahradiť B s A bez narušenia správania nášho programu.

Preskočme priamo na kód, ktorý nám pomôže zamotať hlavu okolo tohto konceptu:

verejné rozhranie Car {void turnOnEngine (); void accelerate (); }

Vyššie definujeme jednoduchý Auto rozhranie s niekoľkými metódami, ktoré by mali byť schopné splniť všetky automobily - zapnutie motora a zrýchlenie vpred.

Implementujme naše rozhranie a poskytneme kód pre metódy:

verejná trieda MotorCar implementuje Car {private Engine engine; // Konštruktéri, getri + nastavovatelia public void turnOnEngine () {// zapnite motor! engine.on (); } public void accelerate () {// posun vpred! engine.powerOn (1000); }}

Ako popisuje náš kód, máme motor, ktorý môžeme zapnúť a môžeme zvýšiť výkon. Počkajte však, jeho rok 2019, a Elon Musk bol zaneprázdnený človek.

Teraz žijeme v ére elektromobilov:

public class ElectricCar implementuje Car {public void turnOnEngine () {hodiť nový AssertionError ("Nemám motor!"); } public void accelerate () {// toto zrýchlenie je šialené! }}

Vhadzovaním auta bez motora do mixu zásadne meníme správanie nášho programu. Toto je Do očí bijúce porušenie substitúcie Liskov a je o niečo ťažšie napraviť ako naše predchádzajúce 2 princípy.

Jedným z možných riešení by bolo prepracovať náš model do rozhraní, ktoré zohľadňujú náš bezmotorový stav Auto.

6. Segregácia rozhrania

„Ja“ v SOLID znamená segregáciu rozhraní a jednoducho to znamená väčšie rozhrania by sa mali rozdeliť na menšie. Týmto spôsobom môžeme zabezpečiť, že implementujúcim triedam bude potrebné venovať iba pozornosť metódam, ktoré ich zaujímajú.

V tomto príklade si vyskúšame prácu zookeeperov. Konkrétnejšie budeme pracovať v medvedom výbehu.

Začnime rozhraním, ktoré načrtáva naše úlohy strážcu medveďov:

verejné rozhranie BearKeeper {void washTheBear (); void feedTheBear (); void petTheBear (); }

Ako vášniví strážcovia zoologických záhrad sme radi, že svoje milované medvede umyjeme a nakŕmime. Všetci si však príliš dobre uvedomujeme nebezpečenstvo, keď sa s nimi budete maznať. Naše rozhranie je bohužiaľ dosť veľké a nám nezostáva nič iné, ako implementovať kód na pohladenie medveďa.

Poďme opravte to tak, že rozdelíte naše veľké rozhranie na 3 samostatné:

verejné rozhranie BearCleaner {void washTheBear (); } verejné rozhranie BearFeeder {void feedTheBear (); } verejné rozhranie BearPetter {void petTheBear (); }

Teraz môžeme vďaka segregácii rozhraní implementovať iba tie metódy, ktoré sú pre nás dôležité:

verejná trieda BearCarer implementuje BearCleaner, BearFeeder {public void washTheBear () {// myslím, že sme vynechali miesto ...} public void feedTheBear () {// Tuna Tuesday ...}}

A nakoniec môžeme nebezpečné veci prenechať bláznom:

verejná trieda CrazyPerson implementuje BearPetter {public void petTheBear () {// Veľa šťastia! }}

Ak pôjdeme ďalej, mohli by sme sa aj rozdeliť BookPrinter triedy z nášho príkladu skôr používať segregáciu rozhraní rovnakým spôsobom. Implementáciou a Tlačiareň rozhranie s jedným tlačiť metódou, mohli by sme vytvoriť samostatnú inštanciu ConsoleBookPrinter a OtherMediaBookPrinter triedy.

7. Inverzia závislosti

Princíp inverzie závislostí sa týka oddelenia softvérových modulov. Týmto spôsobom budú namiesto modulov na vysokej úrovni v závislosti od modulov na nízkej úrovni závisieť oba od abstrakcií.

Aby sme to demonštrovali, poďme na starú školu a oživme počítač so systémom Windows 98 s kódom:

verejná trieda Windows98Machine {}

Aký dobrý je však počítač bez monitora a klávesnice? Pridajme jedného z každého do nášho konštruktora tak, aby každý Windows98Počítač inštanciu dodávame vopred zabalený s Monitor a a Štandardná klávesnica:

verejná trieda Windows98Machine {súkromná konečná klávesnica StandardKeyboard; súkromný posledný monitor Monitor; verejné Windows98Machine () {monitor = nový Monitor (); klávesnica = nová StandardKeyboard (); }}

Tento kód bude fungovať a budeme môcť používať Štandardná klávesnica a Monitor slobodne v rámci našej Windows98Počítač trieda. Problém je vyriešený? Nie úplne. Vyhlásením Štandardná klávesnica a Monitor s Nový kľúčové slovo, tieto 3 triedy sme pevne spojili.

Toto robí nielen našu Windows98Počítač ťažko otestovateľné, ale tiež sme stratili schopnosť vypnúť náš Štandardná klávesnica triedy s inou, ak by vznikla potreba. A sme prilepení k nášmu Monitor triedy tiež.

Odpojme náš stroj od Štandardná klávesnica pridaním všeobecnejšej Klávesnica a používa to v našej triede:

verejné rozhranie Klávesnica {}
verejná trieda Windows98Machine {private final Keyboard keyboard; súkromný posledný monitor Monitor; verejné Windows98Machine (klávesnica klávesnice, monitor monitora) {this.keyboard = keyboard; this.monitor = monitor; }}

Tu používame vzor injekcie závislostí, aby sme uľahčili pridanie Klávesnica závislosť na Windows98Machine trieda.

Upravme tiež naše Štandardná klávesnica triedy na implementáciu Klávesnica rozhranie, takže je vhodný na vstrekovanie do Windows98Machine trieda:

verejná trieda StandardKeyboard implementuje klávesnicu {}

Teraz sú naše triedy oddelené a komunikujú prostredníctvom Klávesnica abstrakcia. Ak chceme, môžeme ľahko vypnúť typ klávesnice v našom stroji pomocou inej implementácie rozhrania. Rovnaký princíp môžeme dodržať aj pri Monitor trieda.

Vynikajúci! Oddelili sme závislosti a môžeme testovať naše Windows98Machine podľa toho, aký testovací rámec si vyberieme.

8. Záver

V tomto tutoriáli sme vzali a hlboko ponoriť do SOLID princípov objektovo orientovaného dizajnu.

My začalo sa stručnou históriou SOLID a dôvodmi, prečo tieto princípy existujú.

Písmeno za písmenom, máme rozdelil význam každej zásady pomocou rýchleho príkladu kódu, ktorý ju porušuje. Potom sme videli, ako náš kód opraviť a dodržiavanie zásad SOLID.

Ako vždy, kód je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found