设计模式第3讲——工厂模式(Factory)

一、什么是工厂模式 工厂模式属于创建型设计模式,它用于解耦对象的创建和使用。通常情况下,我们创建对象时需要使用new操作符 ,但是使用new操作符创建对象会使代码具有耦合性 。工厂模式通过提供一个公共的接口,使得我们可以在不暴露对象创建逻辑的情况下 创建对象。 二、工厂分类 工厂模式分为三种类型:简单工厂

一、什么是工厂模式

工厂模式属于创建型设计模式,它用于解耦对象的创建和使用。通常情况下,我们创建对象时需要使用new操作符,但是使用new操作符创建对象会使代码具有耦合性。工厂模式通过提供一个公共的接口,使得我们可以在不暴露对象创建逻辑的情况下创建对象。

二、工厂分类

工厂模式分为三种类型:简单工厂方法工厂抽象工厂,其本质就是对获取对象过程的抽象。

三、应用场景

3.1 生活场景

  • 你需要一辆汽车,可以直接从工厂里提货,而不用关心它具体是怎么实现的
  • Hibernate换数据库只需要换方言和驱动就可以

3.2 java场景

  • BeanFactory:它是Spring IoC容器的核心接口,通过读取配置文件或注解来创建Bean实例,并将它们注入到其他对象中。BeanFactory使用了工厂模式来隐藏具体的对象实例化过程,客户端只需要通过接口获取Bean对象,而不需要关心具体的实例化细节。
  • FactoryBean:是一种更高级别的工厂模式实现,用于创建特定类型的Bean对象。与普通的BeanFactory不同,FactoryBean的getObject()方法可以返回任意类型的对象实例,并且可以通过配置方式创建和管理实例。
  • Executors:提供了一系列的工厂方法来创建线程池(ThreadPoolExecutor)及其相关组件。
  • Charset:提供了一系列的静态工厂方法,如forName()、availableCharsets()等,用于创建Charset实例。

四、 工厂模式实现

4.0 代码结构

4.1 传统模式

在介绍工厂模式之前先来看看传统模式,以卖包子为例,如下:

 //简单的制作流程
public BaoZi createBaoZi() {
     BaoZi baozi = new BaoZiImpl();
     //准备材料
     baozi.prepare();
     //制作包子
     baozi.make();
     //蒸包子
     baozi.braise();
     return baozi;
}

包子肯定有很多种类吧,那我们可以直接在上述代码中添加根据包子的种类生成不同类型的对象

/**
* 包子肯定有不同的馅:酸菜、豆沙、猪肉,那么他的材料、售价等方式也不同
* 我们可以直接在上述代码中,添加根据包子的不同种类生成不同的对象。
*/
public BaoZi createBaoZi(String type) {
     BaoZi baoZi = null;
     switch (type){
          case "suancai":
              baoZi=new SuanCaiBaoZi();
              break;
          case "dousha":
              baoZi=new DouShaBaoZi();
              break;
          case "pork":
                baoZi=new PorkBaoZi();
                break;
          default:
              throw new IllegalArgumentException("Invalid BaoZi Type");
      }
      //准备材料
      baoZi.prepare();
      //制作包子
      baoZi.make();
      //蒸包子
      baoZi.braise();
      return baoZi;
}

此时的类图如下,让调用者去创建包子对象。

Test:

  //1.传统模式
    @Test
    void traditonal(){
        SaleBaoZi saleBaoZi=new SaleBaoZi();
        //以猪肉包为例
        saleBaoZi.createBaoZi("pork");
    }

4.2 简单工厂模式

简单工程根据客户端的需求创建具体的实例,这种模式对调用者隐藏了实例创建的过程,也使得创建过程更加容易维护。

还是以卖包子为例,简单工厂模式实现如下:

/**
 * 2.简单工厂方法:希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节
 */
public class SimpleFactory {

    public static BaoZi createBaoZi(String type){

        BaoZi baoZi=null;
        switch (type){
            case "suancai":
                baoZi=new SuanCaiBaoZi("酸菜包");
                break;
            case "dousha":
                baoZi=new DouShaBaoZi("豆沙包");
                break;
            case "pork":
                baoZi=new PorkBaoZi("猪肉包");
            case "beef":
                //老板拓展业务了,新加了一个牛肉包类型的包子,那对于简单工厂模式而言,
                //于是就得修改源代码,那么就违反了ocp原则,假如新增100个?
                baoZi=new BeefBaoZi("牛肉包");
                break;
            default:
                throw new IllegalArgumentException("Invalid BaoZi Type");
        }

        return baoZi;
    }
}

 此时的类图如下:

Test:

  //2.简单工厂模式
    @Test
    void simpleFactory(){
        //以猪肉包为例
        BaoZi pork = SimpleFactory.createBaoZi("pork");
        pork.prepare();
        pork.make();
        pork.braise();
    }

 相比传统模式,从类图上就可以看出来,在sale和baozi中间又加了一层

 通过封装SimpleFactory这个类,我们将sale和baozi进行了解耦合。

4.3 方法工厂模式

        简单工厂模式下,如果老板拓展业务了,加了一个牛肉种类的包子,就得在源码基础上修改,那么这就违背了开闭原则(ocp),即对扩展开放,对修改关闭。于是,为了解决这个问题,就又了工厂方法模式。

工厂方法模式是一种更加抽象的工厂模式,它将工厂的职责抽象为接口,由具体的工厂实现创建具体的对象。工厂方法模式弱化了工厂的实现,使得每个工厂只负责一个产品的创建

抽象工厂MeAbStractFactory:

public interface MeAbstractFactory {
    BaoZi createBaoZi();
}

DouShaFactory:

//豆沙包
public class DouShaFactory implements MeAbstractFactory {
    @Override
    public BaoZi createBaoZi() {
        return new DouShaBaoZi("豆沙包");
    }
}

PorkFactory:

//猪肉包
public class PorkFactory implements MeAbstractFactory {
    @Override
    public BaoZi createBaoZi() {
        return new PorkBaoZi("猪肉包");
    }
}

BeefFactory:

//牛肉包
public class BeefFactory implements MeAbstractFactory {
    @Override
    public BaoZi createBaoZi() {
        return new BeefBaoZi("牛肉包");
    }
}

此时的类图如下:

Test:

  //3.方法工厂模式
    @Test
    void methodFactory(){
        MeAbstractFactory factory=new PorkFactory();
        BaoZi pork = factory.createBaoZi();
        pork.prepare();
        pork.make();
        pork.braise();
    }

之前的SimpleFactory在createBaoZi中直接就new出来了,但在方法工厂中,我们将createBaoZi这个动作推迟到了MeAbStactFactory的子类(XXFactory)中才完成。

好处就是,比如后期要卖个羊肉包,我们直接编写个羊肉包类,然后实现MeAbstractFactory类就,实现它自己的功能,这样完全不用修改原来的代码了,也就解决了违反OCP原则的问题。

4.4 抽象工厂模式

抽象工厂模式是基于工厂方法模式的基础上进行的。在这种模式中,每一个工厂不再只负责一个产品的创建,而是负责一组产品的创建。抽象工厂模式将每个产品组都提取为一个接口,每个工厂都负责一个产品组。

         假如老板的生意做大了,在北京开了个分店,并且不止卖包子,还卖蛋糕,那么该怎么拓展呢,很简单,只需要在抽象工厂类中新增创建蛋糕的抽象方法就行,如下:

AbstractFactory:

public interface AbstractFactory {
    //制作包子
    BaoZi createBaoZi(String type);
    //制作蛋糕
    Cake createCake(String type);
}

BJFactory:


//北京分店
public class BJFactory implements AbstractFactory{
    @Override
    public BaoZi createBaoZi(String type) {
        BaoZi baoZi=null;
        switch (type){
            case "beef":
                baoZi=new BJBeefBao("北京牛肉包");
                break;
            case "pork":
                baoZi=new BJPorkBao("北京猪肉包");
            default:
                break;
        }
        return baoZi;
    }

    @Override
    public Cake createCake(String type) {
        Cake cake=null;
        switch (type){
            case "apple":
                cake=new BJAppleCake("北京苹果蛋糕");
                break;
            case "pear":
                cake=new BJPearCake("北京梨味蛋糕");
            default:
                break;
        }
        return cake;
    }
}

可以看到,抽象工厂仅仅是在工厂方法模式下新增了一些接口,只是工厂方法模式的一个拓展,当抽象工厂只有一个产品体系的话就会退化成工厂模式,所以两者本质上没有太大的区别。

Test:

    //4.抽象工厂模式
    @Test
    void abstractFactory(){
        AbstractFactory factory=new BJFactory();
        Cake apple = factory.createCake("apple");
        BaoZi pork = factory.createBaoZi("pork");

        apple.prepare();
        apple.make();
        apple.bake();
        apple.sale();

        pork.prepare();
        pork.make();
        pork.braise();
        pork.sale();
    }

五、总结

5.1 简单工厂模式

优点:

  • 简单工厂模式实现简单,易于理解和使用;
  • 可以对对象的创建进行集中管理,客户端和具体实现解耦。

缺点:

  • 工厂类负责创建所有对象,如果需要添加新类型的产品,则需要修改工厂类的代码,这违反了开闭原则;
  • 工厂类职责过重,导致单一职责原则被破坏。

适用场景:

  • 工厂类负责创建的对象较少,客户端不需要知道对象的创建过程;
  • 客户端需要根据传递的参数来获取对应的对象。

5.2 方法工厂模式

优点: 

  • 方法工厂模式具有良好的可扩展性,如果需要添加新类型的产品,只需要添加对应的工厂方法即可;
  • 与简单工厂模式相比,方法工厂模式更符合开闭原则和单一职责原则。

缺点:

  • 需要客户端自行选择使用哪个工厂方法,不能像简单工厂模式那样直接传参获取对应对象,因此对客户端的编写有一定要求。

适用场景:

  • 应用中需要创建的对象较少,但是需要具备良好的可扩展性;
  • 客户端可以自行选择创建哪种对象。

5.3 抽象工厂

优点:

  • 抽象工厂模式可以创建多个产品族的产品,这些产品之间有相互依赖或约束关系,有助于保持系统的一致性和稳定性;
  • 客户端与具体产品解耦,通过产品族的方式进行管理。

缺点:

  • 抽象工厂模式增加了系统的抽象性和理解难度,不易于理解和修改;
  • 新增产品族时需要修改工厂接口、工厂实现类和产品类,增加了系统的复杂性。

适用场景:

  • 系统需要一系列相互依赖或约束的产品;
  • 客户端不需要知道具体产品的创建过程,只需要知道产品族即可。

END:更多设计模式的介绍,推荐移步至👉 23种设计模式学习导航(Java完整版)👈

知秋君
上一篇 2024-08-05 20:48
下一篇 2024-08-05 20:12

相关推荐