工厂模式

作用:<mark>实现创建者和调用者的分离</mark>
分类

  • 简单工厂模式
  • 工厂方法模式
  • 抽象方法模式

遵循的OOP原则

  • 开闭原则:一个软件的实体应当对扩展开放,对修改关闭
  • 依赖倒转原则:要针对接口编程,不要针对实现编程
  • 迪米特法则:只与你直接的朋友通信,避免和陌生人通信

核心本质

  • 实例化不使用new,而使用工厂类的方法
  • 将选择实现类,创建对象统一管理和控制,从而将调用者和实现类解耦。
  1. 传统的工厂模式(模拟生产车子工厂)

车子接口:

/** * 车子接口 */
public interface Car {
   
    void name();
}

五菱宏光:

public class WuLing implements Car {
   
    @Override
    public void name() {
   
        System.out.println("五菱宏光666~");
    }
}

奥迪:

public class AoDi implements Car {
   
    @Override
    public void name() {
   
        System.out.println("奥迪666~");
    }
}

顾客:

public class Consumer {
   
    public static void main(String[] args) {
   
        Car wuling = new WuLing(); //购卖五菱
        Car aodi = new AoDi();      //购卖奥迪

        wuling.name();
        aodi.name();
    }
}

存在的问题

  • 顾客不应该去new🚗,它按理说应该关注买到已有的车
  • 如果此时有新的品牌车被创造,则它必须实现🚗接口,顾客也必须再去new
  • 调用者(顾客),和实现类(品牌车)的耦合度较高,也违反了三个OOP原则

1. 简单工厂模式(静态工厂模式)

将调用者new这个动作交给工厂来操作,解耦了调用者和实现类。
车子接口:

/** * 车子接口 */
public interface Car {
   
    void name();
}

五菱宏光:

public class WuLing implements Car {
   
    @Override
    public void name() {
   
        System.out.println("五菱宏光666~");
    }
}

奥迪:

public class AoDi implements Car {
   
    @Override
    public void name() {
   
        System.out.println("奥迪666~");
    }
}

🚗工厂:

public class SimpleFactory {
   
    private  static  Car car = null;
    public static Car createCar(String name){
   
        if("五菱".equals(name)){
   
            car = new WuLing();
        }else{
   
            car = new AoDi();
        }
        return car;
    }
}

消费者:

public class Consumer {
   
    public static void main(String[] args) {
   
        Car wuling = SimpleFactory.createCar("五菱"); //购买五菱
        Car aodi = SimpleFactory.createCar("奥迪");   //奥迪
        wuling.name();
        aodi.name();
    }
}

此时,将生产车子的new动作交给了SimpleFactory类来做,而不用调用者来直接操作


存在的问题
没有遵循着开闭原则,因为即使不用我们调用者生产车,但如果有新品牌的车,我们还是要修改车工厂的代码,横向扩展比较难。

2. 工厂方法模式

抽取生产车子工厂,为每一个品牌的车创建工厂,可以做到不去修改某一个车工厂的代码,这样做到在增加某一品牌的时候不去修改其它车工厂的代码, 可以提高横向扩展性,但是代码量却增加了不少。

车子接口:

public interface Car {
   
    void name();
}

车子工厂接口:

public interface CarFactory {
   
    public Car getCar();
}

五菱:

public class WuLing implements Car {
   
    @Override
    public void name() {
   
        System.out.println("五菱宏光666~");
    }
}

五菱工厂:

public class WuLingFactory implements CarFactory {
   
    @Override
    public Car getCar() {
   
        return new WuLing();
    }
}

奥迪:

public class AoDi implements Car {
   
    @Override
    public void name() {
   
        System.out.println("奥迪666~");
    }
}

奥迪工厂:

public class AoDiFactory implements CarFactory {
   
    @Override
    public Car getCar() {
   
        return new AoDi();
    }
}

消费者:

public class Consumer {
   
    public static void main(String[] args) {
   
        Car wuling = new WuLingFactory().getCar();
        wuling.name();
        Car aodi = new AoDiFactory().getCar();
        aodi.name();
    }
}

聪明的人都已经发现了,除了代码量增加了许多,结构和管理上的复杂度也增加了不少,但是可以满足横向扩展,就是说,遵循了开闭原则,做到了对扩展开放,对修改关闭。

简单工厂模式和工厂方法的对比

  • 如果需要满足设计原则,应当选用工厂方法
  • 没有设计原则的要求,应尽量选择简单工厂模式,因为简单工厂模式无论从编程难度,代码结构,管理的复杂性来说都是比工厂方法更低

3. 抽象方法模式

定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类,围绕一个超级工厂创建其它工厂,该超级工厂又被称之为工厂的工厂。

适用场景

  • 客户端不依赖产品类实例如何被创建,需要的时候工厂取出来即可,也无需关系创建对象的细节
  • 强调一系列相关的产品对象属于同一产品族,一起使用创建对象需要大量的代码
  • 提供一个产品类的库,所有的产品类以同样的接口出现,从而使得客户端不依赖于具体的实习

如下:

模拟小米和华为工厂有各自的产品族:手机族和路由器族,用抽象工厂的实现方式,实现消费者从工厂中买到手机的细节:

  1. 抽象的抽象接口:
/** * 抽象的抽象工厂:可以生产手机和路由器,包含两个小工厂:小米和华为 */
public interface IProductFactory {
   

    IphoneProduct getPhone();
    IRouterProduct getRouter();
}
  1. 实现抽象的抽象接口:
//华为工厂可以生产手机和路由器
public class HuaweiProductFactory implements IProductFactory {
   

    @Override
    public IphoneProduct getPhone() {
   
        return new HuaweiPhone();
    }

    @Override
    public IRouterProduct getRouter() {
   
        return new HuaweiRouter();
    }
}
//小米工厂可以生产路由器和手机
public class XiaoMiProductFactory  implements IProductFactory{
   
    @Override
    public IphoneProduct getPhone() {
   
        return new XiaoMiPhone();
    }

    @Override
    public IRouterProduct getRouter() {
   
        return new XiaoMiRouter();
    }
}
  1. 产品族的接口及实现类:

路由器:

//路由器产品
public interface IRouterProduct {
   
    void start();
    void stop();
    void openWifi();
}

public class HuaweiRouter implements IRouterProduct {
   
    @Override
    public void start() {
   
        System.out.println("启动华为路由器");
    }

    @Override
    public void stop() {
   
        System.out.println("关闭华为路由器");
    }

    @Override
    public void openWifi() {
   
        System.out.println("启动华为路由器wifi");
    }
}
public class XiaoMiRouter implements IRouterProduct {
   
    @Override
    public void start() {
   
        System.out.println("启动小米路由器");
    }

    @Override
    public void stop() {
   
        System.out.println("关闭小米路由器");
    }

    @Override
    public void openWifi() {
   
        System.out.println("启动小米wifi");
    }
}

手机:

//手机产品
public interface IphoneProduct {
   
    void start();
    void stop();
    void call();
    void sendMsg();
}
public class HuaweiPhone implements IphoneProduct {
   
    @Override
    public void start() {
   
        System.out.println("启动华为手机");
    }

    @Override
    public void stop() {
   
        System.out.println("关闭华为手机");
    }

    @Override
    public void call() {
   
        System.out.println("拨打华为手机");
    }

    @Override
    public void sendMsg() {
   
        System.out.println("华为手机发消息");
    }
}
public class XiaoMiPhone implements IphoneProduct {
   
    @Override
    public void start() {
   
        System.out.println("启动小米手机");
    }

    @Override
    public void stop() {
   
        System.out.println("关闭小米手机");
    }

    @Override
    public void call() {
   
        System.out.println("拨打小米手机");
    }

    @Override
    public void sendMsg() {
   
        System.out.println("小米手机发消息");
    }
}
  1. 消费者:
public class Consumer {
   
    public static void main(String[] args) {
   

        System.out.println("-----------------生产小米系列产品-----------------");
        XiaoMiProductFactory xmf = new XiaoMiProductFactory();
        IphoneProduct xiaomiPhone = xmf.getPhone();
        xiaomiPhone.start();
        xiaomiPhone.call();
        IRouterProduct xiaomiRouter = xmf.getRouter();
        xiaomiRouter.start();
        xiaomiRouter.openWifi();
        System.out.println("-----------------生产华为系列产品-----------------");
        HuaweiProductFactory huaweiFactory = new HuaweiProductFactory();
        IphoneProduct huaweiPhone = huaweiFactory.getPhone();
        huaweiPhone.start();
        huaweiPhone.sendMsg();
        IRouterProduct huaweiRouter = huaweiFactory.getRouter();
        huaweiRouter.start();
        huaweiRouter.openWifi();
    }
}
  1. 运行结果:

抽象工厂模式内部类与接口直接的依赖图:

看着类直接的依赖关系显然头很大,不如看个轻松点的

可见,无论是依赖图还是我画结构图,抽象工厂模式的内部结构依旧十分复杂,而且不易扩展,怎么说呢?倘若,我们要为抽象的抽象工厂增加一个生产笔记本的方法,那么如果可以生产笔记本的厂家也都必须增加一个对应的生产方法,那如果底下有很多厂家都可以生产笔记本,那么每个品牌的工厂就要修改多份,这样也就破坏了开闭原则。

抽象工厂模式的优点
可以将一系列产品统一到一起创建,然后将具体的产品代码隔离,创建具体的产品可以无需关注其细节。
抽象工厂模式的缺点
规定了所有可能被创建的产品集合,在产品簇中扩展新的产品困难,增加了系统的抽象性和理解难度。

4. 小结

  1. 简单工厂模式(静态工厂模式):

虽然某种程度上不符合设计原则(开闭原则),但实际使用最多。

  1. 工厂方法模式

不修改已有类的前提上,通过增加新的工厂类实现扩展性

  1. 抽象工厂模式

不可以增加产品,可以增加产品簇。

应用场景

  • JDK中的Calendar的getInstance方法
  • JDBC中的Connection对象的获取
  • Spring IOC容器创建管理bean对象
  • 反射中Class对象的newInstance方法

参考B站:https://www.bilibili.com/video/BV1mc411h719?t=1413&p=4