Tarkvaraarendus on suurepärane, kuid ... ma arvan, et võime kõik nõustuda, et see võib olla emotsionaalne rullnokk. Alguses on kõik suurepärane. Lisage uusi funktsioone üksteise järel mõne päeva, kui mitte tundide jooksul. Sa oled õnnelikus vöötis!
Kiirelt paar kuud edasi ja teie arengukiirus aeglustub. Kas sellepärast, et te ei tööta nii palju kui varem? Mitte päris. Edasi paar kuud edasi ja teie arengukiirus aeglustub veelgi. Selle projektiga töötamine pole enam lõbus ja sellest on saanud tõmme.
Kuid see läheb hullemaks. Hakkate oma rakenduses avastama mitu viga. Sageli loob ühe vea lahendamine kaks uut. Sel hetkel võite hakata laulma:
99 väikest viga koodis. 99 väikest viga. Võtke üks, pange sellele plaaster,
… 127 väikest viga koodis.
Kuidas suhtute selle projekti kallal praegu töötamisse? Kui sa oled nagu mina, hakkad tõenäoliselt motivatsiooni kaotama. Selle rakenduse väljatöötamine on keeruline, kuna igal olemasoleva koodi muutmisel võivad olla ettearvamatud tagajärjed.
See kogemus on tarkvaramaailmas tavaline ja võib selgitada, miks nii paljud programmeerijad tahavad oma lähtekoodi lahti teha ja kõik ümber kirjutada.
Mis on selle probleemi põhjus?
Peamine põhjus on keerukuse suurenemine. Minu kogemuse põhjal aitab üldist keerukust kõige rohkem kaasa asjaolu, et enamikus tarkvaraprojektides on kõik ühendatud. Igas klassis olevate sõltuvuste tõttu ei saa teie kasutajad äkki registreeruda, kui muudate klassis mõnda koodi, mis e-kirju saadab. Miks nii? Kuna teie registreerimiskood sõltub koodidest, mis meilid saadab. Nüüd ei saa te vigu sisestamata midagi muuta. Kõiki sõltuvusi pole lihtsalt võimalik jälgida.
Nii et teil on see olemas; meie probleemide tegelik põhjus on keerukuse suurendamine, mis tuleneb kõigist sõltuvustest, mis meie koodil on.
Naljakas on see, et see probleem on olnud teada juba aastaid. See on tavaline antipattern, mida nimetatakse 'suureks savikuuliks'. Olen sellist arhitektuuri näinud peaaegu igas projektis, millega olen aastate jooksul tegelenud mitmes erinevas ettevõttes.
Mis see antipattern siis täpselt on? Lihtsalt öeldes saate suure savipalli, kui iga ese sõltub teistest esemetest. Allpool näete populaarse avatud lähtekoodiga Apache Hadoopi projekti sõltuvuste graafikut. Suure savikuuli (õigemini suure lõngakera) visualiseerimiseks tõmmake ring ja asetage projektiklassid sinna ühtlaselt. Lihtsalt tõmmake iga klassi paari vahel üksteisest sõltuv joon. Nüüd näete oma probleemide allikat.
kuidas raamistikku luua
Seega esitasin endale küsimuse: kas oleks võimalik keerukust vähendada ja ikkagi lõbutseda nagu projekti alguses? Tõesõna, kustutada ei saa kõik keerukus. Kui soovite lisada uusi funktsioone, peate alati oma koodi keerukamaks muutma. Keerukus võib aga liikuda ja levida.
Mõelge masinatööstusele. Kui väike masinatöökoda loob masinaid, ostab ta standardsete esemete komplekti, loob paar kohandatud eset ja ühendab need. Nad saavad need komponendid täiesti eraldi valmistada ja kõik viimasena kokku panna, tehes vaid mõned parandused. Kuidas on see võimalik? Nad teavad, kuidas iga üksus sobib, tuginedes sellistele tööstusharu standarditele nagu poldi suurus ja esialgsed otsused, näiteks kinnitusaukude suurus ja nende vaheline kaugus.
Iga ülaltoodud komplekti üksuse võib hankida sõltumatu ettevõte, kellel pole teadmisi lõpptoote või selle muude osade kohta. Niikaua kui iga moodulüksus on valmistatud vastavalt spetsifikatsioonile, saate lõpliku seadme luua plaanipäraselt.
Kas me saame seda tarkvaratööstuses korrata?
Kindlasti saame! Kasutades liideseid ja tagurpidi juhtimispõhimõtet; parim osa on asjaolu, et seda lähenemist saab kasutada mis tahes objektorienteeritud keeles: Java, C #, Swift, TypeScript, JavaScript, PHP - loend jätkub. Selle meetodi rakendamiseks pole vaja mingit väljamõeldud raamistikku. Tuleb lihtsalt kinni pidada mõnest lihtsast reeglist ja jääda distsiplineerituks.
Kui kuulsin esimest korda kontrolli tühistamisest, sain kohe aru, et olen leidnud lahenduse. See on kontseptsioon olemasolevate sõltuvuste võtmiseks ja nende ümberpööramiseks liideste abil. Liidesed on lihtsad meetodideklaratsioonid. Need ei paku konkreetset teostust. Selle tulemusena saab neid kasutada kahe elemendi vahelise kokkuleppena nende ühendamiseks. Soovi korral saab neid kasutada moodulühendustena. Niikaua kui üks element pakub liidest ja teine element pakub rakendust, saavad nad töötada koos, teadmata üksteisest midagi. See on geniaalne.
Vaatame lihtsas näites, kuidas saaksime oma süsteemi moodulkoodi loomiseks lahutada. Järgmised diagrammid on rakendatud lihtsate Java-rakendustena. Siit leiate neid GitHubi hoidla .
Oletame, et meil on väga lihtne rakendus, mis koosneb ainult ühest klassist Main
, kolmest teenusest ja ühest klassist Util
. Need elemendid sõltuvad üksteisest mitmel viisil. Allpool näete rakendust, mis kasutab lähenemist 'suur muda pall'. Klassid lihtsalt helistavad üksteisele. Need on tihedalt seotud ja te ei saa lihtsalt ühte eset välja tõmmata, teisi puudutamata. Selles stiilis loodud rakendused võimaldavad teil esialgu kiiresti kasvada. Ma arvan, et see stiil sobib kontseptsioonikindlate projektide jaoks, kuna saate mängida lihtsalt. Kuid see ei sobi tootmiseks valmis lahenduste jaoks, sest isegi hooldus võib olla ohtlik ja kõik muudatused võivad põhjustada ettearvamatuid vigu. Alloleval skeemil on kujutatud seda suurt saviarhitektuuri palli.
Parema lähenemise otsimisel võime kasutada tehnikat, mida nimetatakse sõltuvuse süstimiseks. See meetod eeldab, et kõiki komponente tuleb kasutada liideste kaudu. Olen lugenud väiteid, et see ühendab esemed lahti, aga kas tõesti? Ei. Vaadake allolevat skeemi.
Ainus erinevus hetkeolukorra ja suure mudapalli vahel on asjaolu, et nüüd helistame klassidele otse helistamise asemel nende liideste kaudu. Parandab üksteisest eraldamiselemente veidi. Kui soovite näiteks taaskasutada Servicio A
Mõnes teises projektis saate seda teha nii, et võtate välja Servicio A
koos Interfaz A
, aga ka Interfaz B
ja Interface Útil
. Nagu näete, Servicio A
see sõltub ikkagi muudest elementidest. Seetõttu on meil endiselt probleeme koodi muutmisega ühes kohas ja käitumise segamini ajamisega teises kohas. See tekitab endiselt probleemi, et kui muudate Servicio B
e Interfaz B
, peate muutma kõiki sellest sõltuvaid elemente. See lähenemine ei lahenda midagi; minu arvates lisab see lihtsalt liidese kihi elementide peale. Te ei tohiks kunagi süstida sõltuvusi, vaid pigem neist lõplikult vabaneda. Hurraa iseseisvuseks!
Lähenemisviis, mis minu arvates lahendab kõik peamised sõltuvuse peavalud, teeb seda sellega, et ei kasuta sõltuvusi üldse. Loote komponendi ja selle kuulaja. Kuulaja on lihtne liides. Alati, kui peate meetodit välja kutsuma väljaspool praegust elementi, lisage lihtsalt meetod kuulajale ja kutsuge see selle asemel. Elemendil on lubatud kasutada ainult paketis faile, kutsemeetodeid ja kasutada põhiraamistiku või muude kasutatud teekide pakutavaid klasse. Allpool näete rakenduse skeemi, mida on muudetud elementide arhitektuuri kasutamiseks.
Pange tähele, et selles arhitektuuris on ainult klass Main
sellel on mitu sõltuvust. Ühendage kõik elemendid ja kapseldage rakenduse äriloogika.
Teenused on seevastu täiesti sõltumatud elemendid. Nüüd saate kõik teenused sellest rakendusest välja võtta ja mujal uuesti kasutada. Need ei sõltu millestki muust. Kuid oodake, see läheb paremaks: te ei pea neid teenuseid enam muutma, kui te ei muuda nende käitumist. Niikaua kui need teenused teevad seda, mida nad peaksid tegema, võivad nad jääda terveks kuni aegade lõpuni. Neid saab luua a professionaalne tarkvarainsener või esmakordne kodeerija, kes on pühendunud halvimale spagetikoodile, mida keegi on kunagi teinud goto
segatud. See pole oluline, sest teie loogika on kapseldatud. Nii kohutav kui see ka pole, ei levi see kunagi teistesse klassidesse. See annab teile ka õiguse jagada projektiga seotud töö mitme arendaja vahel, kus iga arendaja saab iseseisvalt töötada oma komponendi kallal, ilma et oleks vaja teist katkestada või isegi teistest arendajatest teada saada.
Lõpuks võite hakata veel üks kord iseseisvat koodi kirjutama, täpselt nagu oma viimase projekti alguses.
Määratleme struktuurielemendi muster, et saaksime selle korrata.
Elemendi lihtsaim versioon koosneb kahest asjast: põhielemendi klassist ja kuulajast. Kui soovite elementi kasutada, peate kuulaja juurutama ja helistama põhiklassile. Siin on lihtsaima konfiguratsiooni skeem:
Ilmselt peate lõpuks elemendile veelgi keerukamaks muutma, kuid saate seda lihtsalt teha. Lihtsalt veenduge, et ükski teie loogikaklassidest ei sõltuks projekti teistest failidest. Nad saavad selles elemendis kasutada ainult põhiraamistikku, imporditud teeke ja muid faile. Kui tegemist on varafailidega, nagu pildid, vaatamisväärsused, helid jms, tuleb need ka elementidesse kapseldada, et neid oleks tulevikus lihtne taaskasutada. Saate kogu kausta lihtsalt teise projekti kopeerida ja ongi kõik!
Allpool näete tabelit, mis näitab täpsemat üksust. Pange tähele, et see koosneb teie kasutatavast vaatest ega sõltu muudest rakenduse failidest. Kui soovite teada lihtsat meetodit sõltuvuste kontrollimiseks, vaadake lihtsalt jaotist Import. Kas on faili väljaspool praegust üksust? Sel juhul peaksite need sõltuvused eemaldama, teisaldades need elemendile või lisades kuulajale vastava kõne.
Vaatame Java-s loodud lihtsat näidet “Tere maailm”.
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(); } }
Esialgu määratleme ElementListener
väljundi printimise meetodi määramiseks. Element ise on määratletud allpool. Helistamine sayHello
elemendile printige lihtsalt kiri, kasutades ElementListener
Pange tähele, et element on meetodi printOutput
rakendamisest täiesti sõltumatu. Seda saab printida konsooli, füüsilisse printerisse või väljamõeldud kasutajaliidesesse. Element ei sõltu sellest rakendusest. Selle abstraktsiooni tõttu saab seda elementi erinevates rakendustes hõlpsasti taaskasutada.
Nüüd heitke pilk App
põhiklassile. Rakendage kuulaja ja ühendage element koos konkreetse teostusega. Nüüd saame seda kasutama hakata.
Seda näidet saate JavaScripti käivitada ka siin
Vaatame elementide mustri kasutamist suuremahulistes rakendustes. Üks asi on seda väikeses projektis näidata; teine on selle rakendamine reaalses maailmas.
Mulle meeldiva täispika veebirakenduse struktuur näeb välja selline:
src ├── client │ ├── app │ └── elements │ └── server ├── app └── elements
Lähtekoodi kaustas jagasime esialgu kliendi- ja serverifailid. See on mõistlik asi, mida teha, kuna need töötavad kahes erinevas keskkonnas: brauseris ja tagalaserveris.
Seejärel jagame igas kihis oleva koodi kaustadeks, mida nimetatakse rakendusteks ja üksusteks. Üksused koosnevad eraldi komponentidega kaustadest, samal ajal kui rakenduste kaust ühendab kõik üksused ja salvestab kogu äriloogika.
Nii saab üksusi erinevate projektide vahel taaskasutada, samal ajal kui kogu rakenduspõhine keerukus on koondatud ühte kausta ja taandub sageli lihtsatele üksusekõnedele.
Kui usume, et praktika on alati teooria üle ülimuslik, vaatame pilti reaalsest elust, mis on loodud Node.js ja TypeScript.
See on väga lihtne veebirakendus, mida saab kasutada edasijõudnumate lahenduste lähtepunktina. See järgib elemendi arhitektuuri ja kasutab ulatuslikult struktuurielemendi mustrit.
Tipphetkedest näete, et põhileht on üksusena eristatud. Sellel lehel on oma vaade. Nii et kui soovite seda näiteks taaskasutada, saate lihtsalt kogu kausta kopeerida ja teise projekti paigutada. Lihtsalt ühendage kõik ja teil on hea minna.
See on näide, mis näitab, et saate juba täna oma rakendustesse üksusi tutvustada. Võite hakata eristama sõltumatuid komponente ja eraldama nende loogikat. Pole tähtis, kui segane on kood, millega praegu töötate.
Loodan, et selle uue tööriistakomplektiga saate hõlpsamini hooldatavat koodi arendada. Enne elementmustri praktikasse jõudmist uurime kiiresti kõiki peamisi punkte:
Palju probleeme ilmnevad tarkvaras sõltuvalt komponentide vahelistest sõltuvustest.
Ühes kohas muudatust tehes saate ettenägematut käitumist tutvustada ka teises kohas.
Kolm üldist arhitektuurilist lähenemist on:
Suur savipall. See on suurepärane kiireks arenguks, kuid mitte nii suurepärane stabiilseks tootmiseks.
Sõltuvuse süstimine. See on pool lahendus, mida peaksite vältima.
Elementide arhitektuur. See lahendus võimaldab teil luua sõltumatuid komponente ja neid teistes projektides uuesti kasutada. See on hooldatav ja hiilgav tootmise stabiilseks väljalaskmiseks.
Põhielemendi muster koosneb põhiklassist, millel on kõik peamised meetodid, samuti kuulajast, mis on lihtne liides, mis võimaldab suhelda välise maailmaga.
Kogu virnaelementide arhitektuuri saavutamiseks eraldatakse esiosa kõigepealt tagakoodi koodist. Seejärel looge mõlemas kaust rakenduse ja üksuste jaoks. Kaust Üksused koosneb kõigist eraldiseisvatest üksustest, samal ajal kui rakenduste kaust ühendab kõik omavahel.
Nüüd saate minna ja alustada oma üksuste loomist ja jagamist. Pikemas perspektiivis aitab see teil luua tooteid, mida on lihtne hooldada. Edu ja andke teada, mida olete loonud!
Samuti, kui leiate, et olete oma koodi enneaegselt optimeerinud, lugege seda _ Kuidas vältida enneaegse optimeerimise needust minu ApeeScape'i partnerilt Kevin Blochilt.