002-创建型-01-工厂方法模式(Factory Method)

一、概述

  定义一个创建对象的接口,但让实现这个接口的类来决定实例化那个类,工厂方法让类的实例化推迟到子类中进行。

  工厂方法模式(FACTORY METHOD)同样属于一种常用的对象创建型设计模式,又称为多态工厂模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂,具体工厂,抽象产品,具体产品。

  工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,解决了许多简单工厂模式的问题,完全实现‘开-闭 原则’,让扩展变得简单,让继承变得可行,增加了多态性的体现。其次更复杂的层次结构,可以应用于产品结果复杂的场合。

  工厂方法模式对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不再负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。

1.1、适用场景

  创建对象需要大量重复的代码

  应用层不依赖于产品类实例如何被创建、实现等细节

  一个类通过其子类来指定创建那个对象

1.2、优缺点

优点

  用户只需要关心所需产品对应的工厂,无须关系创建细节
  加入新产品,只需要增加相应的具体产品类和相应的工厂子类即可,符合开闭原则,提高可扩展性
  每个具体工厂类只负责创建对应的产品,符合单一职责原则
缺点

  添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
  一个具体工厂只能创建一种具体产品

1.3、类图角色及其职责

  

    1、抽象工厂(Creator)角色:(FruitFactory)
      是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
    2、具体工厂(Concrete Creator)角色:(AppleFactory、BananaFactory)
      这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。在上图中有两个这样的角色:BulbCreator与TubeCreator。
    3、抽象产品(Product)角色:(Fruit)
      工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。
    4、具体产品(Concrete Product)角色:(Apple、Banana)
      这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

1.4、演进过程

版本一、简单工厂模式问题【违背开闭原则】

  有了002-创建型-01-简单工厂 非GOF设计模式 的引入,但是每次如果我们要增加新的具体工厂时。我们就需要修改已经写好的工厂。如增加一个梨的类

public class Pear implements Fruit {  //具体产品
    @Override
    public void get() {
        System.out.println("采集梨子");
    }
}
View Code

   修改工厂类

public class FruitFactory001 { //工厂
    public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException{
        //不区分大小写
        if(type.equalsIgnoreCase("Apple")){
            return Apple.class.newInstance();
        }else if(type.equalsIgnoreCase("Banana")){
            return Banana.class.newInstance();
        }else if(type.equalsIgnoreCase("Pear")){
            return Pear.class.newInstance();
        }else{
            System.out.println("找不到相应的实体类");
            return null;
        }
    }
}
View Code

  测试

    @Test
    public void base001() throws Exception {
        //实例化Apple,用到了工厂类
        Fruit apple = FruitFactory001.getFruit("Apple");
        //实例化Banana,用到了工厂类
        Fruit banana = FruitFactory001.getFruit("Banana");
        //实例化Pear,用到了工厂类
        Fruit pear = FruitFactory001.getFruit("Pear");

        apple.get();
        banana.get();
        pear.get();
    }
View Code 

违背了开闭原则.

版本二、工厂模式

  根据工厂方法模式我们创建一个抽象工厂

public interface FruitFactory {
    Fruit getFruit();
}

  然后再创建相应的具体工厂实现抽象工厂

public class AppleFactory implements FruitFactory {
    @Override
    public Fruit getFruit() {
        return new Apple();
    }
}
public class BananaFactory implements FruitFactory {
    @Override
    public Fruit getFruit() {
        return new Banana();
    }
}

  测试

    @Test
    public void base002() throws Exception {
        //获得AppleFactory
        FruitFactory af = new AppleFactory();
        //通过AppleFactory来获得Apple实例对象
        Fruit apple = af.getFruit();
        apple.get();

        //获得BananaFactory
        FruitFactory bf = new BananaFactory();
        //通过BananaFactory来获得Apple实例对象
        Fruit banana = bf.getFruit();
        banana.get();
    }

  可以看到,工厂方法模式,如果要新增具体产品,根本不必动原有工厂代码,只要新建一个新增产品的专属工厂,并实现抽象工厂即可。

查看类图

  

  如果需要扩展,不需要改动现有代码,只需要新增具体产品,新建一个新增产品的专属工厂,并实现抽象工厂即可。

1.5、工厂方法模式与简单工厂模式比较

  1、工厂方法模式与简单工厂模式在结构上的不同不是很明显。工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。

  2、工厂方法模式之所以有一个别名叫多态性工厂模式是因为具体工厂类都有共同的接口,或者有共同的抽象父类。

  3、当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了开放-封闭原则。而简单工厂模式在添加新产品对象后,不得不修改工厂方法,扩展性不好。

  4、工厂方法模式退化后可以演变成简单工厂模式。

 

转载于:https://www.cnblogs.com/bjlhx/p/11163591.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值