工厂模式

简单工厂模式

  • 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
  • 在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)
  • 需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程

简单工厂模式UML类图
简单工厂模式UML类图

  • Factory(工厂):核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象
  • Product(抽象类产品):工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象
  • ProductA,ProductB(具体产品):简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。它要实现抽象产品中声明的抽象方法(有关抽象类)
interface Product{
    void method();
}

class ProductA implements Product{
    @Override
    public void method(){
    
    }
}

class ProductB implements Product{
    @Override
    public void method(){
    
    }
}

class Factory{
    public static Product createProduct(String arg){
        if(arg.equals("A")){
            return new ProductA();
        }else if(arg.equals("B")){
            return new ProductB();
        }else{
            System.out.println("其他逻辑");
        }
    }
}

例如,我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会为其提供所需要的披萨。这时候,我这家 披萨店就可以看做工厂(Factory),而生产出来的披萨被成为产品(Product),披萨的名称则被称为参数,工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式

简单工厂模式的优点
  (1)工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责
  (2)客户端无需知道所创建具体产品的类名,只需知道参数即可
  (3)也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

简单工厂模式的缺点
  (1)工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受影响
  (2)使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  (3)系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂,这违背了“开闭原则”。
  (4)简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。

简单工厂模式的适用环境
  (1)工厂类负责创建对的对象比较少,因为不会造成工厂方法中的业务逻辑过于复杂
  (2)客户端只知道传入工厂类的参数,对如何创建对象不关心

工厂方法模式

在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了“开闭原则”。在简单工厂模式中,所有的产品都是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法模式结构

工厂方法模式包含如下角色:
Product:抽象产品
ConcreteProduct:具体产品
Factory:抽象工厂
ConcreteFactory:具体工厂

  • 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
  • 当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好地符合了“开闭原则”。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。工厂方法模式退化后可以演变成简单工厂模式。
interface Factory{
    Product createProduct();
}

class ConcreteFactory implements Factory{
    @Override
    public Product createProduct(){
        return new ConcreteFactory();
    }   
}

interface Product{
    void method();
}

class ConcreteProduct implements Product {
    @Override
    public void method{
        ...
    }
}

模式实例与解析

日志记录器
某系统日志记录器要求支持多种日志记录方式,如文件记录、数据库记录等,且用户可以根据要求动态选择日志记录方式,现使用工厂方法模式设计该系统。

工厂方法模式实例

class Client{
    LogFactory fileLogFactory = new FileLogFactory();
    Log fileLog = fileLogFactory.createLog();
    fileLog.log();
}

interface Log{
    void log();
}

class FileLog implements Log{
    @Override
    public void log(){
        System.out.println("记录文件日志");
    }
}

class DataBaseLog implements Log{
    @Override
    public void log(){
        System.out.println("记录数据库日志");
    }
}

interface LogFactory{
    Log createLog();
}

class FileLogFactory implements LogFactory{
    @Override
    public Log createLog(){
        return new FileLog();
    }
}

class DataBaseLogFactory implements LogFactory{
    @Override
    public Log createLog(){
        return new DataBaseLog();
    }
}

模式优缺点

优点 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。 缺点 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

模式适用环境

一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。