Rubiinimoodulid: kaasake vs ettevalmistamine vs laiendamine

Moodulid on Ruby üks huvitavamaid funktsioone. Saate neid kasutada klassidele konkreetse käitumise kinnitamiseks ja koodi korraldamiseks kompositsiooni, mitte pärimise asemel. Siin on lihtne näide:

Samuti kasutavad paljud kalliskivid mooduleid oma koodi korraldamiseks ja hõlpsamaks integreerimiseks teie rakendusse. Näiteks pakub Sidekiq pärl Sidekiq :: Worker moodulit käitumisharjumuste lisamiseks kohandatud klassidele ja nende kasutamiseks asünkroonsete töötajate komponentidena.

Ehkki hõlmamine on kõige tavalisem viis välise koodi klassisse importimiseks, pakub Ruby selle saavutamiseks ka kahte muud võimalust: laiendamine ja eeldamine. Kuid neil pole üldse sama käitumist ja Ruby arendajad mõistavad neid erinevusi sageli valesti.

Nende mõistmiseks peame kõigepealt põhjalikumalt uurima, kuidas Ruby lahendab meetodeid, mida käitusel teostada, kasutades midagi, mida nimetatakse esivanemate ahelaks.

Esivanemate kett

Kui Ruby klass luuakse, sisaldab ta konstantsete nimede loetelu, mis on tema esivanemad. Need on kõik klassid, millelt klass pärib, ja moodulid, mida nad kaasavad. Näiteks helistades esivanematele keelpilliklassi, saame selle esivanemate nimekirja:

> Keelpillid
=> [String, võrreldav, objekt, PP: ObjectMixin, kernel, BasicObject]

Näeme ahela ülaosas BasicObject, mis on Ruby-objektide hierarhia juur, ja ka Object, kõigi klasside superklass, mis hõlmab ka kerneli moodulit.

Kui nimetame stringi eksemplaril (või mõnel muul klassil) meetodit object_id, otsib Ruby klassi esivanemate kaudu meetodit object_id ja leiab lõpuks selle objektiklassis Object.

Kui helistate meetodile, mida kuskil pole määratletud, ei leia Ruby meetodit üheski esivanemate ahelas olevas klassis ega moodulis ja kutsub kokku meetodi Basic_bject välja laskmise, mis annab arendajale viimase võimaluse varukoodi täitmiseks. .

Teades Ruby klasside esivanemate ahela põhitõdesid, saame nüüd vaadata moodulite importimise erinevaid viise.

Kaasa

kaasamine on enimkasutatud ja lihtsaim moodulikoodi importimise viis. Klassimääratluses nimetades lisab Ruby mooduli klassi esivanemate ahelasse vahetult pärast selle ülemklassi. Tulles tagasi meie esimese näite juurde:

Kui vaatame Service klassi esivanemaid, näeme, et logimismoodul asub just klassi enda ja selle otsese superklassi vahel, mis on Objekt.

> Teenindajad
=> [Teenus, logimine, objekt, ...]

Seetõttu võime moodulis määratletud meetodeid nimetada klassi esinemisjuhtudeks. Ruby, kes neid meetodeid klassist ei leia, astub ahelas ülespoole, et leida need moodulilt.

Samuti väärib märkimist, et kahe või enama mooduli lisamisel sisestatakse viimane alati uuesti otse klassi ja ülejäänud ahela vahele:

Niisiis, sellises meetodikonflikti korral nagu selles näites, oleks esivanemate ahelas esimene reageeriv moodul viimati lisatud moodul Debug.

Pikenda

Teisest küljest impordib laiendus klassis tegelikult moodulmeetodid klassimeetoditena. Kui oleme oma näitesse lisamise asemel kasutanud laiendit, poleks logimismoodul sisestatud teenindusklassi esivanemate ahelasse. Nii et me poleks võinud logimeetodit nimetada ühelgi teenuse esinemisjuhul.

Selle asemel oleks Ruby lisanud mooduli Teenuse klassi singletonite klassi esivanemate ahelasse. Selles singleton-klassis (nimega #Service) on tegelikult määratletud teenuse klassi meetodid. Mooduli logimise meetodid oleksid siis olnud saadaval teenuse klassi meetoditena.

Siis oleksime võinud seda meetodit nimetada nii:

Service.log: info, "midagi juhtus"

Sageli soovite kasutada moodulit eksemplari meetodite importimiseks klassi, kuid samal ajal ka klassimeetodite määratlemiseks. Tavaliselt peaksite eksemplari meetodite importimiseks kasutama kahte erinevat moodulit, üks koos kaasamisega ja klassimeetodite määratlemiseks teine ​​laiendatud mooduliga.

Tavaline mõte ühe mooduli kasutamisel on mooduli kaasasoleva konksumeetodi kasutamine ja klassimeetodite importimine ka käitusel:

Nüüd, kui kaasame mooduli teenindusklassi, imporditakse mooduli meetodid klassi eksemplari meetoditena. Kaasatud meetodit nimetatakse ka koos argumendiga kaasamise klassi. Seejärel võime kutsuda selle laiendama, et importida alamooduli ClassMethods meetodid klassimeetoditena. Suhtlusring on täielik.

Valmistage

Alates Ruby 2-st saadaval on prepend rubyistidele natuke vähem teada kui tema kaks ülejäänud sõpra. See töötab tegelikult nagu kaasamine, välja arvatud see, et selle asemel, et sisestada moodul klassi ja selle superklassi vahele, lisab ta selle keti põhjale, isegi enne klassi ennast.

See tähendab, et klassi eksemplarile meetodi kutsumisel uurib Ruby enne klassi uurimist moodulimeetodeid. See erinev käitumisviis võimaldab teil olemasolevaid klasse kaunistada moodulitega ja rakendada nn loogikat:

Prependi kasutades sisestatakse moodul ServiceDebugger esivanemate ahela kõige alumisse ossa.

Saate seda ise uuesti kontrollida, helistades Service.ancestorsile:

> Teenindajad
=> [ServiceDebugger, teenus, objekt ...]

Teenuse eksemplaris käivitatud helistamine käivitab kõigepealt moodulis ServiceDebugger määratletud meetodi. Saame kasutada super, et helistada sama meetodit otsese esivanema ahelas ülespoole, mis on ise teenindusklass. Kasutasime seda käitumist ära, et kaunistada teenuse rakendamist väga lihtsal ja elegantsel viisil.

Täname, et lugesite ja rõõmsat Ruby-kodeerimist!

See postitus on avaldatud ka minu ajaveebis.
Kui teile see postitus meeldis, ärge unustage ♥ :)