【从0开始学设计模式-3| 工厂模式】

工厂模式有三种,分别是简单工厂模式、工厂方法模式、抽象工厂模式,下面按顺序说一下:

简单工厂模式

概述

简单工厂模式 (Simple Factory Pattern)又叫做静态工厂方法模式(Static Factory Method Pattern),虽然并不属于GoF的23种设计模式之一,但是是学习其他工厂模式的的基础。

定义 :工厂是一个用于创建其他对象的对象 ------ 从形式上讲,工厂是一个函数或方法,它返回不同原型或类型的对象。

结构

主要包含如下几个角色:

  • Factory(工厂角色) :即工厂类,是简单工厂模式的核心,负责实现创建产品实例的内部逻辑,可以被外界调用,根据提供的参数创建所需的对象,提供了静态方法factoryMethod(),返回类型为抽象产品类型Product
  • Product(抽象产品角色) :是工厂类创建的所有对象的父类,封装了各种产品对象的公有方法,它提高了系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法即可,因为所有的具体产品对象都是其子类
  • ConcreteProduct(具体产品角色) :也就是工厂所创建的所有具体对象,每一个具体产品角色都继承了抽象产品角色,例如图中的ProductOneProductTwo,他们都需要实现在抽象角色Product中声明的抽象方法

案例实现

产金币 or 铜币:

核心代码如下:

  • 硬币接口类以及具体实现类

    java 复制代码
    public interface Coin {
        /**
         * 定义接口,返回硬币描述
         * @return
         */
        String getDescription();
    }
    java 复制代码
    public class GoldCoin implements Coin{
        @Override
        public String getDescription() {
            return "金币";
        }
    }
    java 复制代码
    public class CopperCoin implements Coin{
    
        @Override
        public String getDescription() {
            return "铜币";
        }
    }
  • 工厂类

    java 复制代码
    public class CoinFactory {
      public static Coin getCoin(int type){
        switch (type){
          case 1:
            return new CopperCoin();
          case 2:
            return new GoldCoin();
          default:
            throw new IllegalArgumentException("没有对应的硬币类型");
        }
      }
    }
  • 客户端

    java 复制代码
    public class Client {
      public static void main(String[] args) {
    
        /**
          * ResourceBundle是Java 提供的一个标准工具类,专门用于读取属性文件(.properties)。
          * 它会自动在项目的 resources 目录(或者类路径 classpath)下寻找对应的文件
          * getBundle("conf"):
          *  会在你的项目中寻找一个名为 conf.properties 的文件
          */
        ResourceBundle conf = ResourceBundle.getBundle("conf");
        Coin coin = CoinFactory.getCoin(Integer.valueOf(conf.getString("sfp")));
        System.out.println("你获得了一枚" + coin.getDescription());
    
      }
    }
  • 配置文件:conf.properties

    properties 复制代码
    sfp=1

当然其实这个案例也可以简化,将Coin类 与 CoinFactory类 合并成一个接口, 这样可以少一层继承,代码省略,感兴趣可以自己写一写

优缺点以及适用环境

主要优点:

  • 工厂类包含了必要的判断逻辑,决定了具体创建哪一个产品实例,客户端不需要直接创建产品对象,仅仅使用即可。实现了对象创建和使用的分离
  • 客户端不需要知道所创建的具体产品类的类名 ,只需要知道所对应的参数即可,这样对于一些复杂的类名,可以减少使用者的记忆量
  • 通过引入配置文件 ,可以不修改任何客户端代码的情况下更换具体产品类,提升了系统灵活性

缺点:

  • 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受影响。
  • 扩展困难,一旦添加新产品就不得不修改工厂类的逻辑,这样会违背开闭原则,例如上面的例子如加一个银币类,那么工厂类里面就需要加上对应的判断代码,而且产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
  • 使用简单工厂模式势必会增加系统中类的个数,增加了系统的复杂度和理解难度

适用场景:

  • 工厂类负责创建的对象比较少,这样不会造成工厂方法中的业务逻辑太过复杂。
  • 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

工厂方法模式

概述

工厂方法模式(Factory Method Pattern) ,继承了简单工厂模式的优点,同时做出了修改以达到符合开闭原则的要求。

工厂方法模式也被称为虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。

定义:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

也就是说,工厂方法模式中不再提供一个工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构

结构

主要包含如下几个角色:

  • Product(抽象产品):它是定义产品的接口,是产品对象的公共父类。
  • ConcreteProduct(具体产品):实现抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
  • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
  • ConcreteFactory(具体工厂):是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

案例实现

核心代码实现:

  • 武器抽象类和具体实现类

    java 复制代码
    public abstract class Weapon {
      /**
         * 亮出武器
         */
      public abstract void showWeapon();
    }
    java 复制代码
    public class OrcWeapon extends Weapon{
      @Override
      public void showWeapon() {
        System.out.println("我是兽人,我用的是狼牙棒");
      }
    }
    java 复制代码
    public class ElfWeapon extends Weapon {
      @Override
      public void showWeapon() {
        System.out.println("我是精灵,我用弓箭");
      }
    }
  • 抽象工厂和具体实现类

    java 复制代码
    public interface Blacksmith {
      /**
         * 打造装备
         * @return 装备
         */
      Weapon makeWeapon();
    }
    java 复制代码
    public class OrcBlacksmith implements Blacksmith {
      @Override
      public Weapon makeWeapon() {
        return new OrcWeapon();
      }
    }
    java 复制代码
    public class ElfBlacksmith implements Blacksmith {
      @Override
      public Weapon makeWeapon() {
        return new ElfWeapon();
      }
    }
  • 客户端

    java 复制代码
    public class Client {
      public static void main(String[] args) throws Exception {
        //加载配置
        ResourceBundle conf = ResourceBundle.getBundle("conf");
        String className = conf.getString("fmp");
        /**
         * 反射:创建工厂
         * JVM 根据 className 这个字符串去类路径中寻找对应的 .class 字节码文件。
         * 在拿到的 Class 对象基础上,调用这个类的无参构造函数创建实例
         */
        Blacksmith blacksmith = (Blacksmith) (Class.forName(className)).newInstance();
        //打造装备
        Weapon weapon = blacksmith.makeWeapon();
        //显示装备
        weapon.showWeapon();
      }
    }
  • 配置文件conf.properties

    properties 复制代码
    fmp=com.zeroone.star.factory.fmp.OrcBlacksmith

优缺点以及适用环境

优点

  • 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名(只需要知道工厂的类名)。简单工厂模式也有类似的特点
  • 基于工厂角色和产品角色的多态性设计 是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类
  • 添加新产品的时候,只需要添加一个具体工厂和具体产品就可以了,无需修改任何代码,满足开闭原则。系统的可扩展性也就变得非常好。

缺点

  • 在添加新产品时,既需要写新的具体产品类,也还要提供对应的具体工厂类,系统中类的个数将成对增加,一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  • 由于需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度

适用环境

  • 客户端不知道它所需要的对象的类。只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
  • 抽象工厂类通过其子类来指定创建哪个对象,利用面向对象的多态性里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

抽象工厂模式

概述

工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,也满足了开闭原则,但是由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,会增加系统的开销。

此时,我们可以考虑将一些相关的产品组成一个"产品族 ",由同一个工厂来统一生产,这就是 抽象工厂模式(Abstract Factory Pattern) 的基本思想。

首先引入两个概念:

  • 产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是手机,其子类有三星、小米、华为,则抽象手机与具体品牌的手机之间构成了一个产品等级结构,抽象手机是父类,而具体品牌的手机是其子类。
  • 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如小米手机位于手机产品等级结构中、小米笔记本位于笔记本产品等级结构中,小米路由位于路由产品等级结构中,这一系列电子产品就构成了一个产品族。

使用 :当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。

定义:抽象工厂模式提供了一种方法来封装一组具有共同主题的单个工厂,而无需指定其具体类。

结构

主要包含如下几个角色:

  • AbstractFactory(抽象工厂) :声明了一组用于创建一组产品 的方法,每一个方法对应一种产品
  • ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
  • AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
  • ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

案例实现

要创建一个王国,我们需要具有共同组织的对象。精灵王国需要一个精灵国王,精灵城堡和精灵军队,而兽人王国需要一个兽人国王,兽人城堡和兽人军队。王国中的对象之间存在依赖关系。

核心代码实现:

  • 关于产品的抽象类跟具体实现类

    java 复制代码
    public interface Army {
        /**
         * 获取描述
         * @return 描述信息
         */
        String getDescription();
    }
    java 复制代码
    public class ElfArmy implements Army {
      @Override
      public String getDescription() {
        return "精灵军队";
      }
    }
    java 复制代码
    public interface Castle {
      /**
         * 获取描述
         * @return 描述信息
         */
      String getDescription();
    }
    java 复制代码
    public class ElfCastle implements Castle {
      @Override
      public String getDescription() {
        return "精灵城堡";
      }
    }
    java 复制代码
    public interface King {
      /**
         * 获取描述
         * @return 描述信息
         */
      String getDescription();
    }
    java 复制代码
    public class ElfKing implements King {
      @Override
      public String getDescription() {
        return "精灵国王";
      }
    }
  • 关于工厂的抽象类跟具体实现类

    java 复制代码
    public interface KingdomFactory {
      Castle creatCastle();
      King createKing();
      Army createArmy();
    }
    java 复制代码
    public class ElfKingdomFactory implements KingdomFactory {
      @Override
      public Castle creatCastle() {
        return new ElfCastle();
      }
    
      @Override
      public King createKing() {
        return new ElfKing();
      }
    
      @Override
      public Army createArmy() {
        return new ElfArmy();
      }
    }

兽人部落的写法是一样的。这里省略不写了。

  • 客户端

    java 复制代码
    public class Client {
      public static void main(String[] args) throws Exception {
        //加载配置
        ResourceBundle conf = ResourceBundle.getBundle("conf");
        String className = conf.getString("afp");
        //创建工厂
        KingdomFactory factory = (KingdomFactory) Class.forName(className).newInstance();
        //创建王国
        System.out.println("建造了一座" + factory.creatCastle().getDescription());
        System.out.println("推选了一位" + factory.createKing().getDescription());
        System.out.println("组建了一支" + factory.createArmy().getDescription());
      }
    }
  • 配置文件

    properties 复制代码
    afp=com.zeroone.star.factory.afp.impl.ElfKingdomFactory

开闭原则"的倾斜性

在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦 ,抽象工厂模式的这种性质称为 "开闭原则"的倾斜性

对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

  • 增加产品族 :对于增加新的产品族,抽象工厂模式很好地支持了"开闭原则",只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。
  • 增加新的产品等级结构 :对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了"开闭原则"

优缺点以及适用环境

优点

  • 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  • 当一个产品族中的多个对象被设计成一起工作 时,它能够保证客户端始终只使用同一个产品族中的对象
  • 增加新的产品族 很方便,无须修改已有系统,符合"开闭原则"

缺点

  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了"开闭原则"

适用环境

  • 用户无须关心对象的创建过程,将对象的创建和使用解耦。其他工厂类也是这样的
  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
  • 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。

总结一下三者

  • 如果产品的种类很少,而且几乎不会增加,可以使用简单工厂模式
  • 如果同一类产品需要增加很多实现,系统要求有可扩展性,那么可以直接使用工厂方法模式
  • 如果存在多个产品等级结构 ,且这些产品必须成套使用 。那么可以直接使用抽象工厂模式

工厂模式还是比较简单的,如果这篇文章对你有帮助,欢迎点赞、评论、关注、收藏。你们的支持是我前进的动力!

相关推荐
海特伟业1 天前
隧道调频广播覆盖-隧道调频广播无线覆盖系统建设要点、难点分析与解决应对
运维·设计模式
sg_knight1 天前
设计模式实战:享元模式(Flyweight)
python·设计模式·享元模式·flyweight
Swift社区2 天前
AI 时代,ArkUI 的设计模式会改变吗?
人工智能·设计模式
数据中穿行2 天前
访问者设计模式全方位深度解析
设计模式
宁雨桥2 天前
前端设计模式面试题大全
前端·设计模式
数据中穿行2 天前
迭代器设计模式全方位深度解析
设计模式
数据中穿行2 天前
观察者设计模式全方位深度解析
设计模式
程序员Terry2 天前
别老写重复代码了!模版方法模式一次讲透
java·设计模式
数据中穿行2 天前
建造者模式全方位深度解析
设计模式