Skip to content

修改语句拗口问题 #624

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 17 commits into from
Nov 27, 2020
4 changes: 2 additions & 2 deletions docs/book/09-Polymorphism.md
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ Disposing Shared 0

**static long counter** 跟踪所创建的 **Shared** 实例数量,还提供了 **id** 的值。**counter** 的类型是 **long** 而不是 **int**,以防溢出(这只是个良好实践,对于本书的所有示例,**counter** 不会溢出)。**id** 是 **final** 的,因为它的值在初始化时确定后不应该变化。

在将一个 **shared** 对象附着在类上时,必须记住调用 `addRef()`,而 `dispose()` 方法会跟踪引用数,以确定在何时真正地执行清理工作。使用这种技巧需要加倍细心,但是如果正在共享需要被清理的对象,就没有太多选择了
在将一个 **shared** 对象附着在类上时,必须记住调用 `addRef()`,而 `dispose()` 方法会跟踪引用数,以确定在何时真正地执行清理工作。使用这种技巧需要加倍细心,但是如果需要清理正在共享的对象,你没有太多选择

### 构造器内部多态方法的行为

Expand Down Expand Up @@ -1154,7 +1154,7 @@ HappyActor
SadActor
```

**Stage** 对象中包含了 **Actor** 引用,该引用被初始化为指向一个 **HappyActor** 对象,这意味着 `performPlay()` 会产生一个特殊行为。但是既然引用可以在运行时与其他不同的对象绑定,那么它就可以被替换成对 **SadActor** 的引用,`performPlay()` 的行为随之改变。这样你就获得了运行时的动态灵活性(这被称为状态模式)。与之相反,我们不能在运行时决定继承不同的对象,那在编译时就完全确定下来了
**Stage** 对象中包含了 **Actor** 引用,该引用被初始化为指向一个 **HappyActor** 对象,这意味着 `performPlay()` 会产生一个特殊行为。但是既然引用可以在运行时与其他不同的对象绑定,那么它就可以被替换成对 **SadActor** 的引用,`performPlay()` 的行为随之改变。这样你就获得了运行时的动态灵活性(这被称为状态模式)。与之相反,我们无法在运行时才决定继承不同的对象;那在编译时就完全决定好了

有一条通用准则:使用继承表达行为的差异,使用属性表达状态的变化。在上个例子中,两者都用到了。通过继承得到的两个不同类在 `act()` 方法中表达了不同的行为,**Stage** 通过组合使自己的状态发生变化。这里状态的改变产生了行为的改变。

Expand Down
8 changes: 4 additions & 4 deletions docs/book/10-Interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ public interface Operations {
}
```

这是模版方法设计模式的一个版本(在“设计模式”一章中详细描述),`runOps()` 是一个模版方法。`runOps()` 使用可变参数列表,因而我们可以传入任意多的 **Operation** 参数并按顺序运行它们:
这是*模版*方法设计模式的一个版本(在“设计模式”一章中详细描述),`runOps()` 是一个模版方法。`runOps()` 使用可变参数列表,因而我们可以传入任意多的 **Operation** 参数并按顺序运行它们:

```java
// interface/Machine.java
Expand Down Expand Up @@ -768,17 +768,17 @@ Woodwind.play() MIDDLE_C
| 状态 | 不能包含属性(除了静态属性,不支持对象状态) | 可以包含属性,非抽象方法可能引用这些属性 |
| 默认方法 和 抽象方法 | 不需要在子类中实现默认方法。默认方法可以引用其他接口的方法 | 必须在子类中实现抽象方法 |
| 构造器 | 没有构造器 | 可以有构造器 |
| 可见性 | 隐式 **public** | 可以是 **protected** 或友元 |
| 可见性 | 隐式 **public** | 可以是 **protected** 或 "friendly" |

抽象类仍然是一个类,在创建新类时只能继承它一个。而创建类的过程中可以实现多个接口。

有一条实际经验:尽可能地抽象。因此,更倾向使用接口而不是抽象类。只有当必要时才使用抽象类。除非必须使用,否则不要用接口和抽象类。大多数时候,普通类已经做得很好,如果不行的话,再移动到接口或抽象类中。
有一条实际经验:在合理的范围内尽可能地抽象。因此,更倾向使用接口而不是抽象类。只有当必要时才使用抽象类。除非必须使用,否则不要用接口和抽象类。大多数时候,普通类已经做得很好,如果不行的话,再移动到接口或抽象类中。

<!-- Complete Decoupling -->

## 完全解耦

当方法操纵的是一个类而非接口时,它就只能作用于那个类或其子类。如果想把方法应用于那个继承层级结构之外的类,就会触霉头。接口在很大程度上放宽了这个限制,因而使用接口可以编写复用性更好的代码。
每当一个方法与一个类而不是接口一起工作时(当方法的参数是类而不是接口),你只能应用那个类或它的子类。如果你想把这方法应用到一个继承层次之外的类,是做不到的。接口在很大程度上放宽了这个限制,因而使用接口可以编写复用性更好的代码。

例如有一个类 **Processor** 有两个方法 `name()` 和 `process()`。`process()` 方法接受输入,修改并输出。把这个类作为基类用来创建各种不同类型的 **Processor**。下例中,**Processor** 的各个子类修改 String 对象(注意,返回类型可能是协变类型而非参数类型):

Expand Down