Skip to content

Commit a50a5c7

Browse files
authored
Update 04_Strategy.md
1 parent bea3fac commit a50a5c7

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

Diff for: ch09/04_Strategy.md

+34-15
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,19 @@ class TrustTaxStrategy extends DefaultTaxStrategy<Trust> {
6666
}
6767
```
6868

69-
可以定义一个收入为 `100,000.00` 美元的人和两个收入相同的信托人,一个非营利组织和一个非营利组织
69+
可以定义一个收入为 `100,000.00` 美元的人和两个收入相同的信托人,一个非营利组织和一个营利组织
7070

7171
```java
7272
Person person = new Person(10000000);
7373
Trust nonProfit = new Trust(10000000, true);
7474
Trust forProfit = new Trust(10000000, false);
7575
```
7676

77-
按照良好的做法,我们用长整数表示所有的货币价值,例如收入或税收,以毫秒为单位表示价值(参见“有效性的一般编程”一章中的“避免浮动和双重,如果需要确切答案” `Java``Joshua Bloch``Addison-Wesley`编写)。
77+
按照良好的做法,我们用长整数表示所有的货币价值,例如收入或税收,以毫秒为单位表示价值(参见“有效性的一般编程”一章中的“避免浮动和双重,如果需要确切答
78+
案” `Java``Joshua Bloch``Addison-Wesley`编写)。
7879

79-
对于每个纳税人` P`,可能有许多可能的计算税收策略。 每个策略实现接口 `TaxStrategy<P>`,该接口指定一个方法计算 `Tax`,该方法将 `P` 作为纳税人的参数并返回所支付的税金。 类`DefaultTaxStrategy` 通过将收入乘以 `40%` 的固定税率来计算税额,而 `DodgingTaxStrategy` 则始终计算零税额:
80+
对于每个纳税人` P`,可能有许多可能的计算税收策略。 每个策略实现接口 `TaxStrategy<P>`,该接口指定一个方法计算 `Tax`,该方法将 `P` 作为纳税人的参数
81+
并返回所支付的税金。 类`DefaultTaxStrategy` 通过将收入乘以 `40%` 的固定税率来计算税额,而 `DodgingTaxStrategy` 则始终计算零税额:
8082

8183
```java
8284
TaxStrategy<Person> defaultStrategy = new DefaultStrategy<Person>();
@@ -85,7 +87,8 @@ assert defaultStrategy.computeTax(person) == 4000000;
8587
assert dodgingStrategy.computeTax(person) == 0;
8688
```
8789

88-
当然,我们的例子是为了说明而简化的 - 我们不建议您使用这些策略中的任何一种来计算税收! 但是应该清楚这些技术是如何延伸到更复杂的纳税人和税收策略的。最后,如果信托是非营利组织,并且使用默认税策略,则 `TrustTaxStrategy` 类计算零税额:
90+
当然,我们的例子是为了说明而简化的 - 我们不建议您使用这些策略中的任何一种来计算税收! 但是应该清楚这些技术是如何延伸到更复杂的纳税人和税收策略的。最
91+
后,如果信托是非营利组织,并且使用默认税策略,则 `TrustTaxStrategy` 类计算零税额:
8992

9093
```java
9194
TaxStrategy<Trust> trustStrategy = new TrustTaxStrategy();
@@ -99,13 +102,20 @@ assert trustStrategy.computeTax(forProfit) == 4000000;
99102
trustStrategy.computeTax(person); // 编译报错
100103
```
101104

102-
没有泛型,`TrustTaxStrategy``computeTax` 方法将不得不接受 `TaxPayer` 类型的参数并将其转换为 `Trust` 类型,并且错误会在运行时抛出异常,而不是在编译时捕获。
105+
没有泛型,`TrustTaxStrategy``computeTax` 方法将不得不接受 `TaxPayer` 类型的参数并将其转换为 `Trust` 类型,并且错误会在运行时抛出异常,而不是
106+
在编译时捕获。
103107

104-
这个例子说明了许多面向对象程序中的结构化技术 - 即并行类层次结构。在这种情况下,一个类层次结构由 `TaxPayer``Person``Trust` 组成。并行类层次结构由与以下每个策略相对应的策略组成:两种策略:`DefaultTaxStrategy``DutingTaxStrategy` 适用于任何 `TaxPayer`,没有专门的策略适用于 `Person`,并且 `Trust` 有一个专门的策略。
108+
这个例子说明了许多面向对象程序中的结构化技术 - 即并行类层次结构。在这种情况下,一个类层次结构由 `TaxPayer``Person``Trust` 组成。并行类层次结
109+
构由与以下每个策略相对应的策略组成:两种策略:`DefaultTaxStrategy``DutingTaxStrategy` 适用于任何 `TaxPayer`,没有专门的策略适用于
110+
`Person`,并且 `Trust` 有一个专门的策略。
105111

106-
通常,这种并行层次结构之间存在某种联系。在这种情况下,与给定 `TaxPayer` 并行的 `TaxStrategy``computeTax` 方法需要具有相应类型的参数;例如,`TrustTaxStrategy``computeTax` 方法需要一个 `Trust` 类型的参数。对于泛型,我们可以在类型本身中整齐地捕捉这个连接。在这种情况下,`TaxStrategy<P>``computeTax` 方法需要 `P` 类型的参数,其中 `P` 必须是 `TaxPayer` 的子类。使用我们在此描述的技术,泛型通常可用于捕获其他并行类层次结构中的类似关系。
112+
通常,这种并行层次结构之间存在某种联系。在这种情况下,与给定 `TaxPayer` 并行的 `TaxStrategy``computeTax` 方法需要具有相应类型的参数;例如,
113+
`TrustTaxStrategy``computeTax` 方法需要一个 `Trust` 类型的参数。对于泛型,我们可以在类型本身中整齐地捕捉这个连接。在这种情况下,
114+
`TaxStrategy<P>``computeTax` 方法需要 `P` 类型的参数,其中 `P` 必须是 `TaxPayer` 的子类。使用我们在此描述的技术,泛型通常可用于捕获其他并行
115+
类层次结构中的类似关系。
107116

108-
具有递归泛型的高级策略模式在策略模式的更高级用途中,对象包含要应用于其的策略。建模这种情况需要递归泛型类型,并利用一种技巧为此指定一个泛型类型。修订后的战略模式如示例 `9-7` 所示。在高级版本中,每个纳税人对象都包含自己的税收策略,每种纳税人的构造函数都包含一个税收策略作为附加参数:
117+
具有递归泛型的高级策略模式在策略模式的更高级用途中,对象包含要应用于其的策略。建模这种情况需要递归泛型类型,并利用一种技巧为此指定一个泛型类型。修订
118+
后的战略模式如示例 `9-7` 所示。在高级版本中,每个纳税人对象都包含自己的税收策略,每种纳税人的构造函数都包含一个税收策略作为附加参数:
109119

110120
```java
111121
Person normal = new Person(10000000, new DefaultTaxStrategy<Person>());
@@ -125,7 +135,8 @@ assert forProfit.computeTax() == 4000000;
125135

126136
这种结构往往是可取的,因为可以将给定的税收策略直接与给定的纳税人联系起来。
127137

128-
之前,我们使用了 `TaxPayer` 类和接口 `TaxStrategy<P>`,其中类型变量 `P` 代表策略适用的 `TaxPayer` 的子类。 现在我们必须将类型参数 `P` 添加到两者中,以便类 `TaxPayer<P>` 可以具有类型 `TaxStrategy<P>` 的字段。 类型变量 `P` 的新声明必须是递归的,如 `TaxPayer` 类的新标题所示:
138+
之前,我们使用了 `TaxPayer` 类和接口 `TaxStrategy<P>`,其中类型变量 `P` 代表策略适用的 `TaxPayer` 的子类。 现在我们必须将类型参数 `P` 添加到两者
139+
中,以便类 `TaxPayer<P>` 可以具有类型 `TaxStrategy<P>` 的字段。 类型变量 `P` 的新声明必须是递归的,如 `TaxPayer` 类的新标题所示:
129140

130141
```java
131142
class TaxPayer<P extends TaxPayer<P>>
@@ -138,9 +149,11 @@ interface Comparable<T extends Comparable<T>>
138149
class Enum<E extends Enum<E>>
139150
```
140151

141-
在所有这三种情况下,类或接口都是类型层次结构的基类,类型参数代表基类的特定子类。 因此,`TaxPayer<P>` 中的 `P` 代表特定类型的纳税人,例如 `Person` 或 `Trust`; 就像 `Comparable<T>` 中的 `T` 代表被比较的特定类,比如 `String`; 或 `Enum<E>` 中的 `E` 表示特定枚举类型,如季节。
152+
在所有这三种情况下,类或接口都是类型层次结构的基类,类型参数代表基类的特定子类。 因此,`TaxPayer<P>` 中的 `P` 代表特定类型的纳税人,例如 `Person`
153+
或 `Trust`; 就像 `Comparable<T>` 中的 `T` 代表被比较的特定类,比如 `String`; 或 `Enum<E>` 中的 `E` 表示特定枚举类型,如季节。
142154

143-
纳税人类包含一个税收策略字段和一个将纳税人转到税收策略的方法,以及一个像 `TaxPayer` 中使用的 `P` 一样的递归声明。 总的来说,我们可能期望它看起来像这样:
155+
纳税人类包含一个税收策略字段和一个将纳税人转到税收策略的方法,以及一个像 `TaxPayer` 中使用的 `P` 一样的递归声明。 总的来说,我们可能期望它看起来像这
156+
样:
144157

145158
```java
146159
// not well-typed!
@@ -155,9 +168,12 @@ class Person extends TaxPayer<Person> { ... }
155168
class Trust extends TaxPayer<Trust> { ... }
156169
```
157170

158-
但编译器拒绝上面的类型错误。 问题是这有 `TaxPayer<P>` 类型,而 `computeTax` 的参数必须有 `P` 类型。 事实上,在每个具体的纳税人类别中,例如人员或信任,这种情况确实具有类型 `P`; 例如,`Person` 扩展了 `TaxPayer<Person>`,所以 `P` 与此类中的 `Person` 相同。 所以,实际上,这将与 `P` 具有相同的类型,但类型系统不知道!
171+
但编译器拒绝上面的类型错误。 问题是这有 `TaxPayer<P>` 类型,而 `computeTax` 的参数必须有 `P` 类型。 事实上,在每个具体的纳税人类别中,例如人员或信
172+
任,这种情况确实具有类型 `P`; 例如,`Person` 扩展了 `TaxPayer<Person>`,所以 `P` 与此类中的 `Person` 相同。 所以,实际上,这将与 `P` 具有相同的
173+
类型,但类型系统不知道!
159174

160-
我们可以用一个技巧来解决这个问题。 在基类 `TaxPayer<P>` 中,我们定义了一个抽象方法 `getThis`,其目的是返回与此相同的值,但赋予其类型 `P`. 该方法在与特定种类的纳税人相对应的每个类中实例化,例如 `Person``Trust`,其类型实际上与 `P` 类型相同。 概括地说,修正的代码现在看起来像这样:
175+
我们可以用一个技巧来解决这个问题。 在基类 `TaxPayer<P>` 中,我们定义了一个抽象方法 `getThis`,其目的是返回与此相同的值,但赋予其类型 `P`. 该方法在
176+
与特定种类的纳税人相对应的每个类中实例化,例如 `Person``Trust`,其类型实际上与 `P` 类型相同。 概括地说,修正的代码现在看起来像这样:
161177

162178
```java
163179
// now correctly typed
@@ -177,9 +193,12 @@ final class Trust extends TaxPayer<Trust> {
177193
}
178194
```
179195

180-
与以前的代码不同之处在于粗体。 这个的出现被调用 `getThis` 所取代; 方法 `getThis` 在基类中声明为抽象,并且在基类的每个最终子类中都适当地实例化。 基类 `TaxPayer<P>` 必须声明为 `abstract`,因为它声明了 `getThis` 的类型,但未声明正文。 `getThis` 的主体在 `Person``Trust` 的最后一个子类中声明。
196+
与以前的代码不同之处在于粗体。 这个的出现被调用 `getThis` 所取代; 方法 `getThis` 在基类中声明为抽象,并且在基类的每个最终子类中都适当地实例化。 基
197+
`TaxPayer<P>` 必须声明为 `abstract`,因为它声明了 `getThis` 的类型,但未声明正文。 `getThis` 的主体在 `Person``Trust` 的最后一个子类中声
198+
明。
181199

182-
由于信任被声明为最终的,所以它不能有子类。 假设我们想要一个 `Trust` 的子类 `NonProfitTrust`。 那么我们不仅需要删除 `Trust` 类的最终声明,还需要为其添加一个类型参数。 以下是所需代码的草图:
200+
由于信任被声明为最终的,所以它不能有子类。 假设我们想要一个 `Trust` 的子类 `NonProfitTrust`。 那么我们不仅需要删除 `Trust` 类的最终声明,还需要为
201+
其添加一个类型参数。 以下是所需代码的草图:
183202

184203
```java
185204
abstract class Trust<T extends Trust<T>> extends TaxPayer<T> { ... }

0 commit comments

Comments
 (0)