fix issue:#161 (#364)

* Fix issue:#112 翻译Summary

Signed-off-by: crimson <1291463831@qq.com>

* fix issue:#161 翻译工厂模式

Signed-off-by: crimson <1291463831@qq.com>

* fix issue:#161

Signed-off-by: crimson <1291463831@qq.com>
This commit is contained in:
Crimson_Loves_Code
2020-01-22 09:31:31 +08:00
committed by Joe
parent ee50c09b55
commit 2010769b24

View File

@@ -425,8 +425,368 @@ Spinning
<!-- Factories: Encapsulating Object Creation -->
## 工厂模式
当你发现必须将新类型添加到系统中时,合理的第一步是使用多态性为这些新类型创建一个通用接口。这会将你系统中的其余代码与要添加的特定类型的信息分开,使得可以在不改变现有代码的情况下添加新类型……或者看起来如此。起初,在这种设计中,似乎你必须更改代码的唯一地方就是你继承新类型的地方,但这并不是完全正确的。 你仍然必须创建新类型的对象,并且在创建时必须指定要使用的确切构造器。因此,如果创建对象的代码分布在整个应用程序中,那么在添加新类型时,你将遇到相同的问题——你仍然必须追查你代码中新类型碍事的所有地方。恰好是类型的创建碍事,而不是类型的使用(通过多态处理),但是效果是一样的:添加新类型可能会引起问题。
解决方案是强制对象的创建都通过通用工厂进行,而不是允许创建代码在整个系统中传播。 如果你程序中的所有代码都必须执行通过该工厂创建你的一个对象,那么在添加新类时只需要修改工厂即可。
由于每个面向对象的程序都会创建对象,并且很可能会通过添加新类型来扩展程序,因此工厂是最通用的设计模式之一。
举例来说,让我们重新看一下**Shape**系统。 首先,我们需要一个用于所有示例的基本框架。 如果无法创建**Shape**对象,则需要抛出一个合适的异常:
```java
// patterns/shapes/BadShapeCreation.java package patterns.shapes;
public class BadShapeCreation extends RuntimeException {
public BadShapeCreation(String msg) {
super(msg);
}
}
```
接下来,是一个**Shape**基类:
```java
// patterns/shapes/Shape.java
package patterns.shapes;
public class Shape {
private static int counter = 0;
private int id = counter++;
@Override
public String toString(){
return getClass().getSimpleName() + "[" + id + "]";
}
public void draw() {
System.out.println(this + " draw");
}
public void erase() {
System.out.println(this + " erase");
}
}
```
该类自动为每一个**Shape**对象创建一个唯一的`id`
`toString()`使用运行期信息来发现特定的**Shape**子类的名字。
现在我们能很快创建一些**Shape**子类了:
```java
// patterns/shapes/Circle.java
package patterns.shapes;
public class Circle extends Shape {}
```
```java
// patterns/shapes/Square.java
package patterns.shapes;
public class Square extends Shape {}
```
```java
// patterns/shapes/Triangle.java
package patterns.shapes;
public class Triangle extends Shape {}
```
工厂是具有能够创建对象的方法的类。 我们有几个示例版本,因此我们将定义一个接口:
```java
// patterns/shapes/FactoryMethod.java
package patterns.shapes;
public interface FactoryMethod {
Shape create(String type);
}
```
`create()`接收一个参数,这个参数使其决定要创建哪一种**Shape**对象,这里是`String`,但是它其实可以是任何数据集合。对象的初始化数据(这里是字符串)可能来自系统外部。 这个例子将测试工厂:
```java
// patterns/shapes/FactoryTest.java
package patterns.shapes;
import java.util.stream.*;
public class FactoryTest {
public static void test(FactoryMethod factory) {
Stream.of("Circle", "Square", "Triangle",
"Square", "Circle", "Circle", "Triangle")
.map(factory::create)
.peek(Shape::draw)
.peek(Shape::erase)
.count(); // Terminal operation
}
}
```
在主函数`main()`里,要记住除非你在最后使用了一个终结操作,否则**Stream**不会做任何事情。在这里,`count()`的值被丢弃了。
创建工厂的一种方法是显式创建每种类型:
```java
// patterns/ShapeFactory1.java
// A simple static factory method
import java.util.*;
import java.util.stream.*;
import patterns.shapes.*;
public class ShapeFactory1 implements FactoryMethod {
public Shape create(String type) {
switch(type) {
case "Circle": return new Circle();
case "Square": return new Square();
case "Triangle": return new Triangle();
default: throw new BadShapeCreation(type);
}
}
public static void main(String[] args) {
FactoryTest.test(new ShapeFactory1());
}
}
```
输出结果:
```java
Circle[0] draw
Circle[0] erase
Square[1] draw
Square[1] erase
Triangle[2] draw
Triangle[2] erase
Square[3] draw
Square[3] erase
Circle[4] draw
Circle[4] erase
Circle[5] draw
Circle[5] erase
Triangle[6] draw
Triangle[6] erase
```
`create()`现在是添加新类型的Shape时系统中唯一需要更改的其他代码。
### 动态工厂
前面例子中的**静态**`create()`方法强制所有创建操作都集中在一个位置,因此这是添加新类型的**Shape**时唯一必须更改代码的地方。这当然是一个合理的解决方案,因为它把创建对象的过程限制在一个框内。但是,如果你在添加新类时无需修改任何内容,那就太好了。 以下版本使用反射在首次需要时将**Shape**的构造器动态加载到工厂列表中:
```java
// patterns/ShapeFactory2.java
import java.util.*;
import java.lang.reflect.*;
import java.util.stream.*;
import patterns.shapes.*;
public class ShapeFactory2 implements FactoryMethod {
Map<String, Constructor> factories = new HashMap<>();
static Constructor load(String id) {
System.out.println("loading " + id);
try {
return Class.forName("patterns.shapes." + id)
.getConstructor();
} catch(ClassNotFoundException |
NoSuchMethodException e) {
throw new BadShapeCreation(id);
}
}
public Shape create(String id) {
try {
return (Shape)factories
.computeIfAbsent(id, ShapeFactory2::load)
.newInstance();
} catch(InstantiationException |
IllegalAccessException |
InvocationTargetException e) {
throw new BadShapeCreation(id);
}
}
public static void main(String[] args) {
FactoryTest.test(new ShapeFactory2());
}
}
```
输出结果:
```java
loading Circle
Circle[0] draw
Circle[0] erase
loading Square
Square[1] draw
Square[1] erase
loading Triangle
Triangle[2] draw
Triangle[2] erase
Square[3] draw
Square[3] erase
Circle[4] draw
Circle[4] erase
Circle[5] draw
Circle[5] erase
Triangle[6] draw
Triangle[6] erase
```
和之前一样,`create()`方法基于你传递给它的**String**参数生成新的**Shape**s但是在这里它是通过在**HashMap**中查找作为键的**String**来实现的。 返回的值是一个构造器,该构造器用于通过调用`newInstance()`创建新的**Shape**对象。
然而,当你开始运行程序时,工厂的`map`为空。`create()`使用`map``computeIfAbsent()`方法来查找构造器(如果该构造器已存在于`map`中)。如果不存在则使用`load()`计算出该构造器,并将其插入到`map`中。 从输出中可以看到,每种特定类型的**Shape**都是在第一次请求时才加载的,然后只需要从`map`中检索它。
### 多态工厂
《设计模式》这本书强调指出,采用“工厂方法”模式的原因是可以从基本工厂中继承出不同类型的工厂。 再次修改示例,使工厂方法位于单独的类中:
```java
// patterns/ShapeFactory3.java
// Polymorphic factory methods
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import patterns.shapes.*;
interface PolymorphicFactory {
Shape create();
}
class RandomShapes implements Supplier<Shape> {
private final PolymorphicFactory[] factories;
private Random rand = new Random(42);
RandomShapes(PolymorphicFactory... factories){
this.factories = factories;
}
public Shape get() {
return factories[ rand.nextInt(factories.length)].create();
}
}
public class ShapeFactory3 {
public static void main(String[] args) {
RandomShapes rs = new RandomShapes(
Circle::new,
Square::new,
Triangle::new);
Stream.generate(rs)
.limit(6)
.peek(Shape::draw)
.peek(Shape::erase)
.count();
}
}
```
输出结果:
```java
Triangle[0] draw
Triangle[0] erase
Circle[1] draw
Circle[1] erase
Circle[2] draw
Circle[2] erase
Triangle[3] draw
Triangle[3] erase
Circle[4] draw
Circle[4] erase
Square[5] draw
Square[5] erase
```
**RandomShapes**实现了**Supplier \<Shape>**,因此可用于通过`Stream.generate()`创建**Stream**。 它的构造器采用**PolymorphicFactory**对象的可变参数列表。 变量参数列表以数组形式出现,因此列表是以数组形式在内部存储的。`get()`方法随机获取此数组中一个对象的索引,并在结果上调用`create()`以产生新的**Shape**对象。 添加新类型的**Shape**时,**RandomShapes**构造器是唯一需要更改的地方。 请注意,此构造器需要**Supplier \<Shape>**。 我们传递给其**Shape**构造器的方法引用,该引用可满足**Supplier \<Shape>**约定因为Java 8支持结构一致性。
鉴于**ShapeFactory2.java**可能会抛出异常,使用此方法则没有任何异常——它在编译时完全确定。
### 抽象工厂
抽象工厂模式看起来像我们之前所见的工厂对象,但拥有不是一个工厂方法而是几个工厂方法, 每个工厂方法都会创建不同种类的对象。 这个想法是在创建工厂对象时,你决定如何使用该工厂创建的所有对象。 《设计模式》中提供的示例实现了跨各种图形用户界面GUI的可移植性你创建一个适合你正在使用的GUI的工厂对象然后从中请求菜单按钮滑块等等它将自动为GUI创建适合该项目版本的组件。 因此你可以将从一个GUI更改为另一个所产生的影响隔离限制在一处。 作为另一个示例,假设你正在创建一个通用游戏环境来支持不同类型的游戏。 使用抽象工厂看起来就像下文那样:
```java
// patterns/abstractfactory/GameEnvironment.java
// An example of the Abstract Factory pattern
// {java patterns.abstractfactory.GameEnvironment}
package patterns.abstractfactory;
import java.util.function.*;
interface Obstacle {
void action();
}
interface Player {
void interactWith(Obstacle o);
}
class Kitty implements Player {
@Override
public void interactWith(Obstacle ob) {
System.out.print("Kitty has encountered a ");
ob.action();
}
}
class KungFuGuy implements Player {
@Override
public void interactWith(Obstacle ob) {
System.out.print("KungFuGuy now battles a ");
ob.action();
}
}
class Puzzle implements Obstacle {
@Override
public void action() {
System.out.println("Puzzle");
}
}
class NastyWeapon implements Obstacle {
@Override
public void action() {
System.out.println("NastyWeapon");
}
}
// The Abstract Factory:
class GameElementFactory {
Supplier<Player> player;
Supplier<Obstacle> obstacle;
}
// Concrete factories:
class KittiesAndPuzzles extends GameElementFactory {
KittiesAndPuzzles() {
player = Kitty::new;
obstacle = Puzzle::new;
}
}
class KillAndDismember extends GameElementFactory {
KillAndDismember() {
player = KungFuGuy::new;
obstacle = NastyWeapon::new;
}
}
public class GameEnvironment {
private Player p;
private Obstacle ob;
public GameEnvironment(GameElementFactory factory) {
p = factory.player.get();
ob = factory.obstacle.get();
}
public void play() {
p.interactWith(ob);
}
public static void main(String[] args) {
GameElementFactory kp = new KittiesAndPuzzles(), kd = new KillAndDismember();
GameEnvironment g1 = new GameEnvironment(kp), g2 = new GameEnvironment(kd);
g1.play();
g2.play();
}
}
```
输出结果:
```java
Kitty has encountered a Puzzle
KungFuGuy now battles a NastyWeapon
```
在这种环境中,**Player**对象与**Obstacle**对象进行交互,但是根据你所玩游戏的类型,存在不同类型的玩家和障碍物。 你可以通过选择特定的**GameElementFactory**来确定游戏的类型,然后**GameEnvironment**控制游戏的设置和玩法。 在此示例中,设置和玩法非常简单,但是这些活动(初始条件和状态变化)可以决定游戏的大部分结果。 这里,**GameEnvironment**不是为继承而设计的,尽管这样做很有意义。 它还包含“双重调度”和“工厂方法”的示例,稍后将对这两个示例进行说明。
<!-- Function Objects -->
## 函数对象