Skip to content

Commit 2010769

Browse files
crimson-gaosjsdfg
authored andcommitted
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>
1 parent ee50c09 commit 2010769

File tree

1 file changed

+360
-0
lines changed

1 file changed

+360
-0
lines changed

docs/book/25-Patterns.md

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,368 @@ Spinning
425425
<!-- Factories: Encapsulating Object Creation -->
426426
## 工厂模式
427427

428+
当你发现必须将新类型添加到系统中时,合理的第一步是使用多态性为这些新类型创建一个通用接口。这会将你系统中的其余代码与要添加的特定类型的信息分开,使得可以在不改变现有代码的情况下添加新类型……或者看起来如此。起初,在这种设计中,似乎你必须更改代码的唯一地方就是你继承新类型的地方,但这并不是完全正确的。 你仍然必须创建新类型的对象,并且在创建时必须指定要使用的确切构造器。因此,如果创建对象的代码分布在整个应用程序中,那么在添加新类型时,你将遇到相同的问题——你仍然必须追查你代码中新类型碍事的所有地方。恰好是类型的创建碍事,而不是类型的使用(通过多态处理),但是效果是一样的:添加新类型可能会引起问题。
429+
430+
解决方案是强制对象的创建都通过通用工厂进行,而不是允许创建代码在整个系统中传播。 如果你程序中的所有代码都必须执行通过该工厂创建你的一个对象,那么在添加新类时只需要修改工厂即可。
431+
432+
由于每个面向对象的程序都会创建对象,并且很可能会通过添加新类型来扩展程序,因此工厂是最通用的设计模式之一。
433+
434+
举例来说,让我们重新看一下**Shape**系统。 首先,我们需要一个用于所有示例的基本框架。 如果无法创建**Shape**对象,则需要抛出一个合适的异常:
435+
436+
```java
437+
// patterns/shapes/BadShapeCreation.java package patterns.shapes;
438+
public class BadShapeCreation extends RuntimeException {
439+
public BadShapeCreation(String msg) {
440+
super(msg);
441+
}
442+
}
443+
```
444+
445+
接下来,是一个**Shape**基类:
446+
447+
```java
448+
// patterns/shapes/Shape.java
449+
package patterns.shapes;
450+
public class Shape {
451+
private static int counter = 0;
452+
private int id = counter++;
453+
@Override
454+
public String toString(){
455+
return getClass().getSimpleName() + "[" + id + "]";
456+
}
457+
public void draw() {
458+
System.out.println(this + " draw");
459+
}
460+
public void erase() {
461+
System.out.println(this + " erase");
462+
}
463+
}
464+
```
465+
466+
该类自动为每一个**Shape**对象创建一个唯一的`id`
467+
468+
`toString()`使用运行期信息来发现特定的**Shape**子类的名字。
469+
470+
现在我们能很快创建一些**Shape**子类了:
471+
472+
```java
473+
// patterns/shapes/Circle.java
474+
package patterns.shapes;
475+
public class Circle extends Shape {}
476+
```
477+
478+
```java
479+
// patterns/shapes/Square.java
480+
package patterns.shapes;
481+
public class Square extends Shape {}
482+
```
483+
484+
```java
485+
// patterns/shapes/Triangle.java
486+
package patterns.shapes;
487+
public class Triangle extends Shape {}
488+
```
489+
490+
工厂是具有能够创建对象的方法的类。 我们有几个示例版本,因此我们将定义一个接口:
491+
492+
```java
493+
// patterns/shapes/FactoryMethod.java
494+
package patterns.shapes;
495+
public interface FactoryMethod {
496+
Shape create(String type);
497+
}
498+
```
499+
500+
`create()`接收一个参数,这个参数使其决定要创建哪一种**Shape**对象,这里是`String`,但是它其实可以是任何数据集合。对象的初始化数据(这里是字符串)可能来自系统外部。 这个例子将测试工厂:
501+
502+
```java
503+
// patterns/shapes/FactoryTest.java
504+
package patterns.shapes;
505+
import java.util.stream.*;
506+
public class FactoryTest {
507+
public static void test(FactoryMethod factory) {
508+
Stream.of("Circle", "Square", "Triangle",
509+
"Square", "Circle", "Circle", "Triangle")
510+
.map(factory::create)
511+
.peek(Shape::draw)
512+
.peek(Shape::erase)
513+
.count(); // Terminal operation
514+
}
515+
}
516+
```
517+
518+
在主函数`main()`里,要记住除非你在最后使用了一个终结操作,否则**Stream**不会做任何事情。在这里,`count()`的值被丢弃了。
519+
520+
创建工厂的一种方法是显式创建每种类型:
521+
522+
```java
523+
// patterns/ShapeFactory1.java
524+
// A simple static factory method
525+
import java.util.*;
526+
import java.util.stream.*;
527+
import patterns.shapes.*;
528+
public class ShapeFactory1 implements FactoryMethod {
529+
public Shape create(String type) {
530+
switch(type) {
531+
case "Circle": return new Circle();
532+
case "Square": return new Square();
533+
case "Triangle": return new Triangle();
534+
default: throw new BadShapeCreation(type);
535+
}
536+
}
537+
public static void main(String[] args) {
538+
FactoryTest.test(new ShapeFactory1());
539+
}
540+
}
541+
```
542+
543+
输出结果:
544+
545+
```java
546+
Circle[0] draw
547+
Circle[0] erase
548+
Square[1] draw
549+
Square[1] erase
550+
Triangle[2] draw
551+
Triangle[2] erase
552+
Square[3] draw
553+
Square[3] erase
554+
Circle[4] draw
555+
Circle[4] erase
556+
Circle[5] draw
557+
Circle[5] erase
558+
Triangle[6] draw
559+
Triangle[6] erase
560+
```
561+
562+
`create()`现在是添加新类型的Shape时系统中唯一需要更改的其他代码。
563+
564+
### 动态工厂
565+
566+
前面例子中的**静态**`create()`方法强制所有创建操作都集中在一个位置,因此这是添加新类型的**Shape**时唯一必须更改代码的地方。这当然是一个合理的解决方案,因为它把创建对象的过程限制在一个框内。但是,如果你在添加新类时无需修改任何内容,那就太好了。 以下版本使用反射在首次需要时将**Shape**的构造器动态加载到工厂列表中:
567+
568+
```java
569+
// patterns/ShapeFactory2.java
570+
import java.util.*;
571+
import java.lang.reflect.*;
572+
import java.util.stream.*;
573+
import patterns.shapes.*;
574+
public class ShapeFactory2 implements FactoryMethod {
575+
Map<String, Constructor> factories = new HashMap<>();
576+
static Constructor load(String id) {
577+
System.out.println("loading " + id);
578+
try {
579+
return Class.forName("patterns.shapes." + id)
580+
.getConstructor();
581+
} catch(ClassNotFoundException |
582+
NoSuchMethodException e) {
583+
throw new BadShapeCreation(id);
584+
}
585+
}
586+
public Shape create(String id) {
587+
try {
588+
return (Shape)factories
589+
.computeIfAbsent(id, ShapeFactory2::load)
590+
.newInstance();
591+
} catch(InstantiationException |
592+
IllegalAccessException |
593+
InvocationTargetException e) {
594+
throw new BadShapeCreation(id);
595+
}
596+
}
597+
public static void main(String[] args) {
598+
FactoryTest.test(new ShapeFactory2());
599+
}
600+
}
601+
```
602+
603+
输出结果:
604+
605+
```java
606+
loading Circle
607+
Circle[0] draw
608+
Circle[0] erase
609+
loading Square
610+
Square[1] draw
611+
Square[1] erase
612+
loading Triangle
613+
Triangle[2] draw
614+
Triangle[2] erase
615+
Square[3] draw
616+
Square[3] erase
617+
Circle[4] draw
618+
Circle[4] erase
619+
Circle[5] draw
620+
Circle[5] erase
621+
Triangle[6] draw
622+
Triangle[6] erase
623+
```
624+
625+
和之前一样,`create()`方法基于你传递给它的**String**参数生成新的**Shape**s,但是在这里,它是通过在**HashMap**中查找作为键的**String**来实现的。 返回的值是一个构造器,该构造器用于通过调用`newInstance()`创建新的**Shape**对象。
626+
627+
然而,当你开始运行程序时,工厂的`map`为空。`create()`使用`map``computeIfAbsent()`方法来查找构造器(如果该构造器已存在于`map`中)。如果不存在则使用`load()`计算出该构造器,并将其插入到`map`中。 从输出中可以看到,每种特定类型的**Shape**都是在第一次请求时才加载的,然后只需要从`map`中检索它。
628+
629+
### 多态工厂
630+
631+
《设计模式》这本书强调指出,采用“工厂方法”模式的原因是可以从基本工厂中继承出不同类型的工厂。 再次修改示例,使工厂方法位于单独的类中:
632+
633+
```java
634+
// patterns/ShapeFactory3.java
635+
// Polymorphic factory methods
636+
import java.util.*;
637+
import java.util.function.*;
638+
import java.util.stream.*;
639+
import patterns.shapes.*;
640+
interface PolymorphicFactory {
641+
Shape create();
642+
}
643+
class RandomShapes implements Supplier<Shape> {
644+
private final PolymorphicFactory[] factories;
645+
private Random rand = new Random(42);
646+
RandomShapes(PolymorphicFactory... factories){
647+
this.factories = factories;
648+
}
649+
public Shape get() {
650+
return factories[ rand.nextInt(factories.length)].create();
651+
}
652+
}
653+
public class ShapeFactory3 {
654+
public static void main(String[] args) {
655+
RandomShapes rs = new RandomShapes(
656+
Circle::new,
657+
Square::new,
658+
Triangle::new);
659+
Stream.generate(rs)
660+
.limit(6)
661+
.peek(Shape::draw)
662+
.peek(Shape::erase)
663+
.count();
664+
}
665+
}
666+
```
667+
668+
输出结果:
669+
670+
```java
671+
Triangle[0] draw
672+
Triangle[0] erase
673+
Circle[1] draw
674+
Circle[1] erase
675+
Circle[2] draw
676+
Circle[2] erase
677+
Triangle[3] draw
678+
Triangle[3] erase
679+
Circle[4] draw
680+
Circle[4] erase
681+
Square[5] draw
682+
Square[5] erase
683+
```
684+
685+
**RandomShapes**实现了**Supplier \<Shape>**,因此可用于通过`Stream.generate()`创建**Stream**。 它的构造器采用**PolymorphicFactory**对象的可变参数列表。 变量参数列表以数组形式出现,因此列表是以数组形式在内部存储的。`get()`方法随机获取此数组中一个对象的索引,并在结果上调用`create()`以产生新的**Shape**对象。 添加新类型的**Shape**时,**RandomShapes**构造器是唯一需要更改的地方。 请注意,此构造器需要**Supplier \<Shape>**。 我们传递给其**Shape**构造器的方法引用,该引用可满足**Supplier \<Shape>**约定,因为Java 8支持结构一致性。
686+
687+
鉴于**ShapeFactory2.java**可能会抛出异常,使用此方法则没有任何异常——它在编译时完全确定。
688+
689+
### 抽象工厂
690+
691+
抽象工厂模式看起来像我们之前所见的工厂对象,但拥有不是一个工厂方法而是几个工厂方法, 每个工厂方法都会创建不同种类的对象。 这个想法是在创建工厂对象时,你决定如何使用该工厂创建的所有对象。 《设计模式》中提供的示例实现了跨各种图形用户界面(GUI)的可移植性:你创建一个适合你正在使用的GUI的工厂对象,然后从中请求菜单,按钮,滑块等等,它将自动为GUI创建适合该项目版本的组件。 因此,你可以将从一个GUI更改为另一个所产生的影响隔离限制在一处。 作为另一个示例,假设你正在创建一个通用游戏环境来支持不同类型的游戏。 使用抽象工厂看起来就像下文那样:
692+
693+
```java
694+
// patterns/abstractfactory/GameEnvironment.java
695+
// An example of the Abstract Factory pattern
696+
// {java patterns.abstractfactory.GameEnvironment}
697+
package patterns.abstractfactory;
698+
import java.util.function.*;
699+
interface Obstacle {
700+
void action();
701+
}
702+
703+
interface Player {
704+
void interactWith(Obstacle o);
705+
}
706+
707+
class Kitty implements Player {
708+
@Override
709+
public void interactWith(Obstacle ob) {
710+
System.out.print("Kitty has encountered a ");
711+
ob.action();
712+
}
713+
}
714+
715+
class KungFuGuy implements Player {
716+
@Override
717+
public void interactWith(Obstacle ob) {
718+
System.out.print("KungFuGuy now battles a ");
719+
ob.action();
720+
}
721+
}
722+
723+
class Puzzle implements Obstacle {
724+
@Override
725+
public void action() {
726+
System.out.println("Puzzle");
727+
}
728+
}
729+
730+
class NastyWeapon implements Obstacle {
731+
@Override
732+
public void action() {
733+
System.out.println("NastyWeapon");
734+
}
735+
}
736+
737+
// The Abstract Factory:
738+
class GameElementFactory {
739+
Supplier<Player> player;
740+
Supplier<Obstacle> obstacle;
741+
}
742+
743+
// Concrete factories:
744+
class KittiesAndPuzzles extends GameElementFactory {
745+
KittiesAndPuzzles() {
746+
player = Kitty::new;
747+
obstacle = Puzzle::new;
748+
}
749+
}
750+
751+
class KillAndDismember extends GameElementFactory {
752+
KillAndDismember() {
753+
player = KungFuGuy::new;
754+
obstacle = NastyWeapon::new;
755+
}
756+
}
757+
758+
public class GameEnvironment {
759+
private Player p;
760+
private Obstacle ob;
761+
762+
public GameEnvironment(GameElementFactory factory) {
763+
p = factory.player.get();
764+
ob = factory.obstacle.get();
765+
}
766+
public void play() {
767+
p.interactWith(ob);
768+
}
769+
public static void main(String[] args) {
770+
GameElementFactory kp = new KittiesAndPuzzles(), kd = new KillAndDismember();
771+
GameEnvironment g1 = new GameEnvironment(kp), g2 = new GameEnvironment(kd);
772+
g1.play();
773+
g2.play();
774+
}
775+
}
776+
777+
```
778+
779+
输出结果:
780+
781+
```java
782+
Kitty has encountered a Puzzle
783+
KungFuGuy now battles a NastyWeapon
784+
```
785+
786+
在这种环境中,**Player**对象与**Obstacle**对象进行交互,但是根据你所玩游戏的类型,存在不同类型的玩家和障碍物。 你可以通过选择特定的**GameElementFactory**来确定游戏的类型,然后**GameEnvironment**控制游戏的设置和玩法。 在此示例中,设置和玩法非常简单,但是这些活动(初始条件和状态变化)可以决定游戏的大部分结果。 这里,**GameEnvironment**不是为继承而设计的,尽管这样做很有意义。 它还包含“双重调度”和“工厂方法”的示例,稍后将对这两个示例进行说明。
428787

429788
<!-- Function Objects -->
789+
430790
## 函数对象
431791

432792

0 commit comments

Comments
 (0)