@@ -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