Ez a dokumentum a kezdő Scala3 fejlesztőknek szól, akik már járatosak a Scala prózában, de értetlenül állnak minden `implicits
` és paraméterezett tulajdonságok a kódban.
Ez a dokumentum elmagyarázza, miért, hogyan, hol és mikor Típusosztályok (TC).
A dokumentum elolvasása után a kezdő Scala3 fejlesztő komoly ismereteket szerez a használatához, és belemerül a forráskódba nagyon a Scala könyvtárak közül, és kezdje el írni az idiomatikus Scala kódot.
Kezdjük azzal, hogy miért…
A kifejezési probléma
A 1998, Philip Wadler kijelentette hogy „a kifejezési probléma egy régi probléma új neve”. Ez a szoftver bővíthetőség problémája. Wadler úr szerint a kifejezési probléma megoldásának meg kell felelnie a következő szabályoknak:
- 1. szabály: Engedélyezze a végrehajtását meglévő viselkedések (gondoljunk a Scala-vonásra), amelyre alkalmazni kell új ábrázolások (gondolj egy esetosztályra)
- 2. szabály: Engedélyezze a végrehajtását új viselkedések kell alkalmazni meglévő reprezentációk
- 3. szabály: Nem veszélyeztetheti a típusú biztonság
- 4. szabály: Nem szükséges újrafordítani meglévő kódot
A probléma megoldása lesz ennek a cikknek az ezüstszála.
1. szabály: a meglévő viselkedés megvalósítása új ábrázoláson
Bármely objektumorientált nyelv rendelkezik beépített megoldással az 1. szabályhoz altípus polimorfizmus. Bármelyik `-t nyugodtan megvalósíthatjatrait
` egy `-től való függésben van meghatározvaclass
` a saját kódjában, a függőség újrafordítása nélkül. Lássuk működés közben:
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
val eth = Lib1.Ethereum()
val btc = Lib1.Bitcoin()
val dot = Lib2.Polkadot()
Ebben a fiktív példában a library `Lib1
` (5. sor) egy tulajdonságot határoz megBlockchain
` (6. sor) annak 2 megvalósításával (9. és 12. sor). `Lib1
` változatlan marad ebben a dokumentumban (a 4. szabály érvényesítése).
`Lib2
` (15. sor) a meglévő viselkedést valósítja meg `Blockchain
`egy új osztályon`Polkadot
` (1. szabály) típusbiztonságos (3. szabály) módon, újrafordítás nélkülLib1
` (4. szabály).
2. szabály: a meglévő reprezentációkra alkalmazandó új viselkedések megvalósítása
Képzeljük el `-banLib2
`új viselkedést akarunk`lastBlock
` minden egyes esetében külön kell végrehajtaniBlockchain
`.
Az első dolog, ami eszünkbe jut, egy nagy kapcsoló létrehozása a paraméter típusa alapján.
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
def lastBlock(blockchain: Blockchain): Block = blockchain match
case _:Ethereum => todo
case _:Bitcoin => todo
case _:Polkadot => todo
object Lib3:
import Lib1.*
case class Polygon() extends Blockchain:
override def getBlock(height: Height): Block = todo
import Lib1.*, Lib2.*, Lib3.*
println(lastBlock(Bitcoin()))
println(lastBlock(Ethereum()))
println(lastBlock(Polkadot()))
println(lastBlock(Polygon()))
Ez a megoldás a típusalapú polimorfizmus gyenge reimplementációja, amely már be van sütött nyelven!
`Lib1
` érintetlenül marad (ne feledje, a 4-es szabály érvényesül az egész dokumentumban).
A `Lib2
` van rendben van amíg egy újabb blokkláncot be nem vezetnek a `-baLib3
`. Sérti a típusbiztonsági szabályt (3. szabály), mert ez a kód futás közben meghibásodik a 37. sorban. És módosítja a `Lib2
` sértené a 4. szabályt.
Egy másik megoldás egy `extension
`.
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
def lastBlock(): Block = todo
extension (eth: Ethereum) def lastBlock(): Block = todo
extension (btc: Bitcoin) def lastBlock(): Block = todo
import Lib1.*, Lib2.*
println(Bitcoin().lastBlock())
println(Ethereum().lastBlock())
println(Polkadot().lastBlock())
def polymorphic(blockchain: Blockchain) =
// blockchain.lastBlock()
???
`Lib1
` érintetlenül marad (a 4. szabály érvényesítése a teljes dokumentumban).
`Lib2
` határozza meg a viselkedést a típusának (21. sor), a `kiterjesztésnek' pedig a meglévő típusoknak (23. és 25. sor).
28-30. sorok, az új viselkedés minden osztályban használható.
De nincs mód arra, hogy ezt az új viselkedést polimorf módon nevezzük (32. sor). Minden erre irányuló kísérlet fordítási hibákhoz (33. sor) vagy típusalapú kapcsolókhoz vezet.
Ez a 2. szabály trükkös. Megpróbáltuk megvalósítani a saját polimorfizmus-definíciónkkal és "kiterjesztési" trükkünkkel. És ez furcsa volt.
Hiányzik egy darab ún ad-hoc polimorfizmus: a viselkedés megvalósításának biztonságos elküldésének képessége egy típus szerint, bárhol is van a viselkedés és típus meghatározva. Írd be a Type Class mintát.
A típusosztály mintája
A Type Class (röviden TC) minta receptje 3 lépésből áll.
- Határozzon meg egy új viselkedést
- Végezze el a viselkedést
- Használd a viselkedést
A következő részben a legegyszerűbb módon valósítom meg a TC mintát. Bőbeszédű, nehézkes és nem praktikus. De várjon, ezeket a figyelmeztetéseket lépésről lépésre rögzítjük a dokumentumban.
1. Határozzon meg egy új viselkedést
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
`Lib1
` ismét érintetlenül maradt.
Az új viselkedés is a TC a tulajdonság által materializálódott. A tulajdonságban definiált függvények a viselkedés egyes aspektusainak alkalmazását jelentik.
A `A
A ` azt a típust jelöli, amelyre a viselkedést alkalmazni szeretnénk, és amelyek a ` altípusaiBlockchain
` esetünkben.
Néhány megjegyzés:
- Ha szükséges, a paraméterezett `
A
` tovább korlátozható a Scala típusú rendszerrel. Például érvényesíthetjük a `A
` lenniBlockchain
`. - Ezenkívül a TC sokkal több funkciót tartalmazhat.
- Végül minden függvénynek sok tetszőleges paramétere lehet.
De az olvashatóság kedvéért tegyük egyszerűvé a dolgokat.
2. Valósítsa meg a viselkedést
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
val ethereumLastBlock = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
val bitcoinLastBlock = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
Minden típushoz az új `LastBlock
` viselkedés várható, ennek a viselkedésnek van egy konkrét példája.
A `Ethereum
` a 22-es megvalósítási sor a `-ből kerül kiszámításraeth
` példány paraméterként átadva.
A `LastBlock
` for `Bitcoin
A 25. sor nem felügyelt IO-val van megvalósítva, és nem használja a paraméterét.
Szóval, `Lib2
`új viselkedést valósít meg`LastBlock
` for `Lib1
` osztályok.
3. Használja a viselkedést
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
val ethereumLastBlock = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
val bitcoinLastBlock = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
import Lib1.*, Lib2.*
def useLastBlock[A](instance: A, behavior: LastBlock[A]) =
behavior.lastBlock(instance)
println(useLastBlock(Ethereum(lastBlock = 2), ethereumLastBlock))
println(useLastBlock(Bitcoin(), bitcoinLastBlock))
30. soruseLastBlock
` a ` példányát használjaA
` és a `LastBlock
` az adott példányhoz meghatározott viselkedés.
33. soruseLastBlock
A ` a ` példányával kerül meghívásraEthereum
` és a ` megvalósításaLastBlock
`-ben van meghatározvaLib2
`. Vegye figyelembe, hogy a ` bármely alternatív megvalósítása átadhatóLastBlock[A]
` (gondolj rá függőségi injekció).
`useLastBlock
` a ragasztó a reprezentáció (a tényleges A) és viselkedése között. Az adatok és a viselkedés elkülönül, amit a funkcionális programozás támogatja.
Megbeszélés
Foglaljuk össze a kifejezési probléma szabályait:
- 1. szabály: Engedélyezze a végrehajtását meglévő viselkedések kell alkalmazni új osztályok
- 2. szabály: Engedélyezze a végrehajtását új viselkedések kell alkalmazni meglévő osztályok
- 3. szabály: Nem veszélyeztetheti a típusú biztonság
- 4. szabály: Nem szükséges újrafordítani meglévő kódot
Az 1. szabály az altípus polimorfizmussal azonnal megoldható.
Az imént bemutatott TC minta (lásd az előző képernyőképet) megoldja a 2. szabályt. Típusbiztos (3. szabály), és soha nem érintettük meg a `Lib1
` (4. szabály).
Használata azonban több okból nem praktikus:
- A 33-34. sorokban kifejezetten át kell adnunk a viselkedést a példánya mentén. Ez plusz rezsi. Csak azt kellene írnunk, hogy `
useLastBlock(Bitcoin())
`. - 31. sorban a szintaxis nem gyakori. Szívesebben írnánk egy tömör és objektumorientáltabb `-t
instance.lastBlock()
` kijelentés.
Kiemeljünk néhány Scala funkciót a TC gyakorlati használatához.
Továbbfejlesztett fejlesztői élmény
A Scala egyedülálló funkciókkal és szintaktikai cukrokkal rendelkezik, amelyek a TC-t igazán élvezetes élménnyé teszik a fejlesztők számára.
Implicit
Az implicit hatókör egy olyan speciális hatókör, amelyet fordítási időben oldanak meg, és egy adott típusból csak egy példány létezhet.
Egy program egy példányt az implicit hatókörbe helyez a `-valgiven
` kulcsszó. Alternatív megoldásként a program lekérhet egy példányt az implicit hatókörből a ` kulcsszóvalusing
`.
Az implicit hatókör a fordítási időben feloldásra kerül, és futás közbeni dinamikus megváltoztatására is van mód. Ha a program fordít, az implicit hatókör feloldásra kerül. Futás közben nem lehetséges, hogy hiányzó implicit példányok legyenek használatban. Az egyetlen lehetséges félreértés a rossz implicit példány használatából fakadhat, de ez a probléma a szék és a billentyűzet közötti lénynek marad.
Ez különbözik a globális hatókörtől, mert:
- Kontextus szerint van megoldva. Egy program két helye használhat egy adott típusú példányt implicit hatókörben, de ez a két példány különbözhet.
- A színfalak mögött a kód implicit argumentumokat ad át annak működéséhez, amíg el nem éri az implicit használatot. Nem használ globális memóriateret.
Visszatérve a típusórára! Vegyük pontosan ugyanezt a példát.
def todo = 42
type Height = Int
type Block = Int
def http(uri: String): Block = todo
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
`Lib1
` ugyanaz a módosítatlan kód, amelyet korábban definiáltunk.
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given ethereumLastBlock:LastBlock[Ethereum] = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
given bitcoinLastBlock:LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
import Lib1.*, Lib2.*
def useLastBlock[A](instance: A)(using behavior: LastBlock[A]) =
behavior.lastBlock(instance)
println(useLastBlock(Ethereum(lastBlock = 2)))
println(useLastBlock(Bitcoin()))
19. sor egy új viselkedésLastBlock
` pontosan úgy van definiálva, mint korábban.
22. sor és 25. sor, `val
` helyébe ` lépgiven
`. A `LastBlock
` az implicit hatókörbe kerülnek.
31. soruseLastBlock
` kijelenti a viselkedést `LastBlock
` implicit paraméterként. A fordító feloldja a ` megfelelő példányátLastBlock
` implicit hatókörből, kontextusba helyezve a hívó helyéről (33. és 34. sor). A 28. sor mindent importál innen: `Lib2
', beleértve az implicit hatályt is. Tehát a fordító a példányok 22. és 25. sorát adja át a `useLastBlock
`.
Könyvtárhasználóként a típusosztály használata egyszerűbb, mint korábban. A 34. és 35. sor a fejlesztőnek csak meg kell győződnie arról, hogy a viselkedés egy példánya az implicit hatókörbe van beillesztve (és ez lehet egy egyszerű "import
`). Ha egy implicit nem `given
` ahol a kód van `using
` azt mondja neki a fordító.
A Scala implicit módon megkönnyíti az osztálypéldányok átadását a viselkedésük példányaival együtt.
Implicit cukrok
Az előző kód 22. és 25. sora tovább javítható! Ismételjük meg a TC-megvalósításokat.
given LastBlock[Ethereum] = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
A 22. és 25. sor, ha a példány neve nem használt, akkor elhagyható.
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
A 22. és 25. sorban a típus ismétlődése `-re cserélhetőwith
` kulcsszó.
given LastBlock[Ethereum] = _.lastBlock
given LastBlock[Bitcoin] = _ => http("http://bitcoin/last")
Mivel egy degenerált tulajdonságot használunk egyetlen funkcióval, az IDE javasolhatja a kód egyszerűsítését egy SAM kifejezéssel. Bár helyes, nem hiszem, hogy ez a SAM megfelelő használata, hacsak nem véletlenül kódol golfozni.
A Scala szintaktikai cukrokat kínál a szintaxis egyszerűsítésére, eltávolítva a szükségtelen elnevezéseket, deklarációkat és típusredundanciákat.
Kiterjesztés
Okosan használva a `extension
` mechanizmus leegyszerűsítheti a szintaxist egy típusosztály használatához.
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
extension[A](instance: A)
def lastBlock(using tc: LastBlock[A]) = tc.lastBlock(instance)
import Lib1.*, Lib2.*
println(Ethereum(lastBlock = 2).lastBlock)
println(Bitcoin().lastBlock)
A 28-29. sorok egy általános kiterjesztési módszer `lastBlock
` bármely ` esetén definiálva vanA
`egy `-velLastBlock
` TC paraméter implicit hatókörben.
A 33-34. sorok a kiterjesztés egy objektumorientált szintaxist használ a TC használatához.
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
extension[A](instance: A)(using tc: LastBlock[A])
def lastBlock = tc.lastBlock(instance)
def penultimateBlock = tc.lastBlock(instance) - 1
import Lib1.*, Lib2.*
val eth = Ethereum(lastBlock = 2)
println(eth.lastBlock)
println(eth.penultimateBlock)
val btc = Bitcoin()
println(btc.lastBlock)
println(btc.penultimateBlock)
A 28. sorban a TC paraméter a teljes kiterjesztésre is definiálható az ismétlődés elkerülése érdekében. A 30. sorban újra felhasználjuk a TC-t a kiterjesztésben a ` meghatározásáhozpenultimateBlock
` (annak ellenére, hogy megvalósítható a `LastBlock
`tulajdonság közvetlenül)
A varázslat akkor történik, amikor a TC-t használják. A kifejezés sokkal természetesebbnek tűnik, azt az illúziót keltve, hogy a viselkedés "lastBlock
` összekeverik a példánysal.
Általános típus TC-vel
import Lib1.*, Lib2.*
def useLastBlock1[A](instance: A)(using LastBlock[A]) = instance.lastBlock
def useLastBlock2[A: LastBlock](instance: A) = instance.lastBlock
val eth = Ethereum(lastBlock = 2)
assert(useLastBlock1(eth) == useLastBlock2(eth))
A 34. sor a függvény implicit TC-t használ. Vegye figyelembe, hogy a TC-t nem kell elnevezni, ha ez a név szükségtelen.
A TC-mintát olyan széles körben használják, hogy létezik egy általános típusszintaxis az „implicit viselkedésű típus” kifejezésére. A 36. sor szintaxisa tömörebb alternatívája az előzőnek (34. sor). Ez elkerüli a névtelen implicit TC paraméter konkrét deklarálását.
Ezzel a fejlesztői tapasztalatokkal foglalkozó rész véget ért. Láttuk, hogy a kiterjesztések, az implicit elemek és néhány szintaktikai cukor kevésbé zsúfolt szintaxist biztosíthatnak a TC használatakor és definiálásakor.
Automatikus levezetés
Sok Scala könyvtár használja a TC-t, így a programozóra bízza, hogy implementálja azokat a kódbázisába.
Például a Circe (egy json de-szerializációs könyvtár) a TC `-t használjaEncoder[T]
` és `Decoder[T]
` a programozóknak, hogy implementálják a kódbázisukban. A megvalósítás után a könyvtár teljes hatóköre használható.
A TC ezen megvalósításai több mint gyakran előfordulnak adatorientált térképezők. Nincs szükségük semmilyen üzleti logikára, unalmasak az írásuk, és teher az esetosztályokkal való szinkronban tartásuk.
Ilyen helyzetben azok a könyvtárak kínálják az ún automatikus levezetés ill félautomata származtatás. Lásd például Circe automatikus és a félautomata származtatás. A félautomata származtatással a programozó egy típusosztály példányát kisebb szintaxissal deklarálhatja, míg az automatikus származtatáshoz nem szükséges semmilyen kódmódosítás, kivéve az importálást.
A motorháztető alatt, fordítási időben, általános makrók önvizsgálatot végeznek típusok tiszta adatstruktúraként, és létrehoz egy TC[T]-t a könyvtárhasználók számára.
A TC általános származtatása nagyon elterjedt, ezért a Scala egy teljes eszköztárat vezetett be erre a célra. Ezt a módszert nem mindig hirdetik a könyvtári dokumentációk, bár ez a Scala 3 módszere a származtatás használatára.
object GenericLib:
trait Named[A]:
def blockchainName(instance: A): String
object Named:
import scala.deriving.*
inline final def derived[A](using inline m: Mirror.Of[A]): Named[A] =
val nameOfType: String = inline m match
case p: Mirror.ProductOf[A] => compiletime.constValue[p.MirroredLabel]
case _ => compiletime.error("Not a product")
new Named[A]:
override def blockchainName(instance: A):String = nameOfType.toLowerCase
extension[A] (instance: A)(using tc: Named[A])
def blockchainName = tc.blockchainName(instance)
import Lib1.*, GenericLib.*
case class Polkadot() derives Named
given Named[Bitcoin] = Named.derived
given Named[Ethereum] = Named.derived
println(Ethereum(lastBlock = 2).blockchainName)
println(Bitcoin().blockchainName)
println(Polkadot().blockchainName)
18. sor egy új TC `Named
` kerül bemutatásra. Ez a TC szigorúan véve nem kapcsolódik a blokklánc üzlethez. Célja a blokklánc elnevezése az esetosztály neve alapján.
Először összpontosítson a definíciók 36-38. soraira. 2 szintaxis létezik a TC származtatására:
- A 36. sor a TC-példányt közvetlenül az esetosztályban definiálhatja a `-val
derives
` kulcsszó. A burkolat alatt a fordító egy adott `-t generálNamed
` példány a `-benPolkadot
` társobjektum. - 37. és 38. sor, a típusosztályok példányai a már létező osztályokon vannak megadva `
TC.derived
`
A 31. sor egy általános kiterjesztést definiál (lásd az előző szakaszokat) és a `blockchainName
` természetesen használatos.
A `derives
` kulcsszó egy metódust vár ` formávalinline def derived[T](using Mirror.Of[T]): TC[T] = ???
` ami a 24. sor definiált. Nem fogom részletesen elmagyarázni, mit csinál a kód. Nagy vonalakban:
- `
inline def
` makrót határoz meg - `
Mirror
A ` a típusok betekintését lehetővé tevő eszköztár része. Különféle tükrök léteznek, és a kód 26. sora a `-re összpontosítProduct
` tükrök (a tokosztály egy termék). 27. sor, ha a programozók olyasmit próbálnak származtatni, ami nem `Product
`, a kód nem fordítódik le. - a `
Mirror
` más típusokat is tartalmaz. Egyikük, `MirrorLabel
`, egy karakterlánc, amely tartalmazza a típus nevét. Ezt az értéket a ` implementációjának 29. sora használjaNamed
` TC.
A TC szerzők metaprogramozást használhatnak olyan függvények biztosítására, amelyek általánosan generálnak TC-példányokat adott típushoz. A programozók dedikált könyvtári API-t vagy a Scala származékos eszközöket használhatják kódjuk példányainak létrehozásához.
Akár általános, akár specifikus kódra van szüksége a TC megvalósításához, minden helyzetre van megoldás.
Összefoglaló az összes előnyről
- Megoldja a kifejezési problémát
- Az új típusok a hagyományos tulajdonságok öröklődésén keresztül valósíthatják meg a meglévő viselkedést
- Új viselkedések implementálhatók a meglévő típusokon
- Az aggodalom szétválasztása
- A kód nincs összevissza és könnyen törölhető. A TC elválasztja az adatokat és a viselkedést, ami a funkcionális programozás mottója.
- Biztonságos
- Típusbiztos, mert nem az önvizsgálatra támaszkodik. Ez elkerüli a típusokat érintő nagy mintaillesztést. Ha találkozik ilyen kód írásával, olyan esetet észlelhet, amikor a TC minta tökéletesen megfelel.
- Az implicit mechanizmus biztonságosan lefordítható! Ha egy példány hiányzik a fordításkor, a kód nem fordítódik le. Nincs meglepetés futás közben.
- Ad-hoc polimorfizmust hoz
- Az ad hoc polimorfizmus általában hiányzik a hagyományos objektum orientált programozásból.
- Az ad-hoc polimorfizmus révén a fejlesztők ugyanazt a viselkedést valósíthatják meg különböző nem kapcsolódó típusoknál, anélkül, hogy hagyományos altípust használnának (ami párosítja a kódot).
- A függőségi injekció egyszerűvé vált
- A TC-példány a Liskov-helyettesítés elvének megfelelően módosítható.
- Ha egy komponens függ egy TC-től, egy megcsúfolt TC könnyen befecskendezhető tesztelési célból.
Ellenjelzések
Minden kalapács számos problémára készült.
A típusosztályok viselkedési problémákra szolgálnak, és nem használhatók adatöröklésre. Erre a célra használjon kompozíciót.
A szokásos altípusozás egyszerűbb. Ha Ön a kódbázis tulajdonosa, és nem törekszik a bővíthetőségre, a típusosztályok túlzásba eshetnek.
Például a Scala magban van egy `Numeric
` típusú osztály:
trait Numeric[T] extends Ordering[T] {
def plus(x: T, y: T): T
def minus(x: T, y: T): T
def times(x: T, y: T): T
Valóban ésszerű ilyen típusú osztályt használni, mert nem csak az algebrai algoritmusok újrafelhasználását teszi lehetővé a Scalába beágyazott típusokon (Int, BigInt, …), hanem a felhasználó által definiált típusokon is (a `ComplexNumber
` például).
Másrészt a Scala gyűjtemények megvalósítása többnyire altípust használ a típusosztály helyett. Ennek a kialakításnak több okból is van értelme:
- A gyűjtemény API-nak teljesnek és stabilnak kell lennie. Felfedi a gyakori viselkedést a megvalósítások által örökölt tulajdonságokon keresztül. A nagymértékben bővíthetőség itt nem különösebben cél.
- Egyszerűen használhatónak kell lennie. A TC mentális többletköltséget jelent a végfelhasználói programozó számára.
- A TC kisebb teljesítményköltséget is jelenthet. Ez kritikus lehet egy gyűjtemény API-nál.
- A gyűjtemény API azonban továbbra is bővíthető a harmadik fél könyvtárai által meghatározott új TC segítségével.
Következtetés
Láttuk, hogy a TC egy egyszerű minta, amely megold egy nagy problémát. A Scala gazdag szintaxisának köszönhetően a TC minta sokféleképpen megvalósítható és használható. A TC minta összhangban van a funkcionális programozási paradigmával, és mesés eszköz a tiszta architektúrához. Nincs ezüst golyó, és TC mintát kell alkalmazni, amikor illeszkedik.
Reméljük, hogy ennek a dokumentumnak a elolvasása után ismereteket szerzett.
A kód elérhető a címen https://github.com/jprudent/type-class-article. Ha bármilyen kérdése vagy észrevétele van, forduljon hozzám. Ha akarja, használhat problémákat vagy kód megjegyzéseket a tárolóban.
Software Engineer
- SEO által támogatott tartalom és PR terjesztés. Erősödjön még ma.
- PlatoData.Network Vertical Generative Ai. Erősítse meg magát. Hozzáférés itt.
- PlatoAiStream. Web3 Intelligence. Felerősített tudás. Hozzáférés itt.
- PlatoESG. Carbon, CleanTech, Energia, Környezet, Nap, Hulladékgazdálkodás. Hozzáférés itt.
- PlatoHealth. Biotechnológiai és klinikai vizsgálatok intelligencia. Hozzáférés itt.
- Forrás: https://www.ledger.com/blog/type-classes-in-scala3-a-beginners-guide
- :van
- :is
- :nem
- :ahol
- ][p
- 1
- 10
- 11
- 12
- 14
- 15%
- 19
- 1998
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 35%
- 36
- 7
- 8
- 9
- a
- képesség
- Rólunk
- AC
- Szerint
- Akció
- tényleges
- Hozzáteszi
- támogatja
- újra
- cél
- algoritmusok
- Minden termék
- lehetővé
- lehetővé teszi, hogy
- mentén
- már
- Is
- alternatív
- Bár
- mindig
- an
- és a
- Másik
- bármilyen
- api
- alkalmazott
- alkalmaz
- megfelelő
- építészet
- Archív
- VANNAK
- érvek
- cikkben
- AS
- szempontok
- At
- kísérlet
- szerzők
- Automatikus
- elérhető
- elkerülése érdekében
- vissza
- bázis
- alapján
- BE
- mert
- előtt
- Kezdő
- viselkedés
- hogy
- között
- Nagy
- blockchain
- Unalmas
- mindkét
- Doboz
- Bring
- széles
- BTC
- teher
- üzleti
- de
- by
- hívás
- hívott
- Hívó
- TUD
- eset
- Szék
- változik
- megváltozott
- osztály
- osztályok
- ragadozó ölyv
- kód
- kódbázis
- codebase
- gyűjtemény
- gyűjtemény
- hogyan
- jön
- Hozzászólások
- Közös
- társ
- teljes
- megfelelnek
- összetevő
- összetétel
- Vonatkozik
- tömör
- arra a következtetésre jut
- összetévesztve
- zavar
- tartalmaz
- Mag
- kijavítására
- tudott
- teremt
- létrehozása
- teremtmény
- kritikai
- dátum
- kijelenti,
- elszánt
- meghatározott
- meghatározott
- Annak meghatározása,
- definíció
- definíciók
- Függőség
- mélység
- származik
- Design
- tervezett
- kimutatására
- Fejlesztő
- fejlesztők
- DID
- különböző
- közvetlenül
- Dispatch
- merülés
- do
- dokumentum
- nem
- Nem
- ne
- dinamikusan
- minden
- könnyű
- könnyebb
- könnyen
- könnyű
- ed
- beágyazott
- találkozás
- végén
- érvényesíteni
- végrehajtás
- élvezetes
- belép
- hibák
- ETH
- Eter (ETH)
- Még
- minden
- pontosan
- példa
- Kivéve
- létezik
- létező
- várható
- elvárja
- tapasztalat
- Magyarázza
- Elmagyarázza
- kifejezetten
- expressz
- kifejezés
- kiterjesztés
- kiterjesztések
- külön-
- nem sikerül
- Jellemzők
- úgy érzi,
- rögzített
- Összpontosít
- koncentrál
- következő
- A
- forma
- ból ből
- funkció
- funkcionális
- funkciók
- további
- Nyereség
- szerzett
- generál
- generál
- GitHub
- adott
- Giving
- Globális
- globális hatókör
- cél
- útmutató
- kalapács
- kéz
- megtörténik
- Legyen
- itt
- Kiemel
- nagyon
- őt
- tart
- motorháztető
- Hogyan
- HTML
- http
- HTTPS
- i
- if
- Illúzió
- kép
- végre
- végrehajtás
- megvalósítások
- végre
- munkagépek
- importál
- behozatal
- javított
- in
- Beleértve
- öröklés
- példa
- példányok
- helyette
- szándékolt
- bele
- Bevezetett
- bevonásával
- kérdés
- kérdések
- IT
- ITS
- veszélyezteti
- json
- éppen
- Tart
- Ismer
- tudás
- nyelv
- keresztnév
- vezetékek
- kilépő
- Főkönyv
- balra
- kevesebb
- kihasználja
- könyvtárak
- könyvtár
- mint
- vonal
- vonalak
- helyszínek
- logika
- Sok
- Makrók
- készült
- Made Easy
- mágia
- fenntartása
- csinál
- KÉSZÍT
- mód
- sok
- egyező
- Lehet..
- me
- mechanizmus
- Memory design
- szellemi
- mers
- meta
- módszer
- esetleg
- bánja
- kisebb
- tükör
- hiányzó
- több
- a legtöbb
- többnyire
- Jelmondat
- kell
- név
- Nevezett
- elnevezési
- Természetes
- Szükség
- szükséges
- soha
- Új
- nem
- megjegyezni
- tárgy
- of
- ajánlat
- Ajánlatok
- gyakran
- Régi
- on
- egyszer
- ONE
- csak
- or
- Más
- mi
- ki
- körvonalak
- felett
- saját
- paradigma
- paraméter
- paraméterek
- rész
- különös
- párt
- elhalad
- Elmúlt
- bérletek
- Múló
- Mintás
- tökéletesen
- teljesítmény
- darab
- Plató
- Platón adatintelligencia
- PlatoData
- kérem
- lehetséges
- Gyakorlati
- jobban szeret
- bemutatott
- előző
- korábban
- alapelv
- Probléma
- problémák
- Termékek
- Program
- Programozó
- programozók
- Programozás
- megfelelő
- ad
- cél
- célokra
- tesz
- helyezi
- Kérdések
- hatótávolság
- Inkább
- el
- elérte
- Olvasás
- tényleg
- ok
- újrafutóz
- recept
- támaszkodnak
- marad
- eszébe jut
- eltávolítása
- helyébe
- raktár
- képviselet
- jelentése
- megoldódott
- tisztelet
- újra
- Gazdag
- Szabály
- szabályok
- s
- biztonságos
- biztosan
- Biztonság
- kedvéért
- Sam
- azonos
- Scala
- színhely
- hatálya
- Rész
- szakaszok
- lát
- látott
- értelemben
- készlet
- számos
- rövid
- kellene
- Ezüst
- Egyszerű
- egyszerűsítése
- egyszerűsítése
- egyetlen
- helyzet
- kicsi
- So
- szoftver
- szilárd
- megoldások
- megoldott
- Megoldja
- néhány
- valami
- forrás
- forráskód
- Hely
- beszélő
- speciális
- különleges
- kifejezetten
- stabil
- kezdet
- nyilatkozat
- Lépés
- Lépései
- Még mindig
- egyértelmű
- áramvonal
- Húr
- struktúra
- ilyen
- cukor
- javasol
- Öltöny
- feltételezett
- biztos
- meglepetés
- kapcsoló
- szinkronizál.
- szintaxis
- rendszer
- T
- Vesz
- Feladat
- megmondja
- Tesztelés
- mint
- köszönöm
- hogy
- A
- The Source
- azok
- Őket
- Ott.
- ők
- dolog
- dolgok
- Szerintem
- Harmadik
- ezt
- azok
- bár?
- Keresztül
- idő
- nak nek
- szerszám
- Eszköztár
- szerszámok
- érintett
- hagyományos
- kipróbált
- valóban
- megpróbál
- kettő
- típus
- típusok
- Ritka
- alatt
- egyedi
- NÉVTELEN
- -ig
- érintetlen
- felhasználatlan
- upon
- Használat
- használ
- használt
- használó
- Felhasználók
- használ
- segítségével
- szokásos
- rendszerint
- érték
- különféle
- járatos
- nagyon
- akar
- volt
- Út..
- módon
- we
- Mit
- Mi
- amikor
- mivel
- ami
- WHO
- egész
- miért
- széles körben
- lesz
- bölcsen
- val vel
- nélkül
- lenne
- ír
- írás
- Rossz
- még
- te
- A te
- magad
- zephyrnet