Ohjelmiston säveltäminen: Johdanto

Tupakointikuutiot savuksi - MattysFlicks - (CC BY 2.0)
Huomaa: Tämä on osa ”Composing Software” -sarjaa (nyt kirja!), Joka käsittelee funktionaalisen ohjelmoinnin ja koostumustekniikan tekniikoiden käyttöä JavaScript ES6 +: ssa alusta alkaen. Pysy kanavalla. Tulevaa on paljon enemmän!
Osta kirja | Hakemisto | Seuraava>
Koostumus: ”Osien tai elementtien yhdistäminen kokonaisuudeksi.” ~ Dictionary.com

Ensimmäisessä lukion ohjelmointitunnissani sanottiin, että ohjelmistokehitys on ”monimutkaisen ongelman jakaminen pienemmiksi ongelmiksi ja yksinkertaisten ratkaisujen muodostaminen kokonaisen ratkaisun muodostamiseksi monimutkaiseen ongelmaan”.

Yksi elämäni suurimmista pahoittelustani on, että en ymmärtänyt oppitunnin merkitystä jo varhain. Olen oppinut ohjelmistosuunnittelun olemuksen aivan liian myöhään.

Olen haastatellut satoja kehittäjiä. Olen oppinut noista istunnoista, että en ole yksin. Hyvin harvat toimivat ohjelmistokehittäjät saavat hyvän käsityksen ohjelmistokehityksen ytimestä. He eivät ole tietoisia käytettävissä olevista tärkeimmistä työkaluista tai siitä, kuinka niitä hyödynnetään hyväksi. 100% on pyrkinyt vastaamaan yhteen tai molempiin tärkeimpiin ohjelmistokehityksen kysymyksiin:

  • Mikä on funktion koostumus?
  • Mikä on esineen koostumus?

Ongelmana on, että et voi välttää sävellystä vain siksi, että et tiedä sitä. Teet sen vielä - mutta teet sen huonosti. Voit kirjoittaa koodia lisää virheitä, ja vaikeuttaa muiden kehittäjien ymmärtämistä. Tämä on iso ongelma. Vaikutukset ovat erittäin kalliita. Vietämme enemmän aikaa ylläpitää ohjelmistoja kuin luomme sitä tyhjästä, ja virheemme vaikuttavat miljardeihin ihmisiin ympäri maailmaa.

Koko maailma toimii nykyään ohjelmistoilla. Jokainen uusi auto on pyörillä varustettu mini-super-tietokone, ja ohjelmistojen suunnittelun ongelmat aiheuttavat todellisia onnettomuuksia ja maksavat todellisia ihmishenkiä. Vuonna 2013 tuomaristo katsoi Toyota-ohjelmistokehitysryhmän syyllistyneen "holtittomaan laiminlyöntiin", kun onnettomuustutkinta paljasti spagettikoodin, jolla oli 10 000 globaalia muuttujaa.

Hakkerit ja hallitukset varastoivat vikoja vakoillakseen ihmisiä, varastaaksesi luottokortteja, hyödyntääkseen tietotekniikkaresursseja hajautettujen palvelunestohyökkäysten (DDoS) hyökkäysten käynnistämiseksi, salasanojen murtamiseksi ja jopa vaalien manipuloimiseksi.

Meidän on tehtävä paremmin.

Sinä kirjoitat ohjelmistoa joka päivä

Jos olet ohjelmistokehittäjä, sävelet toimintoja ja tietorakenteita päivittäin riippumatta siitä, tunnetko sen tai et. Voit tehdä sen tietoisesti (ja paremmin), tai voit tehdä sen vahingossa, kanavateipillä ja hulluilla liimoilla.

Ohjelmistokehitysprosessi jakautuu suuret ongelmat pienemmiksi ongelmiksi, rakentaa komponentit, jotka ratkaisevat nämä pienemmät ongelmat, ja sitten kompostoimaan nämä komponentit yhdessä muodostaen täydellisen sovelluksen.

Sävellystoiminnot

Funktion koostumus on prosessi, jossa funktio kohdistetaan toisen funktion tuotokseen. Kun algebralla on kaksi funktiota, kaista g, (f ∘ g) (x) = f (g (x)). Ympyrä on sävellysoperaattori. Se lausutaan yleisesti "koostuu" tai "jälkeen". Voit sanoa, että ääneen: "f koostuu g: llä on yhtä suuri kuin f x: sta", tai "f: n jälkeen g on yhtä suuri kuin x: n g". Sanomme f jälkeen g, koska g arvioidaan ensin, sitten sen tulos välitetään argumentiksi f: lle.

Aina kun kirjoitat tällaista koodia, sävelet toimintoja:

const g = n => n + 1;
const f = n => n * 2;
const doStuff = x => {
  const afterG = g (x);
  const afterF = f (afterG);
  paluu afterF;
};
doStuff (20); // 42

Aina kun kirjoitat lupausketjun, sävelet toimintoja:

const g = n => n + 1;
const f = n => n * 2;
const wait = time => uusi lupaus (
  (ratkaise, hylkää) => setTimeout (
    ratkaista,
    aika
  )
);
odota (300)
  . sitten (() => 20)
  .Tämän jälkeen (g)
  .Tämän jälkeen (f)
  . sitten (arvo => console.log (arvo)) // 42
;

Samoin joka kerta, kun ketjutat taulukkomenetelmäkutsuja, lodash-menetelmiä, havaittavissa olevia osia (RxJS jne.), Sävelet toimintoja. Jos ketjutat, sävelet. Jos siirrät palautusarvot muihin toimintoihin, sävelet. Jos soitat kahdelle menetelmälle peräkkäin, sinä sävelet käyttämällä tätä syöttötietoina.

Jos ketjutat, sävelet.

Kun sävelet toimintoja tahallaan, teet sen paremmin.

Yhdistämällä toiminnot tarkoituksellisesti, voimme parantaa doStuff () -toimintoamme yksinkertaisella yksivuorauksella:

const g = n => n + 1;
const f = n => n * 2;
const doStuffBetter = x => f (g (x));
doStuffBetter (20); // 42

Tyypillinen vastaväite tälle lomakkeelle on, että sitä on vaikeampi debugoida. Esimerkiksi, kuinka me kirjoittaisimme tämän funktion koostumuksella?

const doStuff = x => {
  const afterG = g (x);
  console.log (`jälkeen g: $ {afterG}`);
  const afterF = f (afterG);
  console.log (`jälkeen f: $ {afterF}`);
  paluu afterF;
};
doStuff (20); // =>
/ *
"jälkeen g: 21"
"jälkeen f: 42"
* /

Ensin abstrakti, että “f: n jälkeen”, “g: n jälkeen” kirjautumalla pieneen apuohjelmaan, nimeltään trace ():

const trace = label => value => {
  console.log (`$ {label}: $ {value}`);
  palautusarvo;
};

Nyt voimme käyttää sitä seuraavasti:

const doStuff = x => {
  const afterG = g (x);
  jälki ('jälkeen g') (jälkeenG);
  const afterF = f (afterG);
  jälki ('f: n jälkeen') (afterF);
  paluu afterF;
};
doStuff (20); // =>
/ *
"jälkeen g: 21"
"jälkeen f: 42"
* /

Suositut toiminnalliset ohjelmointikirjastot, kuten Lodash ja Ramda, sisältävät apuohjelmia toimintojen koostumuksen helpottamiseksi. Voit kirjoittaa yllä olevan toiminnon uudelleen seuraavasti:

tuontiputki tuotteesta 'lodash / fp / flow';
const doStuffBetter = putki (
  g,
  jälki ('g: n jälkeen'),
  f,
  jäljittää ('f jälkeen')
);
doStuffBetter (20); // =>
/ *
"jälkeen g: 21"
"jälkeen f: 42"
* /

Jos haluat kokeilla tätä koodia tuomatta jotain, voit määritellä putken seuraavasti:

// putki (... fns: [... toiminto]) => x => y
const pipe = (... fns) => x => fns.reduce ((y, f) => f (y), x);

Älä huolestu, jos et vielä seuraa, miten se toimii. Myöhemmin tutkimme toimintojen koostumusta paljon yksityiskohtaisemmin. Itse asiassa se on niin välttämätöntä, että se on määritelty ja esitelty useita kertoja koko tekstissä. Asia on auttaa sinua tuntemaan sen niin, että sen määritelmä ja käyttö muuttuu automaattiseksi. Ole yksi sävellyksen kanssa.

putki () luo funktioiden putkilinjan, joka siirtää yhden funktion lähdön toisen tuloon. Kun käytät putkea () (ja sen kaksoset, kirjoita ()), et tarvitse välimuuttujia. Toimintojen kirjoittamista mainitsematta argumentteja kutsutaan pisteettömäksi tyyliksi. Voit tehdä sen, kun soitat funktiolle, joka palauttaa uuden funktion sen sijaan, että julistaisit funktion nimenomaisesti. Tämä tarkoittaa, että et tarvitse toimintoavainsanaa tai nuolen syntaksia (=>).

Pisteettömä tyyli voidaan viedä liian pitkälle, mutta vähän täältä ja täältä on hienoa, koska nämä välittäjämuuttujat lisäävät tarpeetonta monimutkaisuutta toimintoihisi.

Alentuneella monimutkaisuudella on useita etuja:

Työmuisti

Keskimääräisillä ihmisen aivoilla on vain muutama jaettu resurssi erillisiin kvantteihin työmuistissa, ja jokainen muuttuja mahdollisesti kuluttaa yhden näistä kvanteista. Kun lisäät lisää muuttujia, kykymme muistaa tarkkaan kunkin muuttujan merkitys heikkenee. Työmuistimallit sisältävät tyypillisesti 4–7 diskreettiä kvantteja. Näiden lukujen yläpuolella virhetaso nousee dramaattisesti.

Putkimuotoa käyttämällä poistimme 3 muuttujaa - vapauttaen melkein puolet käytettävissä olevasta työmuististamme muihin asioihin. Se vähentää kognitiivista kuormitustamme merkittävästi. Ohjelmistokehittäjät pyrkivät paremmin hajottamaan tietoja työmuistiin kuin tavallinen henkilö, mutta eivät niin paljon, että heikentävät säilyttämisen merkitystä.

Signaalin melusuhde

Tiivis koodi parantaa myös koodisi signaali-kohinasuhdetta. Se on kuin radion kuuntelu - kun radiota ei ole viritetty oikein asemalle, saat paljon häiritsevää ääntä, ja musiikin kuuleminen on vaikeampaa. Kun virität sitä oikealle asemalle, kohina häviää ja saat voimakkaamman musiikkisignaalin.

Koodi on sama tapa. Tiivisempi koodinilmentäminen parantaa ymmärrystä. Jotkut koodit antavat meille hyödyllistä tietoa, ja jotkut koodit vievät vain tilaa. Jos voit vähentää käytetyn koodin määrää vähentämättä lähetetyn merkityksen, helpotat koodin jäsentämistä ja ymmärtämistä muille ihmisille, joiden on luettava se.

Virheiden pinta-ala

Katso ennen ja jälkeen toimintoja. Näyttää siltä, ​​että toiminto meni ruokavalioon ja menetti tonnin painoa. Se on tärkeää, koska ylimääräinen koodi tarkoittaa ylimääräistä pinta-alaa virheiden piilottamiseen, mikä tarkoittaa sitä, että enemmän virheitä piiloutuu siihen.

Vähemmän koodia = vähemmän virheiden pinta-alaa = vähemmän virheitä.

Objektien säveltäminen

”Suosittele objektikoostumusta luokan perinnölle” Neljä ryhmää, ”Suunnittelumallit: uudelleenkäytettävien esinekeskeisten ohjelmistojen elementit”
”Tietotekniikassa yhdistelmädatatyyppi tai yhdistetty tietotyyppi on mitä tahansa tietotyyppiä, joka voidaan rakentaa ohjelmassa käyttämällä ohjelmointikielen primitiivisiä tietotyyppejä ja muita yhdistelmätyyppejä. […] Yhdistelmätyypin rakentamista kutsutaan koostumukseksi. ”~ Wikipedia

Nämä ovat alkeellisia:

const firstName = 'Claude';
const lastName = 'Debussy';

Ja tämä on yhdistelmä:

const fullName = {
  etunimi,
  sukunimi
};

Samoin kaikki taulukot, sarjat, kartat, WeakMaps, TypedArrays jne. Ovat yhdistelmädatatyyppejä. Aina kun rakennat mitään primitiivistä tietorakennetta, suoritat jonkinlaista objektikoostumusta.

Huomaa, että Gang of Four määrittelee kuvion, jota kutsutaan yhdistelmäkuviona, joka on erityinen rekursiivisen objektikoostumuksen tyyppi, jonka avulla voit käsitellä yksittäisiä komponentteja ja yhdistelmäkomposiitteja identtisesti. Jotkut kehittäjät sekoittuvat ajatellen, että komposiittikuvio on ainoa esinekoostumuksen muoto. Älä sekoita. Objektikoostumuksia on monia erilaisia.

Neljän hengen ryhmä jatkaa, "näet objektien koostumuksen sovellettavan uudestaan ​​ja uudestaan ​​suunnittelumallien mukaan", ja sitten ne luetteloivat kolmenlaisia ​​esineiden koostumussuhteita, mukaan lukien delegointi (sellaisena kuin sitä käytetään tila-, strategia- ja vierailijamalleissa), tutustuminen (kun objekti tietää toisesta objektista viittauksella, yleensä välitetään parametrina: käyttö-suhde, esim. verkkopyyntöjen käsittelijälle voidaan lähettää viittaus hakijaan pyynnön kirjaamiseksi - pyynnön käsittelijä käyttää hakijaa) ja aggregointi (kun lapsiobjektit ovat osa vanhempiobjektia: a-on-suhde, esim. DOM-lapset ovat DOM-solmun komponenttiosia - DOM-solmulla on lapsia).

Luokan perintöä voidaan käyttää yhdistelmäobjektien rakentamiseen, mutta se on rajoittava ja hauras tapa tehdä se. Kun Neljän ryhmän sanonta "suosii esineiden koostumusta luokan perinnölle", he suosittelevat sinua käyttämään joustavia lähestymistapoja komposiittikohteiden rakentamiseen, eikä jäykkään, tiukasti kytkettyyn lähestymistapaan luokan perinnöllä.

Käytämme objektien koostumuksen yleisempää määritelmää julkaisusta “Tietotekniikan kategoriset menetelmät: topologian näkökohtien kanssa” (1989):

"Yhdistelmäobjektit muodostetaan asettamalla esineet yhteen siten, että jokainen jälkimmäisistä on" osa "entistä."

Toinen hyvä viite on ”Luotettava ohjelmisto komposiittisuunnittelun avulla”, Glenford J Myers, 1975. Molemmat kirjat ovat jo kauan tulostettuja, mutta voit silti löytää myyjiä Amazonista tai eBaysta, jos haluat tutustua kohteen koostumuksen aiheeseen lisää tekninen syvyys.

Luokan perintö on vain yhdenlaista komposiittikohteen rakentamista. Kaikki luokat tuottavat yhdistelmäobjekteja, mutta kaikkia yhdistelmäobjekteja ei tuoteta luokilla tai luokan perinnöllä. ”Suosikkikohteen koostumus luokan perinnöllisyyden yläpuolella” tarkoittaa, että sinun tulisi muodostaa yhdistelmäobjekteja pienistä komponenttiosista sen sijaan, että perisi kaikki ominaisuudet esi-isältä luokkahierarkiassa. Jälkimmäinen aiheuttaa laajan valikoiman tunnettuja ongelmia olosuhteiden suunnittelussa:

  • Tiukka kytkentäongelma: Koska lasten luokat ovat riippuvaisia ​​vanhemman luokan toteutuksesta, luokan perintö on tiukinta kytkentäkohdetta, joka on saatavana olosuhteisiin suuntautuvassa suunnittelussa.
  • Hauras pohjaluokkaongelma: Tiukan kytkennän takia kantaluokan muutokset voivat rikkoa suuren määrän jälkeläisiä - mahdollisesti kolmansien osapuolten hallinnassa koodissa. Kirjailija voi rikkoa koodin, jota he eivät ole tietoisia.
  • Joustamaton hierarkiaongelma: Yhden esi-isän taksonomialla, kun otetaan huomioon riittävästi aikaa ja kehitystä, kaikki luokan taksonomiat ovat lopulta vääriä uusissa käyttötapauksissa.
  • Päällekkäisyys välttämättömyyden vuoksi: Joustamattomien hierarkioiden takia uudet käyttötapaukset toteutetaan usein päällekkäisyyksillä pikemminkin kuin laajennuksilla, mikä johtaa vastaaviin luokkiin, jotka odottamatta eroavat toisistaan. Kun päällekkäisyys on asetettu, ei ole selvää, mistä luokasta uusien luokkien tulisi laskea tai miksi.
  • Gorilla- / banaaniongelma: ”... olio-ongelmien aiheena on, että heillä on kaikki tämä implisiittinen ympäristö, jota he kantavat mukanaan. Halusit banaania, mutta sait vain gorillan, joka piti banaanin ja koko viidakon. "~ Joe Armstrong," Coders at Work "

Yleisin esinekoostumuksen muoto JavaScriptissä tunnetaan nimellä esineiden liittäminen (aka mixin-koostumus). Se toimii kuin jäätelö. Aloitat esineellä (kuten vaniljajäätelö) ja sekoitat sitten haluamasi ominaisuudet. Lisää joitakin pähkinöitä, karamellia, suklaapirrettä ja päättele pähkinäisellä karamellisuklaakermospäröjäätelöllä.

Yhdistelmärakenteiden rakentaminen luokan perinnöllä:

luokan Foo {
  rakentaja () {
    tämä.a = 'a'
  }
}
luokan palkki laajentaa Fooa {
  rakentaja (vaihtoehdot) {
    Super (optiot);
    tämä.b = 'b'
  }
}
const myBar = uusi palkki (); // {a: 'a', b: 'b'}

Komposiittien rakentaminen sekoituskoostumuksella:

const a = {
  a: 'a'
};
const b = {
  b: 'b'
};
const c = {... a, ... b}; // {a: 'a', b: 'b'}

Tutkimme tarkemmin muita esineiden koostumustyylejä myöhemmin. Nyt sinun ymmärryksesi tulisi olla:

  1. On enemmän kuin yksi tapa tehdä se.
  2. Jotkut tavat ovat parempia kuin toiset.
  3. Haluat valita yksinkertaisimman ja joustavimman ratkaisun käsillä olevaan tehtävään.

johtopäätös

Kyse ei ole toiminnallisesta ohjelmoinnista (FP) vs. olio-ohjelmoinnista (OOP) tai yhdestä kielestä toiseen. Komponentit voivat olla toimintojen, tietorakenteiden, luokkien jne. Muodossa. Eri ohjelmointikielet antavat komponentteille yleensä erilaisia ​​atomielementtejä. Java tarjoaa luokkia, Haskell tarjoaa toimintoja jne. Mutta riippumatta siitä, mitä kieltä ja paradigmaa suosit, et voi päästä eroon funktioiden ja tietorakenteiden kirjoittamisesta. Loppujen lopuksi siihen kaikki juontaa.

Puhumme paljon toiminnallisesta ohjelmoinnista, koska toiminnot ovat helpoin kirjoittaa JavaScriptiin ja toiminnallinen ohjelmointiyhteisö on investoinut paljon aikaa ja vaivaa toimintojen koostumustekniikoiden virallistamiseen.

Mitä emme tee, on sanoa, että toiminnallinen ohjelmointi on parempi kuin olio-ohjelmointi tai että sinun on valittava toinen toisensa yli. OOP vs. FP on väärä kaksijakoisuus. Jokainen todellinen Javascript-sovellus, jonka olen nähnyt viime vuosina, sekoittaa FP: tä ja OOP: ta laajasti.

Käytämme objektikoostumusta tietotyyppien tuottamiseen toiminnallista ohjelmointia varten ja toiminnallista ohjelmointia objektien tuottamiseksi OOP: lle.

Riippumatta siitä, kuinka kirjoitat ohjelmistoa, sinun tulisi säveltää se hyvin.

Ohjelmistokehityksen ydin on sävellys.

Ohjelmistokehittäjä, joka ei ymmärrä koostumusta, on kuin kodinrakentaja, joka ei tiedä pulteista tai nauloista. Rakennusohjelmistot ilman tietoisuutta koostumuksesta ovat kuin kodinrakentaja, joka laittaa seinät yhdessä kanavateipin ja hullujen liimojen kanssa.

On aika yksinkertaistaa, ja paras tapa yksinkertaistaa on päästä ydimeen. Ongelmana on, että melkein kenelläkään teollisuudessa ei ole hyvää käsittelyä välttämättömyydelle. Me alana olemme epäonnistuneet teitä, ohjelmistokehittäjä. Alan vastuumme on kouluttaa kehittäjiä paremmin. Meidän on parannettava. Meidän on otettava vastuu. Kaikki toimii nykyään ohjelmistoilla, taloudesta lääkinnällisiin laitteisiin. Tällä planeetalla ei ole kirjaimellisesti ihmisen elämän nurkkaa, johon ohjelmistomme laatu ei vaikuta. Meidän on tiedettävä, mitä teemme.

On aika oppia säveltämään ohjelmistoja.

Osta kirja | Hakemisto | Seuraava>

Lisätietoja EricElliottJS.com

EricElliottJS.com-sivuston jäsenille on tarjolla videotunteja interaktiivisilla koodihaasteilla. Jos et ole jäsen, kirjaudu tänään.

Eric Elliott on hajautettu järjestelmäasiantuntija ja kirjojen kirjoittaminen “Composing Software” ja “Programming JavaScript Applications”. DevAnywhere.io: n perustajana hän opettaa kehittäjille taitoja, joita he tarvitsevat etätyöskentelyyn ja työ- ja yksityiselämän tasapainon omaksumiseen. Hän rakentaa ja neuvoo kehitystyöryhmiä salausprojekteihin ja on osallistunut ohjelmistokokemuksiin Adobe Systemsille, Zumba Fitnessille, The Wall Street Journalille, ESPN: lle, BBC: lle ja huipputalostajille, mukaan lukien Usher, Frank Ocean, Metallica ja monille muille.

Hän nauttii kauko-elämäntavasta maailman kauneimman naisen kanssa.