Čvrst vodič za ČVRSTE principe

1. Uvod

U ovom uputstvu ćemo raspravljati ČVRSTI principi objektno orijentiranog dizajna.

Prvo ćemo početi istražujući razloge zbog kojih su nastali i zašto bismo ih trebali razmotriti prilikom dizajniranja softvera. Zatim ćemo iznijeti svako načelo zajedno s nekim primjerom koda kako bismo naglasili poantu.

2. Razlog ČVRSTIH NAČELA

ČVRSTA načela prvi je konceptualizirao Robert C. Martin u svom radu iz 2000, Principi dizajna i uzorci dizajna. Te je koncepte kasnije nadogradio Michael Feathers, koji nas je uveo u SOLID kraticu. U posljednjih 20 godina, ovih je 5 principa revolucioniralo svijet objektno orijentiranog programiranja, promijenivši način na koji pišemo softver.

Dakle, što je ČVRSTO i kako nam pomaže pri pisanju boljeg koda? Jednostavno rečeno, Martina i perja principi dizajna potiču nas na stvaranje održivijeg, razumljivijeg i fleksibilnijeg softvera. Slijedom toga, kako naše aplikacije rastu u veličini, možemo smanjiti njihovu složenost i spasite si puno glavobolje dalje!

Sljedećih 5 koncepata čine naša ČVRSTA načela:

  1. Single Odgovornost
  2. Oolovka / Zatvoreno
  3. Liskov Zamjena
  4. Jasegregacija na površini
  5. Dinverzija ovisnosti

Iako neke od ovih riječi mogu zvučati zastrašujuće, lako ih je razumjeti pomoću nekoliko jednostavnih primjera koda. U sljedećim odjeljcima duboko ćemo zaroniti u značenje svakog od ovih principa, zajedno s kratkim Java primjerom koji će ilustrirati svako od njih.

3. Pojedinačna odgovornost

Počnimo stvari s principom jedinstvene odgovornosti. Kao što bismo mogli očekivati, ovo načelo to kaže razred bi trebao imati samo jednu odgovornost. Nadalje, trebao bi imati samo jedan razlog za promjenu.

Kako nam ovaj princip pomaže u izgradnji boljeg softvera? Pogledajmo nekoliko njegovih prednosti:

  1. Testiranje - Razred s jednom odgovornošću imat će mnogo manje test slučajeva
  2. Donja spojnica - Manje funkcionalnosti u jednoj klasi imat će manje ovisnosti
  3. Organizacija - Manje, dobro organizirane razrede lakše je pretraživati ​​od monolitnih

Uzmimo, na primjer, razred koji predstavlja jednostavnu knjigu:

knjiga javnog razreda {naziv privatnog niza; privatni autor niza; privatni tekst niza; // konstruktor, getteri i postavljači}

U ovom kodu pohranjujemo ime, autora i tekst pridruženi instanci a Knjiga.

Dodajmo sada nekoliko metoda za upit teksta:

knjiga javnog razreda {naziv privatnog niza; privatni autor niza; privatni tekst niza; // konstruktor, getteri i postavljači // metode koje se izravno odnose na svojstva knjige public String replaceWordInText (String word) {return text.replaceAll (riječ, tekst); } javni boolean isWordInText (niz riječi) {return text.contains (riječ); }}

Sada, naš Knjiga razred dobro funkcionira i u našu aplikaciju možemo pohraniti onoliko knjiga koliko želimo. Ali, kakva je korist od pohrane podataka ako tekst ne možemo iznijeti na svoju konzolu i pročitati ga?

Bacimo oprez u vjetar i dodajte metodu ispisa:

public class Book {// ... void printTextToConsole () {// naš kôd za formatiranje i ispis teksta}}

Međutim, ovaj kodeks krši načelo jedinstvene odgovornosti koje smo prethodno istaknuli. Da bismo popravili naš nered, trebali bismo implementirati zasebnu klasu koja se bavi samo ispisom naših tekstova:

public class BookPrinter {// metode za izlaz teksta void printTextToConsole (tekst niza) {// naš kod za formatiranje i ispis teksta} void printTextToAnotherMedium (tekst niza) {// kôd za pisanje na bilo koje drugo mjesto ..}}

Super. Ne samo da smo razvili razred koji olakšava Knjiga svojih tiskarskih dužnosti, ali također možemo iskoristiti svoje BookPrinter razreda poslati naš tekst drugim medijima.

Bilo da se radi o e-pošti, zapisnicima ili bilo čemu drugom, imamo zasebnu nastavu posvećenu ovoj jedinoj brizi.

4. Otvoreno za proširenje, zatvoreno za izmjenu

Sada je vrijeme za "O" - formalnije poznato kao princip otvoreno-zatvoreno. Jednostavno rečeno, razredi bi trebali biti otvoreni za proširenje, ali zatvoreni za preinake.Pritom mizaustavite se da ne mijenjamo postojeći kôd i ne uzrokujemo potencijalne nove bugove u inače sretnoj aplikaciji.

Naravno, jedna iznimka od pravila je kod ispravljanja bugova u postojećem kodu.

Istražimo koncept dalje brzim primjerom koda. Kao dio novog projekta, zamislimo da smo implementirali a Gitara razred.

Potpuno je razvijen i ima čak i gumb za glasnoću:

gitara javne klase {private String make; model privatnog niza; privatni int svezak; // Konstruktori, geteri i postavljači}

Pokrećemo aplikaciju i svi je vole. Međutim, nakon nekoliko mjeseci odlučujemo o Gitara je pomalo dosadan i mogao bi to učiniti sa strašnim uzorkom plamena kako bi izgledao malo više "rock and roll".

U ovom trenutku možda bi bilo primamljivo samo otvoriti Gitara klase i dodajte uzorak plamena - ali tko zna koje bi pogreške mogle nastati u našoj aplikaciji.

Umjesto toga, idemo držite se principa otvoreno-zatvoreno i jednostavno proširite naš Gitara razred:

javna klasa SuperCoolGuitarWithFlames proširuje gitaru {private String flameColor; // konstruktor, getteri + postavljači}

Proširivanjem Gitara klase možemo biti sigurni da to neće utjecati na našu postojeću aplikaciju.

5. Zamjena Liskova

Sljedeća na našem popisu je zamjena Liskova, koja je vjerojatno najsloženija od 5 principa. Jednostavno rečeno, ako razred A je podvrsta klase B, tada bismo trebali biti u mogućnosti zamijeniti B s A bez narušavanja ponašanja našeg programa.

Samo skoknimo izravno na kod kako bismo se okomili oko ovog koncepta:

javno sučelje Car {void turnOnEngine (); void accelerate (); }

Iznad definiramo jednostavno Automobil sučelje s nekoliko metoda koje bi svi automobili trebali ispuniti - uključivanjem motora i ubrzavanjem naprijed.

Primijenimo svoje sučelje i pružimo kod za metode:

javna klasa MotorCar implementira Car {privatni motor motora; // Konstruktori, getteri + postavljači public void turnOnEngine () {// uključite motor! motor.na (); } javna praznina accelerate () {// kreni naprijed! motor.powerOn (1000); }}

Kao što opisuje naš kod, imamo motor koji možemo uključiti i možemo povećati snagu. Ali pričekajte, 2019. godina, i Elon Musk bio je zauzet čovjek.

Sada živimo u eri električnih automobila:

javna klasa ElectricCar implementira Car {public void turnOnEngine () {baciti novu AssertionError ("Nemam motor!"); } public void accelerate () {// ovo ubrzanje je ludo! }}

Ubacivanjem automobila bez motora u smjesu, u osnovi mijenjamo ponašanje našeg programa. Ovo je očito kršenje zamjene Liskova i malo je teže popraviti od naša prethodna 2 principa.

Jedno od mogućih rješenja bilo bi prerađivanje našeg modela u sučelja koja uzimaju u obzir naše stanje bez motora Automobil.

6. Segregacija sučelja

"I" u SOLID označava segregaciju sučelja, a to jednostavno znači veća sučelja treba podijeliti na manja. Čineći to, možemo osigurati da provedbu nastave trebaju voditi računa samo o metodama koje su za njih zanimljive.

U ovom ćemo se primjeru okušati kao čuvari zoološkog vrta. I preciznije, radit ćemo u ograđenom prostoru za medvjede.

Počnimo sa sučeljem koje opisuje naše uloge čuvara medvjeda:

javno sučelje BearKeeper {void WashTheBear (); void feedTheBear (); void petTheBear (); }

Kao zagriženi čuvari zoološkog vrta, više nego zadovoljni smo oprati i hraniti svoje voljene medvjede. Međutim, previše smo svjesni opasnosti od njihovog maženja. Nažalost, naše je sučelje prilično veliko i nemamo izbora nego implementirati kod za maženje medvjeda.

Idemo popravite to podijelivši naše veliko sučelje na 3 zasebna:

javno sučelje BearCleaner {void WashTheBear (); } javno sučelje BearFeeder {void feedTheBear (); } javno sučelje BearPetter {void petTheBear (); }

Sada, zahvaljujući segregaciji sučelja, možemo slobodno primijeniti samo one metode koje su nam važne:

javna klasa BearCarer implementira BearCleaner, BearFeeder {public void WashTheBear () {// Mislim da smo propustili jedno mjesto ...} public void feedTheBear () {// Tuna utorkom ...}}

I konačno, opasne stvari možemo prepustiti ludima:

javna klasa CrazyPerson implementira BearPetter {public void petTheBear () {// Sretno s tim! }}

Idući dalje, mogli bismo čak i podijeliti svoje BookPrinter klase iz našeg ranijeg primjera za korištenje segregacije sučelja na isti način. Primjenom a Pisač sučelje s jednim ispis metodu, mogli bismo instancirati odvojeno ConsoleBookPrinter i OstaloMediaBookPrinter razreda.

7. Inverzija ovisnosti

Načelo inverzije ovisnosti odnosi se na razdvajanje softverskih modula. Na ovaj način, umjesto modula visoke razine, ovisno o modulima niske razine, oboje će ovisiti o apstrakcijama.

Da bismo to demonstrirali, idemo u old-school i oživimo Windows 98 računalo s kodom:

javna klasa Windows98Machine {}

Ali kakva je korist od računala bez monitora i tipkovnice? Dodajmo po jedan u naš konstruktor tako da svaki Windows98računalo instantiate dolazi predpakiran s a Monitor i a Standardna tipkovnica:

javna klasa Windows98Machine {privatna konačna tipkovnica StandardKeyboard; privatni završni monitor monitora; javni Windows98Machine () {monitor = novi Monitor (); tipkovnica = nova StandardKeyboard (); }}

Ovaj će kôd raditi, a mi ćemo moći koristiti Standardna tipkovnica i Monitor slobodno unutar našeg Windows98računalo razred. Problem riješen? Ne baš. Proglašavanjem Standardna tipkovnica i Monitor s novi ključna riječ, čvrsto smo povezali ove 3 klase.

Ne samo da ovo čini našim Windows98računalo teško za testiranje, ali također smo izgubili mogućnost da isključimo svoje Standardna tipkovnica razred s drugim ako se ukaže potreba. I mi smo zapeli sa svojim Monitor razred, također.

Odvojimo naš stroj od Standardna tipkovnica dodavanjem općenitijeg Tipkovnica sučelje i koristimo ovo u našoj klasi:

tipkovnica javnog sučelja {}
javna klasa Windows98Machine {privatna završna tipkovnica tipkovnice; privatni završni monitor monitora; javni Windows98Machine (tipkovnica tipkovnice, monitor monitora) {this.keyboard = tipkovnica; this.monitor = monitor; }}

Ovdje koristimo obrazac ubrizgavanja ovisnosti kako bismo olakšali dodavanje Tipkovnica ovisnost o Windows98Machine razred.

Izmijenimo i naš Standardna tipkovnica razred za provedbu Tipkovnica sučelje tako da je pogodno za ubrizgavanje u Windows98Machine razred:

javna klasa StandardKeyboard implementira tipkovnicu {}

Sada su naši razredi nevezani i komuniciraju putem Tipkovnica apstrakcija. Ako želimo, možemo jednostavno isključiti vrstu tipkovnice u našem stroju s drugačijom implementacijom sučelja. Možemo slijediti isti princip za Monitor razred.

Izvrsno! Razdvojili smo ovisnosti i možemo slobodno testirati svoje Windows98Machine s bilo kojim testnim okvirom koji odaberemo.

8. Zaključak

U ovom uputstvu uzeli smo duboko zaroniti u ČVRSTE principe objektno orijentiranog dizajna.

Mi započeo s kratkim prikazom ČVRSTE povijesti i razlozima zbog kojih ovi principi postoje.

Slovo po slovo, jesmo raščlanio značenje svakog principa s brzim primjerom koda koji ga krši. Tada smo vidjeli kako popraviti naš kôd i neka se pridržava ČVRSTIH principa.

Kao i uvijek, kôd je dostupan na GitHub-u.