Ohjelmistojen kehittäminen on hienoa, mutta… luulen, että voimme kaikki olla samaa mieltä siitä, että se voi olla hieman tunteellinen vuoristorata. Alussa kaikki on hienoa. Lisät uusia ominaisuuksia peräkkäin muutamassa päivässä, ellei tunnissa. Olet rullalla!
Nopea eteenpäin muutama kuukausi, ja kehitystesi nopeus laskee. Johtuuko se siitä, ettet työskentele yhtä kovasti kuin ennen? Ei oikeastaan. Nopeasti eteenpäin muutama kuukausi, ja kehitysnopeutesi laskee edelleen. Tämän projektin parissa työskenteleminen ei ole enää hauskaa, ja siitä on tullut vetovoima.
Se pahenee. Aloitat löytää useita virheitä sovelluksestasi. Usein yhden virheen ratkaiseminen luo kaksi uutta. Tässä vaiheessa voit aloittaa laulamisen:
99 pientä vikaa koodissa. 99 pientä vikaa. Ota yksi alas, laita se ympärille,
… 127 pientä vikaa koodissa.
Mitä mieltä olet työskennellä tämän projektin parissa nyt? Jos olet kuin minä, alat todennäköisesti menettää motivaatiotasi. Tämän sovelluksen kehittäminen on vain tuskaa, koska jokaisella muutoksella olemassa olevaan koodiin voi olla arvaamattomia seurauksia.
Tämä kokemus on yleistä ohjelmistomaailmassa ja selittää, miksi niin monet ohjelmoijat haluavat heittää lähdekoodinsa pois ja kirjoittaa kaiken uudelleen.
Joten mikä on tämän ongelman syy?
Tärkein syy on monimutkaisuus. Kokemukseni mukaan suurin monimutkaisuuden tekijä on se, että suurin osa ohjelmistoprojekteista kaikki on yhteydessä toisiinsa. Kunkin luokan riippuvuuksien takia, jos muutat luokan koodia, joka lähettää sähköposteja, käyttäjät eivät voi yhtäkkiä rekisteröityä. Miksi niin? Koska rekisteröintikoodisi riippuu koodista, joka lähettää sähköposteja. Nyt et voi muuttaa mitään tekemättä virheitä. Kaikkia riippuvuuksia ei yksinkertaisesti voida jäljittää.
Joten sinulla on se; ongelmien todellinen syy on monimutkaisuuden lisääminen, joka tulee kaikista koodissamme olevista riippuvuuksista.
Hauska on, että tämä asia on ollut tiedossa jo vuosia. Se on yleinen anti-kuvio, jota kutsutaan 'suureksi mutapalloksi'. Olen nähnyt tämän tyyppisen arkkitehtuurin melkein kaikissa projekteissa, joiden parissa työskentelin vuosien varrella useissa eri yrityksissä.
Joten mikä tämä anti-malli tarkalleen on? Yksinkertaisesti sanottuna saat ison mutapallon, kun jokainen elementti on riippuvainen muista elementeistä. Alla näet kaavion tunnetun avoimen lähdekoodin projektin Apache Hadoop riippuvuuksista. Suuren mutapallon (tai pikemminkin suuren lankapallon) visualisoimiseksi piirrät ympyrän ja sijoitat projektin luokat tasaisesti siihen. Piirrä vain viiva jokaisen toisistaan riippuvan luokkaparin välille. Nyt näet ongelmiesi lähteen.
Joten esitin itselleni kysymyksen: Olisiko mahdollista vähentää monimutkaisuutta ja pitää silti hauskaa kuin projektin alussa? Totta puhuen, et voi poistaa kaikki monimutkaisuudesta. Jos haluat lisätä uusia ominaisuuksia, sinun on aina parannettava koodin monimutkaisuutta. Monimutkaisuutta voidaan kuitenkin siirtää ja erottaa.
Ajattele mekaanista teollisuutta. Kun joku pieni mekaaninen myymälä on luomassa koneita, he ostavat joukon vakioelementtejä, luovat muutaman mukautetun ja yhdistävät ne. He voivat tehdä nämä komponentit täysin erikseen ja koota kaiken loppuun, tekemällä vain muutaman muutoksen. Kuinka tämä on mahdollista? He tietävät, kuinka kukin elementti sopii yhteen asettamalla alan standardit, kuten pulttien koot, ja etupäätökset, kuten asennusreikien koko ja niiden välinen etäisyys.
Jokaisen yllä olevan kokoonpanon osan voi tarjota erillinen yritys, jolla ei ole mitään tietoa lopputuotteesta tai sen muista kappaleista. Niin kauan kuin kukin moduulielementti valmistetaan eritelmien mukaisesti, voit luoda lopullisen laitteen suunnitellusti.
Voimmeko toistaa sen ohjelmistoteollisuudessa?
Toki voimme! Käyttämällä rajapintoja ja ohjausperiaatteen kääntämistä; Parasta on se, että tätä lähestymistapaa voidaan käyttää kaikilla olio-kielillä: Java, C #, Swift, TypeScript, JavaScript, PHP - luetteloa voidaan jatkaa. Tämän menetelmän soveltamiseen ei tarvita hienoa kehystä. Sinun tarvitsee vain noudattaa muutamia yksinkertaisia sääntöjä ja pysyä kurinalaisena.
Kun kuulin ensimmäisen kerran hallinnan kääntämisestä, tajusin heti, että olin löytänyt ratkaisun. Se on käsite nykyisten riippuvuuksien ottamisesta ja niiden kääntämisestä käyttöliittymien avulla. Liitännät ovat yksinkertaisia metodien ilmoituksia. Ne eivät tarjoa mitään konkreettista toteutusta. Tämän seurauksena niitä voidaan käyttää kahden elementin välisenä sopimuksena niiden yhdistämisestä. Niitä voidaan käyttää moduuliliittiminä, jos haluat. Niin kauan kuin yksi elementti tarjoaa käyttöliittymän ja toinen elementti toteuttaa sen, he voivat työskennellä yhdessä tietämättä mitään toisistaan. Se on loistava.
Katsotaanpa yksinkertaisesta esimerkistä, kuinka voimme erottaa järjestelmämme modulaarisen koodin luomiseksi. Alla olevat kaaviot on toteutettu yksinkertaisina Java-sovelluksina. Löydät ne täältä GitHub-arkisto .
Oletetaan, että meillä on hyvin yksinkertainen sovellus, joka koostuu vain Main
luokka, kolme palvelua ja yksi Util
luokassa. Nämä elementit riippuvat toisistaan monin tavoin. Alla näet toteutuksen, joka käyttää 'isoa mutapalloa'. Luokat yksinkertaisesti soittavat toisilleen. Ne ovat tiukasti yhdistettyjä, et voi yksinkertaisesti ottaa yhtä elementtiä koskematta muihin. Tällä tyylillä luotujen sovellusten avulla voit aluksi kasvaa nopeasti. Uskon, että tämä tyyli soveltuu todistushankkeisiin, koska voit leikkiä asioiden kanssa helposti. Se ei kuitenkaan sovi tuotantovalmiisiin ratkaisuihin, koska jopa huolto voi olla vaarallista ja kaikki yksittäiset muutokset voivat aiheuttaa arvaamattomia virheitä. Alla oleva kaavio näyttää tämän suuren mutaarkkitehtuurin.
Paremman lähestymistavan etsimiseksi voimme käyttää tekniikkaa, jota kutsutaan riippuvuusinjektioksi. Tässä menetelmässä oletetaan, että kaikkia komponentteja tulisi käyttää rajapintojen kautta. Olen lukenut väitteitä siitä, että se irrottaa elementit, mutta onko se tosiaan? Ei. Katso alla olevaa kaaviota.
Ainoa ero tämänhetkisen tilanteen ja suuren mutapallon välillä on se, että nyt sen sijaan, että soittaisimme luokkiin suoraan, soitamme heille niiden rajapintojen kautta. Se parantaa elementtien erottamista toisistaan hieman. Jos esimerkiksi haluat käyttää Service A
toisessa projektissa voit tehdä sen ottamalla Service A
itse yhdessä Interface A
: n sekä Interface B
: n kanssa ja Interface Util
. Kuten näette, Service A
riippuu edelleen muista tekijöistä. Tämän seurauksena meillä on edelleen ongelmia koodin muuttamisessa yhdessä paikassa ja sekoittumisessa toisessa. Se aiheuttaa edelleen ongelman, että jos muokkaat Service B
ja Interface B
, sinun on vaihdettava kaikki siitä riippuvat elementit. Tämä lähestymistapa ei ratkaise mitään; mielestäni se vain lisää käyttöliittymän kerroksen elementtien päälle. Sinun ei pitäisi koskaan pistää mitään riippuvuuksia, vaan sinun pitäisi päästä eroon niistä lopullisesti. Hurra itsenäisyydelle!
Uskon, että lähestymistapa ratkaisee kaikki riippuvuuksien tärkeimmät päänsäryt, tekee sen käyttämättä riippuvuuksia lainkaan. Luot komponentin ja sen kuuntelijan. Kuuntelija on yksinkertainen käyttöliittymä. Aina kun haluat kutsua menetelmää nykyisen elementin ulkopuolelta, lisää yksinkertaisesti menetelmä kuuntelijaan ja kutsu se sen sijaan. Elementti saa käyttää vain tiedostoja, kutsumenetelmiä paketissaan ja käyttää pääkehyksen tai muiden käytettyjen kirjastojen tarjoamia luokkia. Alla näet kaavion sovelluksesta, joka on muokattu käyttämään elementtiarkkitehtuuria.
Huomaa, että tässä arkkitehtuurissa vain Main
luokassa on useita riippuvuuksia. Se yhdistää kaikki elementit yhteen ja kapseloi sovelluksen liiketoimintalogiikan.
Palvelut puolestaan ovat täysin itsenäisiä elementtejä. Nyt voit poistaa jokaisen palvelun tästä sovelluksesta ja käyttää niitä uudelleen muualla. Ne eivät ole riippuvaisia mistään muusta. Mutta odota, se paranee: Sinun ei tarvitse muokata näitä palveluja enää koskaan, kunhan et muuta niiden käyttäytymistä. Niin kauan kuin nuo palvelut tekevät mitä heidän pitäisi tehdä, ne voidaan jättää koskematta ajan loppuun asti. Ne voi luoda ammattilainen ohjelmistoinsinööri , tai ensimmäistä kertaa kooderi, joka vaarantaa kaikkien aikojen pahin spagettikoodin goto
lauseet sekoittuvat. Sillä ei ole merkitystä, koska niiden logiikka on koteloitu. Niin kauheaa kuin se saattaa olla, se ei koskaan levitä muille luokille. Tämä antaa sinulle myös voiman jakaa projektin työ useiden kehittäjien kesken, jolloin kukin kehittäjä voi työskennellä omalla komponentillaan itsenäisesti tarvitsematta keskeyttää toista tai edes tietäen muiden kehittäjien olemassaolosta.
Lopuksi voit aloittaa itsenäisen koodin kirjoittamisen vielä kerran, aivan kuten edellisen projektin alussa.
Määritellään rakennuselementtikuvio, jotta voimme luoda sen toistettavalla tavalla.
Elementin yksinkertaisin versio koostuu kahdesta asiasta: pääelementtiluokasta ja kuuntelijasta. Jos haluat käyttää elementtiä, sinun on toteutettava kuuntelija ja soitettava pääluokalle. Tässä on kaavio yksinkertaisimmasta kokoonpanosta:
On selvää, että sinun on lisättävä enemmän monimutkaisuutta elementtiin lopulta, mutta voit tehdä niin helposti. Varmista vain, että mikään logiikkaluokkaasi ei ole riippuvainen muista projektin tiedostoista. He voivat käyttää vain tämän elementin pääkehystä, tuotuja kirjastoja ja muita tiedostoja. Kun on kyse omaisuustiedostoista, kuten kuvista, näkymistä, äänistä jne., Ne tulisi myös kapseloida elementteihin, jotta tulevaisuudessa ne olisi helppo käyttää uudelleen. Voit kopioida koko kansion toiseen projektiin ja siinä se on!
Alla on esimerkkikaavio, joka näyttää edistyneemmän elementin. Huomaa, että se koostuu näkymästä, jota se käyttää, eikä se ole riippuvainen muista sovellustiedostoista. Jos haluat tietää yksinkertaisen menetelmän riippuvuuksien tarkistamiseksi, katso vain tuonti-osiota. Onko olemassa tiedostoja nykyisen elementin ulkopuolelta? Jos näin on, sinun on poistettava nuo riippuvuudet joko siirtämällä ne elementtiin tai lisäämällä sopiva kutsu kuuntelijalle.
Katsotaanpa myös yksinkertaista Java-sovelluksessa luotua 'Hello World' -esimerkkiä.
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(); } }
Aluksi määritämme ElementListener
määrittää tulosteiden tulostustavan. Itse elementti on määritelty alla. Kun soitat sayHello
elementille se vain tulostaa viestin käyttämällä ElementListener
. Huomaa, että elementti on täysin riippumaton printOutput
-sovelluksen toteutuksesta menetelmä. Se voidaan tulostaa konsoliin, fyysiseen tulostimeen tai hienoon käyttöliittymään. Elementti ei riipu toteutuksesta. Tämän abstraktion ansiosta tätä elementtiä voidaan käyttää uudelleen helposti eri sovelluksissa.
Katso nyt tärkein App
luokassa. Se toteuttaa kuuntelijan ja kokoaa elementin yhdessä konkreettisen toteutuksen kanssa. Nyt voimme alkaa käyttää sitä.
Voit käyttää tätä esimerkkiä myös JavaScriptissä täällä
Katsotaanpa, miten elementtikuviota käytetään laajamittaisissa sovelluksissa. Yksi asia on näyttää se pienessä projektissa - toinen on soveltaa sitä todelliseen maailmaan.
Haluan käyttää täyden pinon verkkosovelluksen rakenne näyttää seuraavalta:
src ├── client │ ├── app │ └── elements │ └── server ├── app └── elements
Lähdekoodikansiossa jaoimme aluksi asiakas- ja palvelintiedostot. Se on järkevä tehtävä, koska ne toimivat kahdessa eri ympäristössä: selaimessa ja taustapalvelimessa.
Sitten jaoimme koodin jokaisessa kerroksessa kansioihin, joita kutsutaan sovelluksiksi ja elementeiksi. Elements koostuu kansioista, joissa on itsenäisiä komponentteja, kun taas sovelluskansio yhdistää kaikki elementit yhteen ja tallentaa kaikki liiketoimintalogiikat.
Tällä tavoin elementtejä voidaan käyttää uudelleen eri projektien välillä, kun taas kaikki sovelluskohtaiset monimutkaisuudet on koteloitu yhteen kansioon ja usein vain yksinkertaisiin kutsuihin elementteihin.
Uskomalla, että käytäntö on aina trumpin teoriaa, katsotaanpa tosielämän esimerkkiä, joka on luotu Node.js: ssä ja TypeScriptissä.
Esimerkki todellisesta elämästä
Se on hyvin yksinkertainen verkkosovellus, jota voidaan käyttää lähtökohtana edistyneemmille ratkaisuille. Se noudattaa elementtiarkkitehtuuria sekä käyttää laajasti rakenteellista elementtikuviota.
Kohokohdista näet, että pääsivu on erotettu elementtinä. Tämä sivu sisältää oman näkymän. Joten kun haluat esimerkiksi käyttää sitä uudelleen, voit yksinkertaisesti kopioida koko kansion ja pudottaa sen toiseen projektiin. Johdot vain kaikki yhteen ja olet valmistautunut.
Se on perusesimerkki, joka osoittaa, että voit aloittaa elementtien esittelyn omassa sovelluksessasi tänään. Voit aloittaa erillisten komponenttien erottamisen ja erottaa niiden logiikan. Ei ole väliä kuinka sotkuinen koodi olet tällä hetkellä.
Toivon, että tämän uuden työkalusarjan avulla voit kehittää helpommin ylläpidettävää koodia. Ennen kuin ryhdyt käyttämään elementtikuviota käytännössä, kerrotaan nopeasti kaikki pääkohdat:
Paljon ongelmia ohjelmistoissa tapahtuu useiden komponenttien välisten riippuvuuksien takia.
Tekemällä muutoksen yhdessä paikassa voit ottaa käyttöön arvaamattoman käyttäytymisen muualla.
Kolme yleistä arkkitehtonista lähestymistapaa ovat:
Iso mutapallo. Se on erinomainen nopeaan kehitykseen, mutta ei niin suuri vakaisiin tuotantotarkoituksiin.
Riippuvuuden injektio. Se on puolivalmiita ratkaisuja, joita kannattaa välttää.
Elementtiarkkitehtuuri. Tämän ratkaisun avulla voit luoda itsenäisiä komponentteja ja käyttää niitä uudelleen muissa projekteissa. Se on ylläpidettävä ja loistava, jotta tuotanto pysyy vakaana.
Peruselementtimalli koostuu pääluokasta, jolla on kaikki tärkeimmät menetelmät, sekä kuuntelijasta, joka on yksinkertainen käyttöliittymä, joka mahdollistaa viestinnän ulkomaailman kanssa.
Täyden pinoelementin arkkitehtuurin saavuttamiseksi sinun on ensin erotettava käyttöliittymä taustakoodista. Sitten luot kullekin kansiota sovellukselle ja elementeille. Elementit-kansio koostuu kaikista itsenäisistä elementeistä, kun taas sovelluskansio yhdistää kaiken yhteen.
Nyt voit siirtyä luomaan ja jakamaan omia elementtejäsi. Pitkällä aikavälillä se auttaa luomaan helposti huollettavia tuotteita. Onnea ja kerro minulle, mitä olet luonut!
Lisäksi, jos huomaat optimoivasi koodia ennenaikaisesti, lue Kuinka välttää ennenaikaisen optimoinnin kirous kirjoittanut ApeeScapeer Kevin Bloch.
Liittyvät: JS: n parhaat käytännöt: Rakenna epäsopimusbotti TypeScript- ja riippuvuusinjektioillaKoodia voi olla vaikea ylläpitää useiden komponenttien välisten riippuvuuksien vuoksi. Seurauksena muutosten tekeminen yhdessä paikassa voi johtaa arvaamattomaan käyttäytymiseen muualla.
Modulaarinen arkkitehtuuri tarkoittaa sovelluksen jakamista itsenäisiksi elementeiksi. Tunnustamme kaikki hankkeiden väliset riippuvuudet vaikeiden ongelmien syyksi. Täydellinen riippumattomuus tekee näistä komponenteista erittäin helppoja testata, ylläpitää, jakaa ja käyttää uudelleen tulevaisuudessa.
Yleisiä arkkitehtonisia lähestymistapoja ovat: 1) Iso mutapallo: Erinomainen nopeaan kehitykseen, mutta ei niin sopiva vakaisiin tuotantotarkoituksiin. 2) Riippuvuusinjektio: Puolivalmiste, jota sinun tulisi välttää. 3) Elementtiarkkitehtuuri: Se on ylläpidettävä ja loistava, jotta tuotanto pysyy vakaana.