工厂类模式家族之简单工厂模式

在面向对象编程的领域,设计模式给我们提供了适合特定场景的软件设计思想,不过大多设计模式都可以通过一般的设计进行替代,但我们为什么还有费工夫去遵循设计模式呢?这就要说到设计模式的精髓了,我们通过对软件系统进行良好的设计,不但可以提高代码的可重用性,增强系统的可扩展性,给客户提供良好的接口,还可以减少编码过程中因代码组织太乱扩展过程中需要修改旧代码而带来的一连串的错误,降低维护成本。软件设计过程我们应该尽量追求符合软件设计的开闭原则。

开闭原则:在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数)应该对于扩展是开放的,但是对于修改是封闭的”。该特性在产品化的环境中是特别有价值的,在这种环境中我们认为一旦类完成,我们可以对它进行扩展改变其行为,但是不允许修改类。也就是说一个类的实现只应该因错误而修改。--by wiki

工厂模式家族包括三种重要的模式,分别是简单工厂模式,工厂模式以及抽象工厂模式。他们都属于类的创建型模式。创建型模式包括两种,分别是类的创建型模式对象的创建型模式。类的创建型模式通常使用继承关系,将类的创建交由其具体的子类完成,这样就向外界隐藏了如何得到具体类的实现细节,以及这个类的实例是如何被创建或者组织在一起的。;对象创建型模式通常把一个类的创建委托给另一个对象完成,可以根据语境动态地决定生成哪个具体类的实例。

本节我们来讲解工厂模式中的简单工厂模式。主要包括以下几个部分:

  • 简单工厂模式的实质
  • 简单工厂模式的适用性
  • 简单工厂模式的结构
  • 简单工厂模式的参与者
  • 简单工厂模式各成分之间的交互
  • 实际应用
  • 简单工厂模式的优缺点
  • 简单工厂模式实例代码

简单工厂模式的实质

简单工厂模式又称为静态工厂模式。 它的实质是根据客户传递的信息,工厂类通过该信息制造出相应的产品的实例返回给客户。这样我们就做到了客户只是产品的消费者,而真正的创建者是工厂类。 在简单工厂模式中,待被创建的产品通常继承自同一个类。而这个类中包含了具体产品的所有的公共成员和方法。

简单工厂模式的适用性

简单工厂模式将对象的创建和对象本身的业务分离开来,降低了系统的耦合度,当维护期间需要对客户代码或者产品代码进行修改的时候,修改其中之一不会影响另一个。

简单工厂模式的结构

简单工厂模式的参与者

简单工厂模式中一般有以下几个部分:

  • 工厂类:简单工厂模式的核心,它的作用是根据客户提供的信息创建相应的具体产品
  • 抽象产品:所有具体产品的父类,其中主要包含所有具体产品共有的方法或对象
  • 具体产品:工厂类创建的具体实例。

简单工厂模式各成分之间的交互

  • 客户首先创建factory类(一般为单例模式),
  • factory类创建成功后,客户调用其createProduct方法,并传入相关信息,
  • 具体产品实例被创建并返回给client,开始进行使用concreteProduct

简单工厂模式的实际应用

在实际开发中使用简单工厂模式中,我们可以进行变通的使用。

在实际情况种可能会出现比较复杂的抽象产品和和具体产品之间的关系,这个时候我们依然也可以使用抽象工厂模式:

简单工厂模式的优缺点

简单工厂模式的优点: 通过在中间添加一个工厂类,降低产品类和客户代码之间的耦合度;客户在获取产品的时候无需记住所有产品的构造方法,只需要通过同意的工厂类接口进行创建产品,大大提高了效率准确率。

简单工厂模式的缺点:所有的产品实例化的逻辑都在工厂类的一个创建方法中,当需要添加新产品的时候,不得不进行修改factory类,这样就违背了设计的开闭原则(对扩展开放,对修改封闭);另外当产品类别过多的时候,会出现这个函数冗杂的问题,增加维护成本。最重要的是这个工厂类是所有产品的入口,当它不能工作的时候,所有的产品将陷入瘫痪状态。

简单工厂的实例

下面通过形状shape,circle以及rectangle来实现一个简单的简单工厂模式(如有问题,欢迎指正):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//factory.hpp
#ifndef FACTORY_HPP_
#define FACTORY_HPP_
#include "circle.hpp"
#include "rectangle.hpp"
#include <memory>
#include <string>
class Factory {
private:
Factory() {}
Factory(const Factory &) = delete;
Factory &operator=(const Factory &) = delete;
public:
static std::shared_ptr<Factory> m_factory;
static std::shared_ptr<Factory> getInstance();
static std::shared_ptr<Shape> getShape(const std::string &flag);
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//factory.cpp
#include "factory.hpp"
std::shared_ptr<Factory> Factory::m_factory = nullptr;
std::shared_ptr<Factory> Factory::getInstance() {
if (m_factory == nullptr) {
m_factory = std::unique_ptr<Factory>(new Factory());
}
return m_factory;
}
std::shared_ptr<Shape> Factory::getShape(const std::string &flag) {
if (0 == flag.compare("circle")) {
return std::shared_ptr<Shape>(new Circle());
} else if (0 == flag.compare("rectangle")) {
return std::shared_ptr<Shape>(new Rectangle());
}
}
1
2
3
4
5
6
7
8
9
10
//shape.hpp
#ifndef SHAPE_HPP_
#define SHAPE_HPP_
#include <iostream>
class Shape {
public:
virtual void draw() = 0;
virtual double getArea() = 0;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//circle.hpp
#ifndef CIRCLE_HPP
#define CIRCLE_HPP
#include "shape.hpp"
class Circle : public Shape {
public:
Circle(double t_radius = 0) : m_radius(t_radius) {}
virtual void draw() {
std::cout << "this is a circle, the radius is : " << m_radius << std::endl;
};
virtual double getArea() { return m_radius * m_radius * 3.14; }
private:
double m_radius;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//rectangle.hpp
#ifndef RECTANGLE_HPP_
#define RECTANGLE_HPP_
#include "shape.hpp"
class Rectangle : public Shape {
public:
Rectangle(int a = 0, int b = 0) : m_height(a), m_width(b) {}
virtual void draw() {
std::cout << "this is a rectangle\n height: " << m_height
<< "\nwidth:" << m_width << std::endl;
}
virtual double getArea() { return m_height * m_width; }
private:
int m_height;
int m_width;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
//main.cpp
#include "circle.hpp"
#include "factory.hpp"
#include "rectangle.hpp"
int main() {
std::shared_ptr<Factory> t = Factory::getInstance();
std::shared_ptr<Shape> c = t->getShape("circle");
c->draw();
std::cout << "the area is: " << c->getArea() << std::endl;
std::shared_ptr<Shape> r = t->getShape("rectangle");
r->draw();
std::cout << "the area is :" << r->getArea() << std::endl;
}