@@ -32,17 +32,41 @@ It’s more about modeling operations, imho.”
32
32
How to resolve? Is there a good DDD term to use here?
33
33
{% endcomment %}
34
34
35
- ### traits
35
+ ### Traits
36
36
37
37
Scala trait 可以用作简单的接口,但它们也可以包含抽象和具体的方法和字段,并且它们可以有参数,就像类一样。
38
38
它们为您提供了一种将行为组织成小型模块化单元的好方法。
39
39
稍后,当您想要创建属性和行为的具体实现时,类和对象可以扩展特征,根据需要混合尽可能多的特征以实现所需的行为。
40
40
41
41
作为如何将 traits 用作接口的示例,以下是三个 traits,它们为狗和猫等动物定义了结构良好并且模块化的行为:
42
42
43
+ {% tabs traits class=tabs-scala-version %}
44
+ {% tab 'Scala 2' for=traits %}
45
+
46
+ ``` scala
47
+ trait Speaker {
48
+ def speak (): String // has no body, so it’s abstract
49
+ }
50
+
51
+ trait TailWagger {
52
+ def startTail (): Unit = println(" tail is wagging" )
53
+ def stopTail (): Unit = println(" tail is stopped" )
54
+ }
55
+
56
+ trait Runner {
57
+ def startRunning (): Unit = println(" I’m running" )
58
+ def stopRunning (): Unit = println(" Stopped running" )
59
+ }
60
+ ```
61
+
62
+ {% endtab %}
63
+
64
+ {% tab 'Scala 3' for=traits %}
65
+
66
+
43
67
``` scala
44
68
trait Speaker :
45
- def speak (): String // 没有函数体,这样它是抽象的。
69
+ def speak (): String // has no body, so it’s abstract
46
70
47
71
trait TailWagger :
48
72
def startTail (): Unit = println(" tail is wagging" )
@@ -53,26 +77,80 @@ trait Runner:
53
77
def stopRunning (): Unit = println(" Stopped running" )
54
78
```
55
79
80
+ {% endtab %}
81
+ {% endtabs %}
82
+
56
83
鉴于这些特征,这里有一个 ` Dog ` 类,它扩展了所有这些特征,同时为抽象 ` speak ` 方法提供了一种行为:
57
84
85
+ {% tabs traits-class class=tabs-scala-version %}
86
+ {% tab 'Scala 2' for=traits-class %}
87
+
88
+ ``` scala
89
+ class Dog (name : String ) extends Speaker with TailWagger with Runner {
90
+ def speak (): String = " Woof!"
91
+ }
92
+ ```
93
+
94
+ {% endtab %}
95
+
96
+ {% tab 'Scala 3' for=traits-class %}
97
+
58
98
``` scala
59
99
class Dog (name : String ) extends Speaker , TailWagger , Runner :
60
100
def speak (): String = " Woof!"
61
101
```
62
102
103
+ {% endtab %}
104
+ {% endtabs %}
105
+
63
106
请注意该类如何使用 ` extends ` 关键字扩展 traits。
64
107
65
108
类似地,这里有一个 ` Cat ` 类,它实现了这些相同的 traits,同时还覆盖了它继承的两个具体方法:
66
109
110
+ {% tabs traits-override class=tabs-scala-version %}
111
+ {% tab 'Scala 2' for=traits-override %}
112
+
113
+ ``` scala
114
+ class Cat (name : String ) extends Speaker with TailWagger with Runner {
115
+ def speak (): String = " Meow"
116
+ override def startRunning (): Unit = println(" Yeah ... I don’t run" )
117
+ override def stopRunning (): Unit = println(" No need to stop" )
118
+ }
119
+ ```
120
+
121
+ {% endtab %}
122
+
123
+ {% tab 'Scala 3' for=traits-override %}
124
+
67
125
``` scala
68
126
class Cat (name : String ) extends Speaker , TailWagger , Runner :
69
127
def speak (): String = " Meow"
70
128
override def startRunning (): Unit = println(" Yeah ... I don’t run" )
71
129
override def stopRunning (): Unit = println(" No need to stop" )
72
130
```
73
131
132
+ {% endtab %}
133
+ {% endtabs %}
134
+
74
135
这些示例显示了如何使用这些类:
75
136
137
+ {% tabs traits-use class=tabs-scala-version %}
138
+ {% tab 'Scala 2' for=traits-use %}
139
+
140
+ ``` scala
141
+ val d = new Dog (" Rover" )
142
+ println(d.speak()) // prints "Woof!"
143
+
144
+ val c = new Cat (" Morris" )
145
+ println(c.speak()) // "Meow"
146
+ c.startRunning() // "Yeah ... I don’t run"
147
+ c.stopRunning() // "No need to stop"
148
+ ```
149
+
150
+ {% endtab %}
151
+
152
+ {% tab 'Scala 3' for=traits-use %}
153
+
76
154
``` scala
77
155
val d = Dog (" Rover" )
78
156
println(d.speak()) // prints "Woof!"
@@ -83,6 +161,9 @@ c.startRunning() // "Yeah ... I don’t run"
83
161
c.stopRunning() // "No need to stop"
84
162
```
85
163
164
+ {% endtab %}
165
+ {% endtabs %}
166
+
86
167
如果该代码有意义---太好了,您把 traits 作为接口感到舒服。
87
168
如果没有,请不要担心,它们在 [ Domain Modeling] [ data-1 ] 章节中有更详细的解释。
88
169
@@ -91,6 +172,24 @@ c.stopRunning() // "No need to stop"
91
172
Scala _ classes_ 用于 OOP 风格的编程。
92
173
这是一个模拟“人”的类的示例。在 OOP 中,字段通常是可变的,所以 ` firstName ` 和 ` lastName ` 都被声明为 ` var ` 参数:
93
174
175
+ {% tabs class_1 class=tabs-scala-version %}
176
+ {% tab 'Scala 2' for=class_1 %}
177
+
178
+ ``` scala
179
+ class Person (var firstName : String , var lastName : String ) {
180
+ def printFullName () = println(s " $firstName $lastName" )
181
+ }
182
+
183
+ val p = new Person (" John" , " Stephens" )
184
+ println(p.firstName) // "John"
185
+ p.lastName = " Legend"
186
+ p.printFullName() // "John Legend"
187
+ ```
188
+
189
+ {% endtab %}
190
+
191
+ {% tab 'Scala 3' for=class_1 %}
192
+
94
193
``` scala
95
194
class Person (var firstName : String , var lastName : String ):
96
195
def printFullName () = println(s " $firstName $lastName" )
@@ -101,13 +200,31 @@ p.lastName = "Legend"
101
200
p.printFullName() // "John Legend"
102
201
```
103
202
203
+ {% endtab %}
204
+ {% endtabs %}
205
+
104
206
请注意,类声明创建了一个构造函数:
105
207
106
- ``` 斯卡拉
208
+ {% tabs class_2 class=tabs-scala-version %}
209
+ {% tab 'Scala 2' for=class_2 %}
210
+
211
+ ``` scala
212
+ // this code uses that constructor
213
+ val p = new Person (" John" , " Stephens" )
214
+ ```
215
+
216
+ {% endtab %}
217
+
218
+ {% tab 'Scala 3' for=class_2 %}
219
+
220
+ ``` scala
107
221
// 此代码使用该构造函数
108
222
val p = Person (" 约翰" , " 斯蒂芬斯" )
109
223
```
110
224
225
+ {% endtab %}
226
+ {% endtabs %}
227
+
111
228
[ Domain Modeling] [ data-1 ] 章节中介绍了构造函数和其他与类相关的主题。
112
229
113
230
## FP 领域建模
@@ -120,20 +237,57 @@ to replace the Scala2 “sealed trait + case class” pattern. How to resolve?
120
237
121
238
以 FP 风格编写代码时,您将使用以下结构:
122
239
123
- - 枚举来定义 ADT
124
- - 样例类
125
- - Traits
240
+ - 代数数据类型(ADT)来定义数据
241
+ - Traits 来定义数据上的功能
242
+
243
+ ### 枚举和 Sum Types
126
244
127
- ### 枚举
245
+ Sum types 是在 Scala 中给代数数据类型(ADT)建模的一种方法。
128
246
129
247
` enum ` 构造是在 Scala 3 中对代数数据类型 (ADT) 进行建模的好方法。
248
+
130
249
例如,披萨具有三个主要属性:
131
250
132
251
- 面饼大小
133
252
- 面饼类型
134
253
- 馅料
135
254
136
- 这些是用枚举简洁地建模的:
255
+ 这些是用枚举简洁地建模的,它们是只包含单例值的 sum types:
256
+
257
+ {% tabs enum_1 class=tabs-scala-version %}
258
+ {% tab 'Scala 2' for=enum_1 %}
259
+
260
+ 在 Scala 2 中,用 ` sealed ` 类和 ` case object ` 组合在一起来定义枚举:
261
+
262
+ ``` scala
263
+ sealed abstract class CrustSize
264
+ object CrustSize {
265
+ case object Small extends CrustSize
266
+ case object Medium extends CrustSize
267
+ case object Large extends CrustSize
268
+ }
269
+
270
+ sealed abstract class CrustType
271
+ object CrustType {
272
+ case object Thin extends CrustType
273
+ case object Thick extends CrustType
274
+ case object Regular extends CrustType
275
+ }
276
+
277
+ sealed abstract class Topping
278
+ object Topping {
279
+ case object Cheese extends Topping
280
+ case object Pepperoni extends Topping
281
+ case object BlackOlives extends Topping
282
+ case object GreenOlives extends Topping
283
+ case object Onions extends Topping
284
+ }
285
+ ```
286
+
287
+ {% endtab %}
288
+ {% tab 'Scala 3' for=enum_1 %}
289
+
290
+ Scala 3 提供了 ` enum ` 结构来定义枚举:
137
291
138
292
``` scala
139
293
enum CrustSize :
@@ -146,8 +300,32 @@ enum Topping:
146
300
case Cheese , Pepperoni , BlackOlives , GreenOlives , Onions
147
301
```
148
302
303
+ {% endtab %}
304
+ {% endtabs %}
305
+
149
306
一旦你有了一个枚举,你就可以按照你通常使用特征、类或对象的所有方式来使用枚举:
150
307
308
+ {% tabs enum_2 class=tabs-scala-version %}
309
+ {% tab 'Scala 2' for=enum_2 %}
310
+
311
+ ``` scala
312
+ import CrustSize ._
313
+ val currentCrustSize = Small
314
+
315
+ // enums in a `match` expression
316
+ currentCrustSize match {
317
+ case Small => println(" Small crust size" )
318
+ case Medium => println(" Medium crust size" )
319
+ case Large => println(" Large crust size" )
320
+ }
321
+
322
+ // enums in an `if` statement
323
+ if (currentCrustSize == Small ) println(" Small crust size" )
324
+ ```
325
+
326
+ {% endtab %}
327
+ {% tab 'Scala 3' for=enum_2 %}
328
+
151
329
``` scala
152
330
import CrustSize .*
153
331
val currentCrustSize = Small
@@ -162,19 +340,42 @@ currentCrustSize match
162
340
if currentCrustSize == Small then println(" Small crust size" )
163
341
```
164
342
165
- 下面是另一个如何在 Scala 中创建和使用 ADT 的示例:
343
+ {% endtab %}
344
+ {% endtabs %}
345
+
346
+ 下面是另一个如何在 Scala 中创建 sum type 的示例,它不能被叫作枚举,因为 ` succ ` 样例类有参数:的示例:
347
+
348
+ {% tabs enum_3 class=tabs-scala-version %}
349
+ {% tab 'Scala 2' for=enum_3 %}
350
+
351
+ ``` scala
352
+ sealed abstract class Nat
353
+ object Nat {
354
+ case object Zero extends Nat
355
+ case class Succ (pred : Nat ) extends Nat
356
+ }
357
+ ```
358
+
359
+ Sum Types 在本书的[ 领域建模] ({% link _ overviews/scala3-book/domain-modeling-tools.md %})部分有详细的介绍。
360
+
361
+ {% endtab %}
362
+ {% tab 'Scala 3' for=enum_3 %}
166
363
167
364
``` scala
168
365
enum Nat :
169
366
case Zero
170
367
case Succ (pred : Nat )
171
368
```
172
369
173
- 枚举在本书的 [ 领域建模] [ data-1 ] 部分和 [ 参考文档] ({{ site.scala3ref }}/enums/enums.html) 中有详细介绍。
370
+ 枚举在本书的 [ 领域建模] ({% link _ overviews/scala3-book/domain-modeling-tools.md %})部分和 [ 参考文档] ({{ site.scala3ref }}/enums/enums.html) 中有详细介绍。
371
+
372
+ {% endtab %}
373
+ {% endtabs %}
174
374
175
- ### 样例类
375
+ ### Product Types
376
+
377
+ product type 是代数数据类型(ADT),它只含有一个形状,例如一个单例对象,在Scala 中用 ` case ` 对象来代表;或者是一个可以获取字段的不可变结构,用 ` case ` 类来代表。
176
378
177
- Scala ` case ` 类允许您使用不可变数据结构对概念进行建模。
178
379
` case ` 类具有 ` class ` 的所有功能,还包含其他功能,使它们对函数式编程很有用。
179
380
当编译器在 ` class ` 前面看到 ` case ` 关键字时,它具有以下效果和好处:
180
381
@@ -193,6 +394,9 @@ NOTE: Julien had a comment about how he decides when to use case classes vs clas
193
394
194
395
这段代码演示了几个 ` case ` 类的特性:
195
396
397
+ {% tabs case-class %}
398
+ {% tab 'Scala 2 and 3' for=case-class %}
399
+
196
400
``` scala
197
401
// define a case class
198
402
case class Person (
@@ -216,6 +420,9 @@ val p2 = p.copy(name = "Elton John")
216
420
p2 // : Person = Person(Elton John,Singer)
217
421
```
218
422
423
+ {% endtab %}
424
+ {% endtabs %}
425
+
219
426
有关 ` case ` 类的更多详细信息,请参阅 [ 领域建模] [ data-1 ] 部分。
220
427
221
428
[ data-1] : {% link _ zh-cn/overviews/scala3-book/domain-modeling-tools.md %}
0 commit comments