23招做项目——工厂模式:工厂方法和抽象工厂

本文介绍了工厂模式,包括简单工厂、工厂方法和抽象工厂。通过实例解释了工厂模式的作用,即解决接口无法直接new的问题,降低耦合度。文章讨论了为什么需要工厂模式而非直接使用构造函数,并探讨了各种工厂模式的优缺点及其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在网上的大多数资料里面,将工厂模式分为:简单工厂、工厂方法、抽象工厂,他们之间的联系以电子厂为例)如下:
在这里插入图片描述
那么该文章首先讲解工厂模式是什么,为什么需要工厂模式,最后再对三种模式进行示例。该文章是23招做项目的第一篇文章,参考文献和其他设计模式的讲解看专栏,现在2020.8.06正在逐步更新。

1. 工厂模式是什么

工厂模式的主要应用是在工具包和框架。工厂模式要解决的问题是:解决接口没法new的问题,希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节实质是new的一个替代品,任何你需要创建对象,而又不想指明它的具体class的时候都可以用

在平时编程中,构建对象最常用的方法就是new一个对象,这种new对象方法属于一种硬编码,在比较小的项目里面是没有什么毛病的,但是在大的项目里面会出现一个问题。每new一个对象,相当于调用者多知道一个类,增加了类与类之间的联系,不利于程序的松耦合,容易搞成屎山。其实构建过程可以被封装起来,工厂模式便是封装对象的设计模式

打个比方,直接new一个对象相当于我们做肯德基的新奥尔良汉堡的时候,我们不仅要知道鸡排是调味道的\还要知道怎么样揉面包才使得面包比较松软,但是有了新奥尔良汉堡工厂就不一样了,管他什么三七二十一,有现成的面包和调好味道的鸡扒,将鸡扒的烤好塞进去就完事了。

以做汉堡为例:

//C++,这是汉堡工厂
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class HumbegerFactory
{
public:
    Humbeger * createHumbeger(string type){
        switch(type){
            case "鳕鱼汉堡": return new CodBurger();
            case "牛肉汉堡": return new BeefBurger();
    }
};

//C++,这是调用者
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class User{
private:
    void eat(){
        HumbegerFactory humbegerFactory = new HumbegerFactory();
        Humbeger codBurger = humbegerFactory.createHumbeger("鳕鱼汉堡");
        Humbeger beefBurger = humbegerFactory.createHumbeger("牛肉汉堡");
        codBurger.eat();
        beefBurger.eat();
    }
};

事实上,将构建过程封装的好处不仅可以降低耦合,如果产品的构造十分复杂,使用工厂模式可以减少代码重复。比如,生产一个新奥尔良汉堡需要面包、鸡扒时,可以将汉堡工厂修改如下:

//C++,这是汉堡工厂的2.0版本
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class HumbegerFactory
{
public:
    Humbeger * createHumbeger(string type){
        switch(type){
            case "鳕鱼汉堡": 
            
                //修改部分
                Breed codBurgerBreed = new CodBurgerBreed();
                
                return new CodBurger();
            case "牛肉汉堡": return new BeefBurger();
    }
};

不太懂耦合概念的小伙伴,可以看一下这个杨博的回答讲的非常好程序设计经常提到的解耦,到底是要解除什么之间的耦合?

而调用者的代码则完全不需要改变,而且调用者不需要在每次做汉堡的时候,自己去揉面包、调鸡扒的味道,搞了半天才弄出个汉堡。面包揉的方法再复杂,也是工厂的事情,与我无关,这就是封装的好处。而且,万一有一天广东的汉堡厂要去提供鸡扒给湖南的肯德基,那就直接在工厂里面加点辣就🆗了,湖南的门店就不用特意加拉。
在这里插入图片描述
其实在不知不觉间已经完成了简单工厂的代码,在开始三个模式的论述,我想要解决一个问题:为什么工厂方法不用构造函数?

2. 既然有了构造函数,我为什么要折腾这么多事情,我直接一个东西全部构造了不就完事了吗

注意该段观点来自于工厂模式(factory Method)的本质是什么?为什么引入工厂模式?大佬:高宽宽的回答

既然有了构造函数,我为什么要折腾这么多事情,我直接一个东西全部构造了不就完事了吗。为了解答这个问题,首先解释一下构造函数是干嘛地。

对于C语言而言,创建对象资源是这样的(该程序中指定了p的内存大小):

//c内存分配示例
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
     char * a=NULL;
     a=(char *)malloc(sizeof(char));
     if(a == NULL){
         printf("false");
         return -1;
     }

     printf("%d\n",*a);           
     free(a);  
     printf("%d\n",*a);           
     
     return 0;
}

面向对象的程序语言出现之前,创建一个对象是很繁琐的东西,要先分配内存,做类型转化,还要初始化,然后才能使用,一不小心还会忘了分配内存的释放(这一点我相信大部分大一学生在学C语言,经历过谭老头的坑)。

因此,程序语言面向对象之后,构造函数被发明出来,将分配内存与初始化合并在一起,例如:

//c++构造函数示例,这个图算法看之前
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class AdjMatrixUndirNetwork                 //顶点为字符型
{
public:
	AdjMatrixUndirNetwork(int vertexMaxNum = DEFAULT_SIZE, int infinite = DEFAULT_INFINITY);  
	AdjMatrixUndirNetwork(char* vexs, int vertexNum, int vertexMaxNum = DEFAULT_SIZE, int infinite = DEFAULT_INFINITY);
	~AdjMatrixUndirNetwork();
	//……
private:
	char* vertexes;             
	……
};

AdjMatrixUndirNetwork::AdjMatrixUndirNetwork(int vertexMaxNum, int infinite)
{
	vexMaxNum = vertexMaxNum;            
	……
}
AdjMatrixUndirNetwork::AdjMatrixUndirNetwork(char* vexs, int vertexNum, int vertexMaxNum, int infinite)
{
	……
}

这样做确实没什么毛病,你像做个图算法地示例这样做绝对是最优解,因为这些项目非常简单,也就是只有几个参数。但是,整个构造函数所能够完成的工作从一个复杂的项目(几千万行代码的那种)还是太过初级了。因此复杂的创建逻辑是需要写代码来控制,当你有任何复杂的创建对象过程时,你都需要写一个某种createXXX的函数来实现,其实哪怕不是对象,而是任何资源。

//类似这样子
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class User{
private:
    void eat(){
        HumbegerFactory humbegerFactory = new HumbegerFactory();
        Humbeger codBurger = humbegerFactory.createHumbeger("鳕鱼汉堡");
        Humbeger beefBurger = humbegerFactory.createHumbeger("牛肉汉堡");
        codBurger.eat();
        beefBurger.eat();
    }
};

等一下,究竟什么是比较复杂的创建逻辑呢?例如
1.知道怎么创建一个对象,但是无法把控创建的时机。你需要把“如何创建”的代码塞给“负责什么时候创建”的代码。后者在适当的时机,就回调创建的函数
2."构造函数里不要抛出异常"但问题是,业务要求必须在这里抛一个异常怎么办?,createHumbger解决这个问题。

3. 工厂模式——一场面向过程与面向对象之间的较量

不具体叙述看下面链接。
在这里插入图片描述
来源于leetcode的文章:一场「面向对象」与 「面向过程」 的较量

3.简单工厂

简单工厂就是一台机器专门负责制造螺丝钉,之前构建过的简单工厂模式的代码了:

//C++,这是汉堡工厂的2.0版本
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class HumbegerFactory
{
public:
    Humbeger * createHumbeger(string type){
        switch(type){
            case "鳕鱼汉堡": 
            
                //修改部分
                Breed codBurgerBreed = new CodBurgerBreed();
                
                return new CodBurger();
            case "牛肉汉堡": return new BeefBurger();
    }
};

简单工厂作为工厂方法的一个特例,就是让一个工厂类承担起构建所有对象的职责,调用者需要一些什么,我就给他一些什么。这里不打算画出UML(不太标准,因为标准的在线画里面要钱),一笔带过,再在工厂方法里面进行详细诉述。

弊端在于:

  1. 如果需要生产的产品过多,此模式会导致工厂类边的超级大,一点出现问题,他会有许多引起修改的原因,违背了单一职责原则。如果要求螺丝的大小都不一样,一直改,有一台机器忙不过来
  2. 当要生产新的产品时,必须在工厂类中添加新的分支。本身用工厂模式就是不想理里面到底是什么东西,烦。违背了开闭原则。

4.工厂方法

为了解决简单工厂模式的两个弊端,工厂方法规定每个产品都有一个专属于自己的机器,工厂方法更多的像一个流水线。意图是定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到子类,以上面做汉堡为例子:

//C++,工厂方法模式的示例,这是工厂部分
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class CodBurger{
public:
    Humburger * create(){
        return new Cod();
    }
};

class BeefBurger{
public:
    Humburger * create(){
        return new Beef();
    }
};


//这是调用者部分
class User{
private:
    void eat(){
        CodBurger codBurger = new CodBurger();
        Humburger cod = codBurger.create();
        BeefBurger beefBurger = new BeefBurger();
        Humburger beef = beefBurger.create();
        codBurger.eat();
        beefBurger.eat();
    }
};

不是,我这样和直接new有什么区别,有多少种汉堡就需要知道几个工厂类,耦合度完全没有降低,甚至增加了代码量。请注意,工厂模式的运用场景是——希望解决的是比较复杂的创建过程,这一点在析构函数那里解释了。这里的例子比较简单,可能看不出来,但是当汉堡比较复杂,例如:

//鳕鱼汉堡比较复杂的示例
//作者:阿伟加油鸭,首发于优快云,禁止转载,原文网址:https://blog.youkuaiyun.com/qq_45877524/article/details/107778702
class CodBurger{
public:
    Humburger * create(){
       PickledCucumbers pickledCucumbers = new PickledCucumbers();  //酸黄瓜
       Salad salad = new Salad();                                   //沙拉酱
       Sesame sesame = new Sesame();                                //芝麻
       Breed breed = new Breed();                                   //面包
       return new Cod(pickledCucumbers,salad,sesame,breed)
    }
};

其中沙拉酱之类的基本上所有的汉堡都有的东西,我完全可以直接用一个东西工厂类把这个打包起来,最后在根据各自不一样的的东西,比如鳕鱼条进行组装汉堡,而调用者所要作仅仅是改个夹的东西,其他都不用理,这不就是减少了代码量和耦合了吗。

工厂方法模式的优点:‘

  1. 用工厂方法在一个类的内部创建对象通常比直接创建对象灵活。Factory Method给子类一个钩子以提供对象的扩展版本
  2. 当需要生产新的东西时,无需改变原有的工厂,只需要增加新的工厂即可。保持了面向对象的可扩展性,符合开闭原则

在这里插入图片描述

单词意思
Product定义工厂方法所创建的对象的接口
ConcreteProduct实现Product
Creator声明工厂方法,该方法返回一个Product类型的对象
ConcreteCreator重新定义工厂方法以返回一个ConcreteProduct

C++代码模板示例,其他语言可以看这个链接:最下面就是

//工厂
class Creator {
public:
	virtual Product* CreateProduct() = 0;
};

template<class TheProduct>
class StandardCreator :public Creator {
public:
	virtual Product* CreateProduct();
};

//用户
template <class TheProduct>
Product* StandardCreator<TheProduct>::CreateProduct() {
	return new TheProduct;
}

class MyProduct::public Product {
public:
	MyProduct();
	//……
};

StandardCreator<MyProduct> myCreator;

5.抽象工厂

抽象工厂作为工厂方法的2.0版本,更进一步把整个工厂作为一个接口(不理会一条流水线的生产结果,直接着眼于整个系列,在C++里面采用了继承的方法,看下面示例就知道了。
在这里插入图片描述

名称解释
AbstractFactory声明一个创建抽象产品对象的操作接口
ConcreteFactory实现创建具体产品对象的操作
AbstractProduct为一类产品对象声明一个接口
ConcreteProduct定义一个将被相应的具体工厂创建的对象
Client仅仅使用由AbstractFactory和AbstractProduct类声明的接口

C++示例如下,其他语言在该链接的最后面

#include <iostream.h>

class Shape {
  public:
    Shape() {
      id_ = total_++;
    }
    virtual void draw() = 0;
  protected:
    int id_;
    static int total_;
};
int Shape::total_ = 0;

class Circle : public Shape {
  public:
    void draw() {
      cout << "circle " << id_ << ": draw" << endl;
    }
};
class Square : public Shape {
  public:
    void draw() {
      cout << "square " << id_ << ": draw" << endl;
    }
};
class Ellipse : public Shape {
  public:
    void draw() {
      cout << "ellipse " << id_ << ": draw" << endl;
    }
};
class Rectangle : public Shape {
  public:
    void draw() {
      cout << "rectangle " << id_ << ": draw" << endl;
    }
};

class Factory {
  public:
    virtual Shape* createCurvedInstance() = 0;
    virtual Shape* createStraightInstance() = 0;
};

class SimpleShapeFactory : public Factory {
  public:
    Shape* createCurvedInstance() {
      return new Circle;
    }
    Shape* createStraightInstance() {
      return new Square;
    }
};
class RobustShapeFactory : public Factory {
  public:
    Shape* createCurvedInstance()   {
      return new Ellipse;
    }
    Shape* createStraightInstance() {
      return new Rectangle;
    }
};

int main() {
#ifdef SIMPLE
  Factory* factory = new SimpleShapeFactory;
#elif ROBUST
  Factory* factory = new RobustShapeFactory;
#endif
  Shape* shapes[3];

  shapes[0] = factory->createCurvedInstance();   // shapes[0] = new Ellipse;
  shapes[1] = factory->createStraightInstance(); // shapes[1] = new Rectangle;
  shapes[2] = factory->createCurvedInstance();   // shapes[2] = new Ellipse;

  for (int i=0; i < 3; i++) {
    shapes[i]->draw();
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值