Riik vs ametlik näitleja

Sissejuhatus

Kui ma Akka kasutama hakkasin, oli see üldine lähenemisviis hoida näitlejas olek koos äriloogikaga. Hiljem avastasin selle lähenemisviisi puudused, loogikat on raske taaskasutada, lisada uusi funktsioone, testimine nõuab Akka kasutamist ja ma testin kõrvalmõjusid. Nende probleemide lahendamiseks olen proovinud kasutada riigimonadi. Selles ajaveebipostituses kirjeldatakse refaktoriseerimise protsessi.

Ettevõtte domeen

See näidisprojekt (link githubi) on müügiautomaadi lihtne rakendamine Akka abil. See simuleerib müügiautomaati programmiga, mis kasutajaga terminali kaudu suhtleb. Kasutaja saab algatada tavatoiminguid, nagu mündi sisestamine (+1, +5,…), toote valimine (1, 2,…) või raha väljavõtmine (-).

Müügiautomaadi näitlejal on järgmised omadused:

  • jälgida krediidi summa
  • jälgige toodete kogust ja aegumiskuupäeva
  • müüa toodet
  • kasutajate valimisel raha välja võtma
  • teatage omanikule, kui müügiautomaadil on toode otsa saanud, toote aegumiskuupäev on vahemikust väljas või kasti on peaaegu täis

See on üsna pikk funktsioonide loetelu ja loodetavasti on neil ühikatestid.

https://unsplash.com/photos/5MKCA4STlVM

Aluse rakendamine

Arhitektuur

Müügiautomaadi näitleja vastutab müügiautomaadi oleku hoidmise, kasutajalt saadud sisendite töötlemise ja toimingute saatmise eest kasutaja väljundite ja süsteemiaruannete osalejatele. Näiteks kui kasutaja ostab toote, värskendab müügiautomaadi näitleja laokogust, saadab kasutaja väljunditele teate toote vabastamiseks ja muudatuste pakkumiseks, siis kui toode on välja müüdud, saab ta saata süsteemi aruannetesse sõnumi.

Arhitektuur enne refaktoriseerimist

Müügiautomaadi näitleja sisaldab mõned variandid oleku hoidmiseks. Var'ide kõrval on ka palju loogikat omav vastuvõtuplokk. Allpool olev fragment näitab olulist. Märkimist väärib loogika (read 14–19) segamine ja kõrvalmõju teostamine (read 21–23 sisemise oleku värskendamiseks, read 27–31 sõnumite saatmiseks).

Testimine

Vaatame testi. Nagu näete, kasutan Akka Testkit. Allolev kood kinnitab õnneliku tee stsenaariumi, kui kasutajad ostavad toote sahtlist „1”.

Sellel koodil on mõned puudused. Esiteks peame ärikoodi testimiseks kasutama Akka-d. See nõuab näitlejasüsteemi käivitamist ja see aeglustab testimist. Teiseks peame seadistama näitleja sisemise oleku, saates sõnumeid (underTest! Credit (10)) ja veendudes, et sõnumid töödeldakse (userOutput.expectMsg (CreditInfo (10))). Kolmandaks, testime kõrvaltoimeid. Selle testi väide kinnitab, et VendingMachineActor on saatnud UserOutputsActorile teate GiveProductAndChange (õlu, 7). Lisaks, kui tahame kinnitada näitleja sisemist olekut, peame käsitlema testisõnumit GetState.

Osades testides on ka probleem, et nende väide on „mingeid kõrvalmõjusid ei täideta”. Sellisel juhul kasutame AkkaTestkitist loodud ootNoMessage (). Vaikimisi ootab see kõne 3 sekundit, veendumaks, et ühtegi teadet ei saadetud. See aeglustab meie ehitust. Saame seda aega lühendada, kuid see võib muuta testid ebaühtlaseks.

VendingMachineActoris rakendatud äriloogika sõltub Akkast. Sellel näitlejal on suur vastuvõtuplokk ja mõni riik, et riiki hoida. Koodi lisamine või muutmine eeldab kogu näitleja klassi mõistmist.

Refaktoriseerimine riigimonadiks

Mis on riigimonad

Olekimonad on funktsioon, mis võtab vastu olekud ja tagastab paar uut olekut ja efekte:

Olek -> (olek, efekt)

Osariigi monad on monad, seega pakub see meetod flatMap. Tänu sellele saame moodustada riigimonad:

Umbes mõistmiseks on uus riigimonad üles ehitatud (kuid veel mitte käivitatud). Algse oleku rakendamine seadmel stateMonad1 annab efekti1 ja uue oleku (nimetagem seda olekuks1). Efekt omistatakse efektile1. Järgmisena kutsutakse stateMonad2 argumendina olekuga1 ja efekt omistatakse efektile2. Saagikusektsioonis saame lõpptulemuse luua, kasutades allpool toodud sammude tulemusi.

Nüüd saame käivitada newMonadi argumendina InitState. Selle tulemusel saame uue oleku ja efektid.

Riikliku monaadi kasutamise üksikasjad kassidel leiate raamatust “Scala kassidega” (saadaval siit).

Uue loogika täiendamine vanaga

Olen otsustanud olemasoleva muutmise asemel luua uue näitleja (et saaksin neid kahte teostust võrrelda). Uus teostus kasutab näitlejat (SmActor), et hoida olekut kõnede, suhtluse ja helistatava riigimonadi vahel. Kogu äriloogika, mida tahan riigimonadina rakendada. SmActoril on sama API, nii et saan olemasolevaid teste uuesti kasutada. Sel viisil saan kontrollida, kas mõlemad rakendused toimivad samamoodi. Hiljem kirjutan testid riigimonade jaoks.

Arhitektuur pärast refaktoriseerimist

Selles lähenemisviisis hoiab näitleja ainult ühte muutujat - olekut. See olek koosneb kõigist andmetest, mida riigimonad peavad töötlema. Allpool on määratletud olekuklass:

SmActoris määratlesin ma lihtsalt ühe varia olekuga:

Nüüd saan vastuvõtuplokis juhtumeid vähendada:

Kõigi äriürituste puhul on üks juhtum. Näitleja sisaldab testide ühilduvuse jaoks ka juhtumit GetState.

Objekti VendingMachineSm koos meetodiga komponeerimine puudub. Alustame juurutamist. Nagu ülalpool näidatud, saame loogikat rakendada mõne riigimonadina ja ühendada need komponeerimismeetodil. Esiteks tahan määratleda tagastamise tüübi:

Loogika rakendamine võib jagada väiksemateks monaadideks ja hiljem kombineerida. Esimene rakendamisviis on krediidi värskendamine, kui keegi mündi sisestab või välja võtab:

Pärast mündi sisestamist (krediit (väärtus)) krediiti suurendatakse ja väljundina luuakse kasutajale teade praeguse krediidi kohta. Väärib märkimist, et sellel hetkel ei teostata ühtegi kõrvalmõju (sõnumite saatmine teistele osalistele).

Kui kasutajad otsustavad raha välja võtta, uuendatakse olekut krediidivabalt ja luuakse teade kasutajale. Kõigi muude toimingute korral võime oleku taastada ilma muudatuste ja tulemusteta.

Nüüd saame rakenduse valideerida testiga:

Nagu näete, on krediidi värskendamise loogika lahutatud.

Ülejäänud loogikat saab samal viisil rakendada (kontrollige loogikat ja teste). Kogu protsessi loomiseks saame kasutada kõiki väikeseid riigimonade. Selleks kasutame mõistmist. Osariiki muudetakse ja antakse üle järgmisele riigimonadile. Tulemused koguti ja ühendati saagikus. Sellegipoolest ei toimunud riigimonadi hukkamist. Oleme just suurema ehitanud.

Testimine FP viisil

Selle teostuse abil saame testida väikeste riigimonadide ja nende moodustamise loogikat. Need testid ei avalda kõrvaltoimeid. Osariigi monad tagastab kõik tagastatud efektid. Sel viisil on kinnitaval meetodil kõik andmed, me ei pea muretsema pilkade või TestActorRefi kontrollimise pärast kõrvaltoimete osas. Näitlejatestides testisime, et teatud aja jooksul ei teostataks ühtegi kõrvaltoimet, FP-ga peame lihtsalt kontrollima saadud tulemust. Lisaks ei pea me katsetamiseks käivitama näitlejasüsteeme. Seda kombineerides on test palju kiirem.

Samuti saame hõlpsalt käivitada mitu toimingut ja kontrollida vahetulemusi:

Näitlejatesti puhul on proovimeetodi vastuvõtmise meetod. vastuvõtt on varjunimi Any => Unit. See tähendab, et tundmatu kõrvaltoime võib toimuda. Puhta FP kasutamisel saadakse testitud meetodil kogu tulemus. Meie puhul on täitmiseks uus olek ja efektid (olek => (olek, efekt)). Sel juhul on lihtne kontrollida, kas tagastatud väärtus vastab ootustele.

Loogika ja oleku eraldamine toob testimiseks kasu. Me ei pea seadistama (katsetatava objekti) sisemist olekut, helistades selle meetoditele (või saatma sõnumeid). Kui tahame testida loobumise stsenaariumi, peame meie sisestama mündi müügiautomaati. Keerukama stsenaariumi korral võib sisemise oleku ettevalmistamiseks vaja minna rohkem samme. Kui loogikaga objekt on kodakondsuseta, võime lihtsalt ühe argumendina edastada oleku.

Kasu

Nagu ma eespool näitasin, saame FP abil oma programmi koostada väiksemate osadega. Keskendudes ainult kindlale funktsionaalsusele, saavad monad keeruka loogika loomiseks aheldatud.

Loogika väiksemateks osadeks jagamine võimaldab meil katsetada lihtsamaid funktsioone. Saame testida ka keerukamat loogikat, mis on ehitatud väiksematest osadest. Äriloogika ei sõltu sellistest raamistikest nagu Akka ja lõimedest. Seda saab hõlpsasti testida.

Loogika väiksemaid osi saab programmi erinevates osades uuesti kasutada. Nende väiksemate osade abil saab luua keeruka loogika. Kui meie funktsioonid on puhtad (puuduvad kõrvaltoimed ja muteeruvad olekud), oleme kindlad, et nende funktsioonide ühendamine ei too ootamatut käitumist.

Nagu ma eespool mainisin, ei tohiks meie äriloogikal olla mingeid kõrvalmõjusid. Kõik täidetavad efektid tagastatakse meetodil ja me saame väljundi kinnitada ilma riskita, et olulised üksikasjad puuduvad.

Samuti on meie testid palju kiiremad, kuna me ei pea käivitama Actor Systemi ega kontrollima, et ühtegi kõrvalmõju ei täidetaks.

Kokkuvõte

Oleme liikunud äriloogika näitlejalt eraldi objektile. Näitleja vastutab ainult kõnede ja keermesünkroonimise vahel olekute hoidmise eest. Tänu riigimonadile jagati äriloogika väiksemateks osadeks. Neid väiksemaid osi kasutatakse keeruka loogika loomiseks. Testid süttivad kiiresti.

Lingid:

  • Projekt Githubis https://github.com/otrebski/state-monad
  • Scala kassidega raamat https://underscore.io/books/scala-with-cats/