常用设计模式-工厂模式

工厂模式介绍

工厂模式分为简单工厂模式工厂方法模式抽象工厂模式三类,其中工厂方法和抽象工厂模式的定义和结构图如下,简单工厂模式在后面的实例中给出

工厂方法模式

工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法是一个类的实例化延迟到其子类。

在这里插入图片描述

抽象工厂模式

抽象工厂模式(Abstract Factory):提供一个创建一系列相关相互依赖对象的接口,而无需指定它们具体的类。

在这里插入图片描述

简单工厂 VS 工厂方法

这里通过实现一个计算器例子来了解简单工厂和工厂方法模式

简单工厂模式的计算器实现

在这里插入图片描述

#include <iostream>
using namespace std;
class CalBase
{
public:
  double num1;
  double num2;
public:
  virtual double cal() = 0;
};

class Add : public CalBase
{
public:
  double cal()
  {
    return num1 + num2;
  }
};

class Sub : public CalBase
{
public:
  double cal()
  {
    return num1 - num2;
  }
};

class Mul : public CalBase
{
public:
  double cal()
  {
    return num1 * num2;
  }
};

class Div :public CalBase
{
public:
  double cal()
  {
    return num1 / num2;
  }
};

class Factory
{
public:
  CalBase* createCal(char type)
  {
    switch (type)
    {
    case '+':
      return new Add();
    case '-':
      return new Sub();
    case '*':
      return new Mul();
    case '/':
      return new Div();
    default:
      cout << "运算符输入错误" << endl;
      break;
    }
    return nullptr;
  }
};
int main()
{
  while (true)
  {
    double num1, num2, result;
    char oper;

    cout << "请输入计算式(运算符为#表示退出):" << endl;
    cin >> num1 >> oper >> num2;

    if (oper == '#') break;

    Factory* ftory = nullptr;
    CalBase* cal = ftory->createCal(oper);
    cal->num1 = num1;
    cal->num2 = num2;

    result = cal->cal();

    cout << "计算结果:" << result << endl;
  }
  return 0;
}

请输入计算式(运算符为#表示退出):
50 / 2
计算结果:25
请输入计算式(运算符为#表示退出):
1 # 1
优点
  • 简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。对于客户端来说,去除了与具体产品的依赖,就像你的计算器让客户端不用管该用哪个类的实例,只需要把+传给工厂。工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实力会实现不同的运算。
缺点
  • 如果要加一个求M的N次方的功能。我们是一定需要给运算工厂类的方法里case的分支条件。但这样会修改原有的类,就等于说我们不但对扩展开放了,对修改也就开放了,这样就违背了开放-封闭原则
工厂方法模式的计算器实现

在这里插入图片描述

#include <iostream>
using namespace std;
class CalBase
{
public:
  double num1;
  double num2;
public:
  virtual double cal() = 0;
};

class Add : public CalBase
{
public:
  double cal()
  {
    return num1 + num2;
  }
};

class Sub : public CalBase
{
public:
  double cal()
  {
    return num1 - num2;
  }
};

class Mul : public CalBase
{

public:
  double cal()
  {
    return num1 * num2;
  }
};

class Div :public CalBase
{
public:
  double cal()
  {
    return num1 / num2;
  }
};

// 抽象工厂接口
class IFactory
{
public:
  virtual ~IFactory() {};
  virtual CalBase* Cal() = 0;
};

// 加法工厂
class AddFactory : public IFactory
{
public:
  CalBase* Cal()
  {
    return new Add();
  }
};

// 减法工厂
class SubFactory : public IFactory
{
public:
  CalBase* Cal()
  {
    return new Sub();
  }
};

// 乘法工厂
class MulFactory : public IFactory
{
public:
  CalBase* Cal()
  {
    return new Mul();
  }
};

// 除法工厂
class DivFactory : public IFactory
{
public:
  CalBase* Cal()
  {
    return new Div();
  }
};
int main()
{
  //IFactory* fct = new AddFactory(); 
  IFactory* fct = new DivFactory();
  CalBase* oper = fct->Cal();
  oper->num1 = 10;
  oper->num2 = 30;
  double result = oper->cal();
  cout << result << endl;
}

0.333333
优点
  • 更符合开-闭原则,新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
  • 符合单一职责原则,每个具体工厂类只负责创建对应的产品
  • 不使用静态工厂方法,可以形成基于继承的等级结构
缺点
  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类,比如当前的+法运算有两种算法,当我们需要采用另一种算法时需要修改原来的+法运算类
  • 一个具体工厂只能创建一种具体产品
总结
  • 当一个类不知道它所需要的对象的类时,在工厂方法模式中,不需要知道具体产品类的类名,只需要知道所对应的工厂即可
  • 当一个类希望通过其子类来指定创建对象时在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
  • 将创建对象的任务委托给多个工厂子类中的某一个,在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

抽象工厂 VS 工厂方法

这里利用数据库访问程序来了解抽象工厂和工厂方法

工厂方法模式的数据库访问程序

在这里插入图片描述

#include <iostream>
using namespace std;
// 用户表抽象接口
class IUser
{
public:
  virtual void insertData() = 0;
  virtual void deleteData() = 0;
};

// MySql型数用户据库表
class MySqlUser:public IUser
{
public:
  void insertData()
  {
    cout << "MySql中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "MySql中成功删除一条数据" << endl;
  }
};

// Oracle型用户数据库表
class OracleUser:public IUser
{
public:
  void insertData()
  {
    cout << "Oracle中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "Oracle中成功删除一条数据" << endl;
  }
};

// 抽象工厂接口
class IFactory
{
public:
  virtual ~IFactory() {}
  virtual IUser* createUser() = 0;
};

// 具体工厂 MySql数据库
class MySqlFactory :public IFactory
{
public:
  IUser* createUser()
  {
    return new MySqlUser();
  }
};

// 具体工厂 Oracle数据库
class OracleFactory :public IFactory
{
public:
  IUser* createUser()
  {
    return new OracleUser();
  }
};
void main01()
{
  //IFactory* fct = new MySqlFactory(); // 如果要换成Oracle直接修改这里了就可以了
  IFactory* fct = new OracleFactory();

  IUser* usertable = fct->createUser(); // 将生产出来的产品交给对应的接口
  usertable->insertData();
  usertable->deleteData();
}

运行结果:

Oracle 的 User 表中成功插入一条数据
Oracle 的 User 表中成功删除一条数据
抽象工厂模式的数据库访问程序

在这里插入图片描述

#include <iostream>
using namespace std;

// 用户表抽象接口
class IUser
{
public:
  virtual void insertData() = 0;
  virtual void deleteData() = 0;
};

// MySql型数用户据库表
class MySqlUser :public IUser
{
public:
  void insertData()
  {
    cout << "MySql 的 User 表中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "MySql 的 User 表中成功删除一条数据" << endl;
  }
};

// Oracle型用户数据库表
class OracleUser :public IUser
{
public:
  void insertData()
  {
    cout << "Oracle 的 User 表中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "Oracle 的 User 表中成功删除一条数据" << endl;
  }
};

// ☆增加部门表抽象接口
class IDepartment
{
public:
  virtual void insertData() = 0;
  virtual void deleteData() = 0;
};

// ☆增加MySql型部门数据库表
class MySqlDepartment:public IDepartment
{
public:
  void insertData()
  {
    cout << "MySql 的 Department 表中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "MySql 的 Department 表中成功删除一条数据" << endl;
  }
};

// ☆增加Oracle型部门数据库表
class OracleDepartment :public IDepartment
{
public:
  void insertData()
  {
    cout << "Oracle 的 Department 表中成功插入一条数据" << endl;
  }
  void deleteData()
  {
    cout << "Oracle 的 Department 表中成功删除一条数据" << endl;
  }
};

// 抽象工厂接口
class IFactory
{
public:
  virtual ~IFactory() {}
  virtual IUser* createUser() = 0;
  
  virtual IDepartment* createDepartment() = 0; // ☆添加接口
};

// 具体工厂 MySql数据库
class MySqlFactory :public IFactory
{
public:
  IUser* createUser()
  {
    return new MySqlUser();
  }

  IDepartment* createDepartment() // ☆创建新表
  {
    return new MySqlDepartment();
  }
};

// 具体工厂 Oracle数据库
class OracleFactory :public IFactory
{
public:
  IUser* createUser()
  {
    return new OracleUser();
  }
  IDepartment* createDepartment() // ☆创建新表
  {
    return new OracleDepartment();
  }
};
void main03()
{
  //IFactory* fct = new MySqlFactory(); 
  IFactory* fct = new OracleFactory(); // 负责数据库的切换 这里是唯一可变处

  IUser* usertable = fct->createUser(); // 将生产出来的产品交给对应的接口
  usertable->insertData();
  usertable->deleteData();

  IDepartment* depart = fct->createDepartment(); 
  depart->insertData();
  depart->deleteData();
}

运行结果:

Oracle 的 User 表中成功插入一条数据
Oracle 的 User 表中成功删除一条数据

Oracle 的 Department 表中成功插入一条数据
Oracle 的 Department 表中成功删除一条数据

拓展:

这里的抽象工厂模式仍然会存在一些不足,比如:如果我们的客户端程序类不止一个,有很多地方都在使用IUser或者IDepartment,而这样的设计,其实每一个类的开始都需要声明IFactory * fct = new MySqlFactory(),但是如果我有100个调用数据库访问的类,那岂不是要改很多地方,那怎么办呢?

答案是我们可以利用简单工厂来改进抽象工厂,单这不是最优的做法,因为简单工厂会破坏开闭原则。最优的做法是增加反射机制

优点:
  • 抽象工厂的最大好处是易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易。他只需要改变具体工厂即可使用不同的产品配置。
  • 能够从多个产品族的多个产品中,简洁的获取想要的具体产品。解决了工厂模式中的不符合开闭原则的问题(增加新的产品时候,不修改工厂,而是增加工厂)。
  • 他让具体的创建实例过程与客户端分离,客户端是通过他的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现客户端代码中。
缺点
  • 产品族(比如这里的user和department)扩展比较困难,要增加一个系列的某一产品(再增加一个project表),不仅要增加具体的产品类,还要增加对应的工厂类(或者修改对应产品族的工厂类)。
应用场景

解决两个维度的组合产品的构造问题,取其中一个维度作为产品族,另外一个维度作为产品族中具体的多个产品。

抽象工厂和工厂方法的区别

工厂方法:

  • 一个抽象产品类,可以派生出多个具体产品类。
    • 一个抽象工厂类,可以派生出多个具体工厂类。
      • 每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂:

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类可以创建多个具体产品类的实例。

总结

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

文章图片取至《大话设计模式》

文章制作不易,如果本文对你有帮助的话就点个赞呗,多谢啦↓↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity是一款非常流行的游戏开发引擎,它提供了许多常用设计模式,以帮助开发者更好地组织和管理游戏代码。下面是Unity中常用的几种设计模式: 1. 单例模式:Unity中的许多管理类或者工具类通常只需要一个实例,使用单例模式可以确保只有一个实例被创建,并且全局可访问。 2. 观察者模式:游戏中有很多时候需要监听某些事件的发生,并根据事件触发相应的操作。观察者模式可以实现一个事件的发布和订阅机制,方便不同组件之间进行通信。 3. 工厂模式:在游戏中,有时候需要根据不同的条件来创建不同的对象。使用工厂模式可以将对象的创建从具体类中解耦出来,使得代码更加可维护和扩展。 4. 策略模式:游戏中有很多时候需要根据不同的情况来执行不同的算法或者逻辑。使用策略模式可以将不同的算法封装成不同的策略类,然后在运行时根据需要动态选择不同的策略。 5. 组件模式:Unity中的游戏对象是由不同的组件组成的,每个组件负责不同的功能。使用组件模式可以将游戏对象设计成一个组件的集合,方便开发者对游戏对象进行组装和拆解。 以上是Unity中常用的几种设计模式,它们可以帮助开发者更好地组织和管理代码,提高代码的可读性、可维护性和扩展性。实际开发中,根据具体的需求,我们可以灵活地选择和组合使用这些设计模式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值