Tài liệu này dành cho nhà phát triển Scala3 mới bắt đầu, người đã thành thạo văn xuôi Scala, nhưng đang bối rối về tất cả `implicits
` và các đặc điểm được tham số hóa trong mã.
Tài liệu này giải thích lý do tại sao, như thế nào, ở đâu và khi nào Loại lớp (TC).
Sau khi đọc tài liệu này, nhà phát triển Scala3 mới bắt đầu sẽ có được kiến thức vững chắc để sử dụng và đi sâu vào mã nguồn của rất nhiều của các thư viện Scala và bắt đầu viết mã Scala thành ngữ.
Hãy bắt đầu với lý do tại sao…
Vấn đề biểu hiện
Trong 1998, Philip Wadler đã nêu rằng “bài toán biểu thức là một tên mới cho một bài toán cũ”. Đó là vấn đề về khả năng mở rộng phần mềm. Theo ông Wadler viết, lời giải bài toán biểu thức phải tuân theo các quy tắc sau:
- Quy tắc 1: Cho phép thực hiện hành vi hiện có (nghĩ về đặc điểm Scala) được áp dụng cho đại diện mới (nghĩ về một trường hợp lớp)
- Quy tắc 2: Cho phép triển khai hành vi mới được áp dụng cho đại diện hiện có
- Quy tắc 3: Nó không được gây nguy hiểm cho loại an toàn
- Quy tắc 4: Không cần phải biên dịch lại mã hiện có
Giải quyết vấn đề này sẽ là chủ đề bạc của bài viết này.
Quy tắc 1: thực hiện hành vi hiện có trên biểu diễn mới
Bất kỳ ngôn ngữ hướng đối tượng nào cũng có giải pháp tích hợp cho quy tắc 1 với đa hình kiểu phụ. Bạn có thể thực hiện một cách an toàn bất kỳ `trait
` được xác định trong phần phụ thuộc vào `class
` bằng mã của riêng bạn mà không cần biên dịch lại phần phụ thuộc. Hãy xem điều đó trong thực tế:
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()
Trong ví dụ hư cấu này, thư viện `Lib1
` (dòng 5) xác định một đặc điểm `Blockchain
` (dòng 6) với 2 cách triển khai của nó (dòng 9 & 12). `Lib1
` sẽ giữ nguyên trong TẤT CẢ tài liệu này (thực thi quy tắc 4).
`Lib2
`(dòng 15) thực hiện hành vi hiện có`Blockchain
`trên một lớp mới`Polkadot
` (quy tắc 1) theo kiểu an toàn (quy tắc 3), không cần biên dịch lại `Lib1
`(quy tắc 4).
Quy tắc 2: thực hiện các hành vi mới sẽ được áp dụng cho các biểu diễn hiện có
Hãy tưởng tượng trong `Lib2
`chúng tôi muốn có một hành vi mới`lastBlock
` được triển khai cụ thể cho từng `Blockchain
`.
Điều đầu tiên bạn nghĩ đến là tạo một công tắc lớn dựa trên loại tham số.
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()))
Giải pháp này là sự triển khai lại yếu kém của tính đa hình dựa trên kiểu đã có sẵn trong ngôn ngữ!
`Lib1
` không bị ảnh hưởng (hãy nhớ, quy tắc 4 được thực thi trong toàn bộ tài liệu này).
Giải pháp được triển khai trong `Lib2
` là ổn cho đến khi một blockchain khác được giới thiệu trong `Lib3
`. Nó vi phạm quy tắc an toàn loại (quy tắc 3) vì mã này bị lỗi khi chạy trên dòng 37. Và sửa đổi `Lib2
` sẽ vi phạm quy tắc 4.
Một giải pháp khác là sử dụng `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
` không bị ảnh hưởng (thực thi quy tắc 4 trong toàn bộ tài liệu).
`Lib2
` xác định hành vi cho loại của nó (dòng 21) và `phần mở rộng` cho loại hiện có (dòng 23 & 25).
Dòng 28-30, hành vi mới có thể được sử dụng trong mỗi lớp.
Nhưng không có cách nào gọi hành vi mới này là đa hình (dòng 32). Bất kỳ nỗ lực nào để làm như vậy đều dẫn đến lỗi biên dịch (dòng 33) hoặc chuyển đổi dựa trên kiểu gõ.
Quy tắc số 2 này rất phức tạp. Chúng tôi đã cố gắng triển khai nó bằng định nghĩa riêng của chúng tôi về tính đa hình và thủ thuật `mở rộng`. Và điều đó thật kỳ lạ.
Có một phần còn thiếu tên là đa hình đặc biệt: khả năng gửi một cách an toàn việc triển khai hành vi theo một loại, bất cứ nơi nào hành vi và loại được xác định. Nhập Loại lớp mô hình.
Mẫu lớp loại
Công thức mẫu Loại Loại (viết tắt là TC) có 3 bước.
- Xác định hành vi mới
- Thực hiện hành vi
- Sử dụng hành vi
Trong phần sau, tôi triển khai mẫu TC theo cách đơn giản nhất. Nó dài dòng, vụng về và không thực tế. Nhưng chờ đã, những lưu ý đó sẽ được khắc phục từng bước một trong tài liệu.
1. Xác định hành vi mới
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
`Lib1
`, một lần nữa, vẫn được giữ nguyên.
Hành vi mới is TC được cụ thể hóa bởi đặc điểm. Các chức năng được xác định trong đặc điểm là một cách để áp dụng một số khía cạnh của hành vi đó.
Tham số `A
` đại diện cho kiểu mà chúng ta muốn áp dụng hành vi, là kiểu con của `Blockchain
` trong trường hợp của chúng tôi.
Một số nhận xét:
- Nếu cần, loại tham số `
A
` có thể bị hạn chế hơn nữa bởi hệ thống kiểu Scala. Chẳng hạn, chúng ta có thể thực thi `A
` trở thành một `Blockchain
`. - Ngoài ra, TC có thể có nhiều chức năng hơn được khai báo trong đó.
- Cuối cùng, mỗi hàm có thể có nhiều tham số tùy ý hơn.
Nhưng hãy giữ mọi thứ đơn giản để dễ đọc.
2. Thực hiện hành vi
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")
Đối với mỗi loại, ` mớiLastBlock
` hành vi được mong đợi, có một trường hợp cụ thể về hành vi đó.
Các `Ethereum
`dòng thực hiện 22 được tính từ `eth
` dụ được truyền dưới dạng tham số.
Việc thực hiện `LastBlock
` cho `Bitcoin
`dòng 25 được triển khai với IO không được quản lý và không sử dụng tham số của nó.
Vì vậy, `Lib2
` thực hiện hành vi mới `LastBlock
` cho `Lib1
` các lớp học.
3. Sử dụng hành vi
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))
Dòng 30`useLastBlock
` sử dụng một thể hiện của `A
` và `LastBlock
` hành vi được xác định cho trường hợp đó.
Dòng 33`useLastBlock
` được gọi với một thể hiện của `Ethereum
` và việc triển khai `LastBlock
` được xác định trong `Lib2
`. Lưu ý rằng có thể chuyển bất kỳ triển khai thay thế nào của `LastBlock[A]
`(nghĩ tới tiêm phụ thuộc).
`useLastBlock
` là chất kết dính giữa biểu diễn (A thực tế) và hành vi của nó. Dữ liệu và hành vi được tách biệt, đó là điều mà lập trình chức năng ủng hộ.
Thảo luận
Hãy tóm tắt lại các quy tắc của bài toán biểu thức:
- Quy tắc 1: Cho phép thực hiện hành vi hiện có được áp dụng cho lớp học mới
- Quy tắc 2: Cho phép triển khai hành vi mới được áp dụng cho các lớp hiện có
- Quy tắc 3: Nó không được gây nguy hiểm cho loại an toàn
- Quy tắc 4: Không cần phải biên dịch lại mã hiện có
Quy tắc 1 có thể được giải quyết ngay lập tức bằng tính đa hình kiểu con.
Mẫu TC vừa trình bày (xem ảnh chụp màn hình trước đó) giải quyết quy tắc 2. Đó là loại an toàn (quy tắc 3) và chúng tôi chưa bao giờ chạm vào `Lib1
`(quy tắc 4).
Tuy nhiên, nó không thực tế để sử dụng vì một số lý do:
- Dòng 33-34 chúng ta phải chuyển hành vi một cách rõ ràng dọc theo thể hiện của nó. Đây là một chi phí bổ sung. Chúng ta chỉ nên viết `
useLastBlock(Bitcoin())
`. - Cú pháp dòng 31 không phổ biến. Chúng tôi muốn viết ngắn gọn và hướng đối tượng hơn `
instance.lastBlock()
`tuyên bố.
Hãy nêu bật một số tính năng của Scala để sử dụng TC thực tế.
Trải nghiệm của nhà phát triển nâng cao
Scala có một bộ tính năng và cú pháp độc đáo giúp TC trở thành một trải nghiệm thực sự thú vị cho các nhà phát triển.
Hệ lụy
Phạm vi tiềm ẩn là một phạm vi đặc biệt được giải quyết tại thời điểm biên dịch trong đó chỉ có thể tồn tại một phiên bản của một loại nhất định.
Một chương trình đặt một thể hiện trong phạm vi tiềm ẩn với `given
` từ khóa. Ngoài ra, một chương trình có thể truy xuất một thể hiện từ phạm vi tiềm ẩn bằng từ khóa `using
`.
Phạm vi tiềm ẩn được giải quyết tại thời điểm biên dịch, có cách để thay đổi nó một cách linh hoạt khi chạy. Nếu chương trình biên dịch, phạm vi tiềm ẩn sẽ được giải quyết. Trong thời gian chạy, không thể thiếu các phiên bản tiềm ẩn nơi chúng được sử dụng. Sự nhầm lẫn duy nhất có thể xảy ra có thể đến từ việc sử dụng sai phiên bản tiềm ẩn, nhưng vấn đề này được để lại cho sinh vật nằm giữa ghế và bàn phím.
Nó khác với phạm vi toàn cầu vì:
- Nó được giải quyết theo ngữ cảnh. Hai vị trí của một chương trình có thể sử dụng một thể hiện của cùng một loại đã cho trong phạm vi ngầm định, nhưng hai thể hiện đó có thể khác nhau.
- Phía sau, mã đang chuyển hàm đối số ngầm sang hàm cho đến khi đạt được mức sử dụng ngầm. Nó không sử dụng không gian bộ nhớ chung.
Quay trở lại lớp loại! Hãy lấy ví dụ tương 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
` là mã chưa sửa đổi mà chúng tôi đã xác định trước đó.
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()))
Dòng 19 một hành vi mới `LastBlock
` được xác định, chính xác như chúng ta đã làm trước đây.
Dòng 22 và dòng 25, `val
` được thay thế bằng `given
`. Cả hai cách triển khai `LastBlock
` được đặt trong phạm vi ngầm định.
Dòng 31`useLastBlock
` khai báo hành vi `LastBlock
` như một tham số ngầm định. Trình biên dịch giải quyết phiên bản thích hợp của `LastBlock
` từ phạm vi tiềm ẩn được ngữ cảnh hóa từ vị trí người gọi (dòng 33 và 34). Dòng 28 nhập mọi thứ từ `Lib2
`, bao gồm cả phạm vi ngầm định. Vì vậy, trình biên dịch chuyển các thể hiện được xác định dòng 22 và 25 làm tham số cuối cùng của `useLastBlock
`.
Là người dùng thư viện, việc sử dụng lớp loại dễ dàng hơn trước. Dòng 34 và 35, nhà phát triển chỉ phải đảm bảo rằng một phiên bản của hành vi được đưa vào phạm vi ngầm định (và đây có thể chỉ là `import
`). Nếu một ẩn ý không phải là `given
`mã ở đâu`using
` nó, trình biên dịch nói với anh ta.
Khả năng tiềm ẩn của Scala giúp dễ dàng thực hiện nhiệm vụ chuyển các thể hiện của lớp cùng với các thể hiện hành vi của chúng.
Đường tiềm ẩn
Dòng 22 và 25 của mã trước đó có thể được cải thiện hơn nữa! Hãy lặp lại việc triển khai TC.
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")
Dòng 22 và 25, nếu tên của instance không được sử dụng thì có thể bỏ qua.
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
Dòng 22 và 25, sự lặp lại của loại có thể được thay thế bằng `with
` từ khóa.
given LastBlock[Ethereum] = _.lastBlock
given LastBlock[Bitcoin] = _ => http("http://bitcoin/last")
Vì chúng tôi sử dụng một đặc điểm thoái hóa với một hàm duy nhất trong đó nên IDE có thể đề xuất đơn giản hóa mã bằng biểu thức SAM. Mặc dù đúng nhưng tôi không nghĩ đó là cách sử dụng SAM hợp lý, trừ khi bạn tình cờ chơi gôn.
Scala cung cấp cú pháp để hợp lý hóa cú pháp, loại bỏ việc đặt tên, khai báo và loại dư thừa không cần thiết.
Extension
Được sử dụng một cách khôn ngoan, `extension
` cơ chế có thể đơn giản hóa cú pháp sử dụng một lớp loại.
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)
Dòng 28-29 một phương thức mở rộng chung `lastBlock
` được xác định cho bất kỳ `A
` với một `LastBlock
` Tham số TC trong phạm vi ngầm định.
Các dòng 33-34 phần mở rộng tận dụng cú pháp hướng đối tượng để sử dụng TC.
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)
Dòng 28, tham số TC cũng có thể được xác định cho toàn bộ phần mở rộng để tránh lặp lại. Dòng 30 chúng tôi sử dụng lại TC trong phần mở rộng để xác định `penultimateBlock
` (mặc dù nó có thể được triển khai trên `LastBlock
` đặc điểm trực tiếp)
Điều kỳ diệu sẽ xảy ra khi TC được sử dụng. Biểu cảm có cảm giác tự nhiên hơn rất nhiều, tạo ảo giác rằng hành vi `lastBlock
` được kết hợp với thể hiện.
Loại chung có TC
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))
Dòng 34 hàm sử dụng TC ẩn. Lưu ý rằng TC không cần đặt tên nếu tên đó không cần thiết.
Mẫu TC được sử dụng rộng rãi đến mức có một cú pháp kiểu chung để thể hiện “một kiểu có hành vi tiềm ẩn”. Cú pháp dòng 36 là một thay thế ngắn gọn hơn cho cú pháp trước đó (dòng 34). Nó tránh khai báo cụ thể tham số TC ẩn không được đặt tên.
Điều này kết thúc phần trải nghiệm của nhà phát triển. Chúng ta đã thấy các phần mở rộng, hàm ý và một số đường cú pháp có thể cung cấp cú pháp ít lộn xộn hơn khi TC được sử dụng và xác định.
Dẫn xuất tự động
Rất nhiều thư viện Scala sử dụng TC, để lập trình viên triển khai chúng trong cơ sở mã của họ.
Ví dụ: Circe (thư viện khử tuần tự hóa json) sử dụng TC `Encoder[T]
`và 'Decoder[T]
` để các lập trình viên triển khai trong cơ sở mã của họ. Sau khi triển khai, toàn bộ phạm vi của thư viện có thể được sử dụng.
Việc triển khai TC đó thường xuyên hơn người lập bản đồ hướng dữ liệu. Chúng không cần bất kỳ logic nghiệp vụ nào, viết rất nhàm chán và là gánh nặng để duy trì sự đồng bộ với các lớp trường hợp.
Trong tình huống như vậy, những thư viện đó cung cấp cái được gọi là tự động đạo hàm hoặc bán tự động nguồn gốc. Xem ví dụ Circe tự động và bán tự động nguồn gốc. Với dẫn xuất bán tự động, lập trình viên có thể khai báo một thể hiện của một lớp loại bằng một số cú pháp nhỏ, trong khi dẫn xuất tự động không yêu cầu bất kỳ sửa đổi mã nào ngoại trừ việc nhập.
Dưới mui xe, tại thời điểm biên dịch, các macro chung sẽ xem xét nội tâm loại dưới dạng cấu trúc dữ liệu thuần túy và tạo TC[T] cho người dùng thư viện.
Nói chung, việc tạo ra một TC là rất phổ biến, vì vậy Scala đã giới thiệu một hộp công cụ hoàn chỉnh cho mục đích đó. Phương pháp này không phải lúc nào cũng được quảng cáo bởi các tài liệu thư viện mặc dù đó là cách sử dụng đạo hàm Scala 3.
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)
Dòng 18 một TC mới `Named
` được giới thiệu. Nói đúng ra, TC này không liên quan đến hoạt động kinh doanh blockchain. Mục đích của nó là đặt tên cho blockchain dựa trên tên của lớp trường hợp.
Đầu tiên tập trung vào các dòng định nghĩa 36-38. Có 2 cú pháp để lấy TC:
- Dòng 36, phiên bản TC có thể được định nghĩa trực tiếp trên lớp trường hợp với `
derives
` từ khóa. Dưới mui xe, trình biên dịch tạo ra một ` nhất địnhNamed
` ví dụ trong `Polkadot
` đối tượng đồng hành. - Dòng 37 và 38, các thể hiện của lớp loại được cung cấp trên các lớp có sẵn với `
TC.derived
`
Dòng 31 một phần mở rộng chung được xác định (xem các phần trước) và `blockchainName
` được sử dụng một cách tự nhiên.
Các `derives
` từ khóa mong đợi một phương thức có dạng `inline def derived[T](using Mirror.Of[T]): TC[T] = ???
` được xác định ở dòng 24. Tôi sẽ không giải thích sâu về chức năng của mã. Trong các phác thảo rộng rãi:
- `
inline def
` định nghĩa một macro - `
Mirror
` là một phần của hộp công cụ để xem xét các kiểu nội tâm. Có nhiều loại gương khác nhau và dòng 26 mã tập trung vào `Product
` gương (một lớp trường hợp là một sản phẩm). Dòng 27, nếu người lập trình cố gắng rút ra thứ gì đó không phải là `Product
`, mã sẽ không được biên dịch. - cái '
Mirror
` chứa các loại khác. Một trong số họ, `MirrorLabel
`, là một chuỗi chứa tên loại. Giá trị này được sử dụng trong quá trình triển khai, dòng 29, của `Named
`TC.
Tác giả TC có thể sử dụng lập trình meta để cung cấp các hàm tạo ra các phiên bản TC cho một loại một cách tổng quát. Lập trình viên có thể sử dụng API thư viện chuyên dụng hoặc các công cụ phái sinh Scala để tạo phiên bản cho mã của họ.
Cho dù bạn cần mã chung hay mã cụ thể để triển khai TC thì luôn có giải pháp cho từng tình huống.
Tóm tắt tất cả các lợi ích
- Nó giải quyết vấn đề biểu hiện
- Các kiểu mới có thể thực hiện hành vi hiện có thông qua kế thừa đặc điểm truyền thống
- Hành vi mới có thể được thực hiện trên các loại hiện có
- Tách mối quan tâm
- Mã không bị sai và dễ dàng xóa. TC tách biệt dữ liệu và hành vi, đây là phương châm lập trình chức năng.
- Nó an toàn
- Nó an toàn vì nó không dựa vào sự xem xét nội tâm. Nó tránh việc khớp mẫu lớn liên quan đến các loại. nếu bạn gặp phải việc viết mã như vậy, bạn có thể phát hiện trường hợp mẫu TC sẽ phù hợp hoàn hảo.
- Cơ chế ngầm được biên dịch an toàn! Nếu thiếu một phiên bản tại thời điểm biên dịch thì mã sẽ không được biên dịch. Không có gì ngạc nhiên khi chạy.
- Nó mang lại tính đa hình đặc biệt
- Tính đa hình đặc biệt thường bị thiếu trong lập trình hướng đối tượng truyền thống.
- Với tính đa hình đặc biệt, nhà phát triển có thể triển khai hành vi tương tự cho nhiều loại không liên quan khác nhau mà không cần sử dụng kiểu gõ phụ truyền thống (kết hợp mã)
- Việc tiêm phụ thuộc được thực hiện dễ dàng
- Một phiên bản TC có thể được thay đổi theo nguyên tắc thay thế Liskov.
- Khi một thành phần phụ thuộc vào TC, một TC giả có thể dễ dàng được đưa vào cho mục đích thử nghiệm.
chỉ dẫn truy cập
Mỗi chiếc búa đều được thiết kế để giải quyết nhiều vấn đề khác nhau.
Loại Lớp dành cho các vấn đề về hành vi và không được sử dụng để kế thừa dữ liệu. Sử dụng thành phần cho mục đích đó.
Việc phân nhóm thông thường đơn giản hơn. Nếu bạn sở hữu cơ sở mã và không hướng tới khả năng mở rộng, các lớp loại có thể là quá mức cần thiết.
Chẳng hạn, trong lõi Scala, có một `Numeric
`loại lớp:
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
Việc sử dụng một lớp kiểu như vậy thực sự có ý nghĩa vì nó không chỉ cho phép sử dụng lại các thuật toán đại số trên các kiểu được nhúng trong Scala (Int, BigInt, …), mà còn trên các kiểu do người dùng xác định (a `ComplexNumber
` chẳng hạn).
Mặt khác, việc triển khai các bộ sưu tập Scala chủ yếu sử dụng kiểu con thay vì kiểu lớp. Thiết kế này có ý nghĩa vì nhiều lý do:
- API bộ sưu tập được cho là hoàn chỉnh và ổn định. Nó bộc lộ hành vi chung thông qua các đặc điểm được kế thừa từ quá trình triển khai. Khả năng mở rộng cao không phải là một mục tiêu cụ thể ở đây.
- Nó phải đơn giản để sử dụng. TC bổ sung thêm chi phí tinh thần cho lập trình viên người dùng cuối.
- TC cũng có thể phải chịu chi phí nhỏ về hiệu suất. Điều này có thể rất quan trọng đối với API bộ sưu tập.
- Tuy nhiên, API bộ sưu tập vẫn có thể mở rộng thông qua TC mới do thư viện bên thứ ba xác định.
Kết luận
Chúng ta đã thấy rằng TC là một mẫu đơn giản có thể giải quyết được một vấn đề lớn. Nhờ cú pháp phong phú Scala, mẫu TC có thể được triển khai và sử dụng theo nhiều cách. Mẫu TC phù hợp với mô hình lập trình hàm và là một công cụ tuyệt vời cho một kiến trúc gọn gàng. Không có viên đạn bạc và mẫu TC phải được áp dụng khi phù hợp.
Hy vọng bạn đã có được kiến thức khi đọc tài liệu này.
Mã có sẵn tại https://github.com/jprudent/type-class-article. Hãy liên hệ với tôi nếu bạn có bất kỳ câu hỏi hoặc nhận xét nào. Bạn có thể sử dụng các vấn đề hoặc nhận xét mã trong kho lưu trữ nếu muốn.
Kỹ sư phần mềm
- Phân phối nội dung và PR được hỗ trợ bởi SEO. Được khuếch đại ngay hôm nay.
- PlatoData.Network Vertical Generative Ai. Trao quyền cho chính mình. Truy cập Tại đây.
- PlatoAiStream. Thông minh Web3. Kiến thức khuếch đại. Truy cập Tại đây.
- Trung tâmESG. Than đá, công nghệ sạch, Năng lượng, Môi trường Hệ mặt trời, Quản lý chất thải. Truy cập Tại đây.
- PlatoSức khỏe. Tình báo thử nghiệm lâm sàng và công nghệ sinh học. Truy cập Tại đây.
- nguồn: https://www.ledger.com/blog/type-classes-in-scala3-a-beginners-guide
- : có
- :là
- :không phải
- :Ở đâu
- ][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
- có khả năng
- Giới thiệu
- AC
- Theo
- Hoạt động
- thực tế
- Thêm
- những người ủng hộ
- một lần nữa
- nhằm mục đích
- thuật toán
- Tất cả
- cho phép
- cho phép
- dọc theo
- Đã
- Ngoài ra
- thay thế
- Mặc dù
- luôn luôn
- an
- và
- Một
- bất kì
- api
- áp dụng
- Đăng Nhập
- thích hợp
- kiến trúc
- lưu trữ
- LÀ
- đối số
- bài viết
- AS
- các khía cạnh
- At
- nỗ lực
- tác giả
- Tự động
- có sẵn
- tránh
- trở lại
- cơ sở
- dựa
- BE
- bởi vì
- trước
- mới bắt đầu
- hành vi
- hành vi
- được
- giữa
- lớn
- blockchain
- Nhàm chán
- cả hai
- Hộp
- Mang lại
- rộng
- BTC
- gánh nặng
- kinh doanh
- nhưng
- by
- cuộc gọi
- gọi là
- Caller
- CAN
- trường hợp
- Ghế
- thay đổi
- thay đổi
- tốt nghiệp lớp XNUMX
- các lớp học
- giống cá lăng
- mã
- cơ sở mã
- Cơ sở mã hóa
- bộ sưu tập
- bộ sưu tập
- Đến
- đến
- Bình luận
- Chung
- đồng
- hoàn thành
- tuân theo
- thành phần
- thành phần
- Liên quan
- ngắn gọn
- kết luận
- bị giam giữ
- nhầm lẫn
- chứa
- Trung tâm
- sửa chữa
- có thể
- tạo
- Tạo
- sinh vật
- quan trọng
- dữ liệu
- Cấu trúc dữ liệu
- tuyên bố
- dành riêng
- định nghĩa
- xác định
- Xác định
- định nghĩa
- các định nghĩa
- Phụ thuộc
- chiều sâu
- lấy được
- Thiết kế
- thiết kế
- phát hiện
- Nhà phát triển
- phát triển
- ĐÃ LÀM
- khác nhau
- trực tiếp
- công văn
- bổ nhào
- do
- tài liệu
- làm
- Không
- dont
- năng động
- mỗi
- dễ dàng
- dễ dàng hơn
- dễ dàng
- dễ dàng
- ed
- nhúng
- cuối
- thi hành
- thực thi
- thú vị
- đăng ký hạng mục thi
- lỗi
- ETH
- Ngay cả
- tất cả mọi thứ
- chính xác
- ví dụ
- Trừ
- tồn tại
- hiện tại
- dự kiến
- kỳ vọng
- kinh nghiệm
- Giải thích
- Giải thích
- rõ ràng
- thể hiện
- biểu hiện
- mở rộng
- mở rộng
- thêm
- không
- Tính năng
- cảm thấy
- cố định
- Tập trung
- tập trung
- tiếp theo
- Trong
- hình thức
- từ
- chức năng
- chức năng
- chức năng
- xa hơn
- Thu được
- đạt được
- tạo ra
- tạo
- GitHub
- được
- Cho
- Toàn cầu
- phạm vi toàn cầu
- mục tiêu
- hướng dẫn
- búa
- tay
- xảy ra
- Có
- tại đây
- Đánh dấu
- cao
- anh ta
- tổ chức
- mui xe
- Độ đáng tin của
- HTML
- http
- HTTPS
- i
- if
- Illusion
- hình ảnh
- thực hiện
- thực hiện
- triển khai
- thực hiện
- thực hiện
- nhập khẩu
- nhập khẩu
- cải thiện
- in
- Bao gồm
- thừa kế
- ví dụ
- thay vì
- dự định
- trong
- giới thiệu
- liên quan đến
- vấn đề
- các vấn đề
- IT
- ITS
- Nguy hiểm
- json
- chỉ
- Giữ
- Biết
- kiến thức
- Ngôn ngữ
- Họ
- Dẫn
- để lại
- Ledger
- trái
- ít
- đòn bẩy
- thư viện
- Thư viện
- Lượt thích
- Dòng
- dòng
- . Các địa điểm
- logic
- Rất nhiều
- macro
- thực hiện
- Made Easy
- ma thuật
- duy trì
- làm cho
- LÀM CHO
- cách thức
- nhiều
- phù hợp
- Có thể..
- me
- cơ chế
- Bộ nhớ
- tâm thần
- bộ ba
- Siêu dữ liệu
- phương pháp
- Might
- tâm
- nhỏ
- gương
- mất tích
- chi tiết
- hầu hết
- chủ yếu
- Châm ngôn
- phải
- tên
- Được đặt theo tên
- đặt tên
- Tự nhiên
- Cần
- cần thiết
- không bao giờ
- Mới
- Không
- ghi
- vật
- of
- cung cấp
- Cung cấp
- thường
- Xưa
- on
- hàng loạt
- ONE
- có thể
- or
- Nền tảng khác
- vfoXNUMXfipXNUMXhfpiXNUMXufhpiXNUMXuf
- ra
- đề cương
- kết thúc
- riêng
- mô hình
- tham số
- thông số
- một phần
- riêng
- bên
- vượt qua
- thông qua
- vượt qua
- Đi qua
- Họa tiết
- hoàn hảo
- hiệu suất
- mảnh
- plato
- Thông tin dữ liệu Plato
- PlatoDữ liệu
- xin vui lòng
- có thể
- Thực tế
- thích hơn
- trình bày
- trước
- trước đây
- nguyên tắc
- Vấn đề
- vấn đề
- Sản phẩm
- chương trình
- Lập trình viên
- Lập trình viên
- Lập trình
- đúng
- cho
- mục đích
- mục đích
- đặt
- Puts
- Câu hỏi
- phạm vi
- hơn
- đạt
- đạt
- Reading
- có thật không
- lý do
- tóm tắt
- công thức
- dựa
- vẫn
- nhớ
- loại bỏ
- thay thế
- kho
- đại diện
- đại diện cho
- quyết định
- tôn trọng
- tái sử dụng
- Giàu
- Quy tắc
- quy tắc
- thời gian chạy
- s
- an toàn
- một cách an toàn
- Sự An Toàn
- sake
- Sam
- tương tự
- bối cảnh
- phạm vi
- Phần
- phần
- xem
- đã xem
- ý nghĩa
- định
- một số
- ngắn
- nên
- Gói Bạc
- Đơn giản
- đơn giản hóa
- đơn giản hóa
- duy nhất
- tình hình
- nhỏ
- So
- Phần mềm
- rắn
- giải pháp
- Giải quyết
- một số
- một cái gì đó
- nguồn
- mã nguồn
- Không gian
- nói
- đặc biệt
- riêng
- đặc biệt
- ổn định
- Bắt đầu
- Tuyên bố
- Bước
- Các bước
- Vẫn còn
- đơn giản
- hợp lý hóa
- Chuỗi
- cấu trúc
- như vậy
- đường
- đề nghị
- Bộ đồ
- phải
- chắc chắn
- bất ngờ
- Công tắc điện
- đồng bộ hóa.
- cú pháp
- hệ thống
- Hãy
- Nhiệm vụ
- nói
- Kiểm tra
- hơn
- Cảm ơn
- việc này
- Sản phẩm
- Nguồn
- cung cấp their dịch
- Them
- Đó
- họ
- điều
- điều
- nghĩ
- Thứ ba
- điều này
- những
- Tuy nhiên?
- Thông qua
- thời gian
- đến
- công cụ
- Hộp công cụ
- công cụ
- xúc động
- truyền thống
- cố gắng
- thực sự
- thử
- hai
- kiểu
- loại
- Không phổ biến
- Dưới
- độc đáo
- VÔ DANH
- không cần thiết
- cho đến khi
- hoang sơ
- không sử dụng
- trên
- Sử dụng
- sử dụng
- đã sử dụng
- người sử dang
- Người sử dụng
- sử dụng
- sử dụng
- bình thường
- thường
- giá trị
- khác nhau
- thành thạo
- rất
- muốn
- là
- Đường..
- cách
- we
- yếu
- Điều gì
- Là gì
- khi nào
- trong khi
- cái nào
- CHÚNG TÔI LÀ
- toàn bộ
- tại sao
- rộng rãi
- sẽ
- khôn ngoan
- với
- không có
- sẽ
- viết
- viết
- Sai
- nhưng
- Bạn
- trên màn hình
- mình
- zephyrnet