【从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

开闭原则"的倾斜性

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

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

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

优缺点以及适用环境

优点

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

缺点

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

适用环境

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

总结一下三者

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

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

相关推荐
资深web全栈开发13 小时前
设计模式之空对象模式 (Null Object Pattern)
设计模式
我爱cope16 小时前
【从0开始学设计模式-2| 面向对象设计原则】
设计模式
资深web全栈开发1 天前
设计模式之访问者模式 (Visitor Pattern)
设计模式·访问者模式
sg_knight1 天前
对象池模式(Object Pool)
python·设计模式·object pool·对象池模式
Yongqiang Cheng1 天前
设计模式:C++ 单例模式 (Singleton in C++)
设计模式·c++ 单例模式
得一录1 天前
AI Agent的主流设计模式之反射模式
人工智能·设计模式
我爱cope1 天前
【从0开始学设计模式-1| 设计模式简介、UML图】
设计模式·uml
※DX3906※1 天前
Java多线程3--设计模式,线程池,定时器
java·开发语言·ide·设计模式·intellij idea
J_liaty1 天前
23种设计模式一中介者模式
设计模式·中介者模式