Skip to content

add tabs in zh-cn/overview/scala3-book/num9 #2673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 219 additions & 12 deletions _zh-cn/overviews/scala3-book/taste-modeling.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,41 @@ It’s more about modeling operations, imho.”
How to resolve? Is there a good DDD term to use here?
{% endcomment %}

### traits
### Traits

Scala trait 可以用作简单的接口,但它们也可以包含抽象和具体的方法和字段,并且它们可以有参数,就像类一样。
它们为您提供了一种将行为组织成小型模块化单元的好方法。
稍后,当您想要创建属性和行为的具体实现时,类和对象可以扩展特征,根据需要混合尽可能多的特征以实现所需的行为。

作为如何将 traits 用作接口的示例,以下是三个 traits,它们为狗和猫等动物定义了结构良好并且模块化的行为:

{% tabs traits class=tabs-scala-version %}
{% tab 'Scala 2' for=traits %}

```scala
trait Speaker {
def speak(): String // has no body, so it’s abstract
}

trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}

trait Runner {
def startRunning(): Unit = println("I’m running")
def stopRunning(): Unit = println("Stopped running")
}
```

{% endtab %}

{% tab 'Scala 3' for=traits %}


```scala
trait Speaker:
def speak(): String // 没有函数体,这样它是抽象的。
def speak(): String // has no body, so it’s abstract

trait TailWagger:
def startTail(): Unit = println("tail is wagging")
Expand All @@ -53,26 +77,80 @@ trait Runner:
def stopRunning(): Unit = println("Stopped running")
```

{% endtab %}
{% endtabs %}

鉴于这些特征,这里有一个 `Dog` 类,它扩展了所有这些特征,同时为抽象 `speak` 方法提供了一种行为:

{% tabs traits-class class=tabs-scala-version %}
{% tab 'Scala 2' for=traits-class %}

```scala
class Dog(name: String) extends Speaker with TailWagger with Runner {
def speak(): String = "Woof!"
}
```

{% endtab %}

{% tab 'Scala 3' for=traits-class %}

```scala
class Dog(name: String) extends Speaker, TailWagger, Runner:
def speak(): String = "Woof!"
```

{% endtab %}
{% endtabs %}

请注意该类如何使用 `extends` 关键字扩展 traits。

类似地,这里有一个 `Cat` 类,它实现了这些相同的 traits,同时还覆盖了它继承的两个具体方法:

{% tabs traits-override class=tabs-scala-version %}
{% tab 'Scala 2' for=traits-override %}

```scala
class Cat(name: String) extends Speaker with TailWagger with Runner {
def speak(): String = "Meow"
override def startRunning(): Unit = println("Yeah ... I don’t run")
override def stopRunning(): Unit = println("No need to stop")
}
```

{% endtab %}

{% tab 'Scala 3' for=traits-override %}

```scala
class Cat(name: String) extends Speaker, TailWagger, Runner:
def speak(): String = "Meow"
override def startRunning(): Unit = println("Yeah ... I don’t run")
override def stopRunning(): Unit = println("No need to stop")
```

{% endtab %}
{% endtabs %}

这些示例显示了如何使用这些类:

{% tabs traits-use class=tabs-scala-version %}
{% tab 'Scala 2' for=traits-use %}

```scala
val d = new Dog("Rover")
println(d.speak()) // prints "Woof!"

val c = new Cat("Morris")
println(c.speak()) // "Meow"
c.startRunning() // "Yeah ... I don’t run"
c.stopRunning() // "No need to stop"
```

{% endtab %}

{% tab 'Scala 3' for=traits-use %}

```scala
val d = Dog("Rover")
println(d.speak()) // prints "Woof!"
Expand All @@ -83,6 +161,9 @@ c.startRunning() // "Yeah ... I don’t run"
c.stopRunning() // "No need to stop"
```

{% endtab %}
{% endtabs %}

如果该代码有意义---太好了,您把 traits 作为接口感到舒服。
如果没有,请不要担心,它们在 [Domain Modeling][data-1] 章节中有更详细的解释。

Expand All @@ -91,6 +172,24 @@ c.stopRunning() // "No need to stop"
Scala _classes_ 用于 OOP 风格的编程。
这是一个模拟“人”的类的示例。在 OOP 中,字段通常是可变的,所以 `firstName` 和 `lastName` 都被声明为 `var` 参数:

{% tabs class_1 class=tabs-scala-version %}
{% tab 'Scala 2' for=class_1 %}

```scala
class Person(var firstName: String, var lastName: String) {
def printFullName() = println(s"$firstName $lastName")
}

val p = new Person("John", "Stephens")
println(p.firstName) // "John"
p.lastName = "Legend"
p.printFullName() // "John Legend"
```

{% endtab %}

{% tab 'Scala 3' for=class_1 %}

```scala
class Person(var firstName: String, var lastName: String):
def printFullName() = println(s"$firstName $lastName")
Expand All @@ -101,13 +200,31 @@ p.lastName = "Legend"
p.printFullName() // "John Legend"
```

{% endtab %}
{% endtabs %}

请注意,类声明创建了一个构造函数:

```斯卡拉
{% tabs class_2 class=tabs-scala-version %}
{% tab 'Scala 2' for=class_2 %}

```scala
// this code uses that constructor
val p = new Person("John", "Stephens")
```

{% endtab %}

{% tab 'Scala 3' for=class_2 %}

```scala
// 此代码使用该构造函数
val p = Person("约翰", "斯蒂芬斯")
```

{% endtab %}
{% endtabs %}

[Domain Modeling][data-1] 章节中介绍了构造函数和其他与类相关的主题。

## FP 领域建模
Expand All @@ -120,20 +237,57 @@ to replace the Scala2 “sealed trait + case class” pattern. How to resolve?

以 FP 风格编写代码时,您将使用以下结构:

- 枚举来定义 ADT
- 样例类
- Traits
- 代数数据类型(ADT)来定义数据
- Traits 来定义数据上的功能

### 枚举和 Sum Types

### 枚举
Sum types 是在 Scala 中给代数数据类型(ADT)建模的一种方法。

`enum` 构造是在 Scala 3 中对代数数据类型 (ADT) 进行建模的好方法。

例如,披萨具有三个主要属性:

- 面饼大小
- 面饼类型
- 馅料

这些是用枚举简洁地建模的:
这些是用枚举简洁地建模的,它们是只包含单例值的 sum types:

{% tabs enum_1 class=tabs-scala-version %}
{% tab 'Scala 2' for=enum_1 %}

在 Scala 2 中,用 `sealed` 类和 `case object` 组合在一起来定义枚举:

```scala
sealed abstract class CrustSize
object CrustSize {
case object Small extends CrustSize
case object Medium extends CrustSize
case object Large extends CrustSize
}

sealed abstract class CrustType
object CrustType {
case object Thin extends CrustType
case object Thick extends CrustType
case object Regular extends CrustType
}

sealed abstract class Topping
object Topping {
case object Cheese extends Topping
case object Pepperoni extends Topping
case object BlackOlives extends Topping
case object GreenOlives extends Topping
case object Onions extends Topping
}
```

{% endtab %}
{% tab 'Scala 3' for=enum_1 %}

Scala 3 提供了 `enum` 结构来定义枚举:

```scala
enum CrustSize:
Expand All @@ -146,8 +300,32 @@ enum Topping:
case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions
```

{% endtab %}
{% endtabs %}

一旦你有了一个枚举,你就可以按照你通常使用特征、类或对象的所有方式来使用枚举:

{% tabs enum_2 class=tabs-scala-version %}
{% tab 'Scala 2' for=enum_2 %}

```scala
import CrustSize._
val currentCrustSize = Small

// enums in a `match` expression
currentCrustSize match {
case Small => println("Small crust size")
case Medium => println("Medium crust size")
case Large => println("Large crust size")
}

// enums in an `if` statement
if (currentCrustSize == Small) println("Small crust size")
```

{% endtab %}
{% tab 'Scala 3' for=enum_2 %}

```scala
import CrustSize.*
val currentCrustSize = Small
Expand All @@ -162,19 +340,42 @@ currentCrustSize match
if currentCrustSize == Small then println("Small crust size")
```

下面是另一个如何在 Scala 中创建和使用 ADT 的示例:
{% endtab %}
{% endtabs %}

下面是另一个如何在 Scala 中创建 sum type 的示例,它不能被叫作枚举,因为 `succ` 样例类有参数:的示例:

{% tabs enum_3 class=tabs-scala-version %}
{% tab 'Scala 2' for=enum_3 %}

```scala
sealed abstract class Nat
object Nat {
case object Zero extends Nat
case class Succ(pred: Nat) extends Nat
}
```

Sum Types 在本书的[领域建模]({% link _overviews/scala3-book/domain-modeling-tools.md %})部分有详细的介绍。

{% endtab %}
{% tab 'Scala 3' for=enum_3 %}

```scala
enum Nat:
case Zero
case Succ(pred: Nat)
```

枚举在本书的 [领域建模][data-1] 部分和 [参考文档]({{ site.scala3ref }}/enums/enums.html) 中有详细介绍。
枚举在本书的 [领域建模]({% link _overviews/scala3-book/domain-modeling-tools.md %})部分和 [参考文档]({{ site.scala3ref }}/enums/enums.html) 中有详细介绍。

{% endtab %}
{% endtabs %}

### 样例类
### Product Types

product type 是代数数据类型(ADT),它只含有一个形状,例如一个单例对象,在Scala 中用 `case` 对象来代表;或者是一个可以获取字段的不可变结构,用 `case` 类来代表。

Scala `case` 类允许您使用不可变数据结构对概念进行建模。
`case` 类具有 `class` 的所有功能,还包含其他功能,使它们对函数式编程很有用。
当编译器在 `class` 前面看到 `case` 关键字时,它具有以下效果和好处:

Expand All @@ -193,6 +394,9 @@ NOTE: Julien had a comment about how he decides when to use case classes vs clas

这段代码演示了几个 `case` 类的特性:

{% tabs case-class %}
{% tab 'Scala 2 and 3' for=case-class %}

```scala
// define a case class
case class Person(
Expand All @@ -216,6 +420,9 @@ val p2 = p.copy(name = "Elton John")
p2 // : Person = Person(Elton John,Singer)
```

{% endtab %}
{% endtabs %}

有关 `case` 类的更多详细信息,请参阅 [领域建模][data-1] 部分。

[data-1]: {% link _zh-cn/overviews/scala3-book/domain-modeling-tools.md %}