浅谈设计模式:工厂模式

工厂模式(Factory Pattern)

在一个工厂中,不同的产品由管理者发配给不同的车间进行生产,当有新的产品需要生产时,则建立一个新的车间,减少不同车间之间的联系。工厂模式就是模拟这样一个工厂生产方式,定义一个工厂类,通过工厂类创建不同子类对象,从紧耦合变为松耦合。

工厂模式主要分为三种:

  1. 简单工厂模式
  2. 工厂方法模式
  3. 抽象工厂模式

1. 简单工厂模式(Single Factory Pattern)

工厂模式是GOF23中的创建型模式,简单工厂模式并不属于23中设计模式中的一种,而是工厂方法模式中的一种简单实现。这里以访问不同数据库的相同数据为例,首先定义一个用户信息表以及一个用户表访问的抽象基类,并且定义用户表访问的虚方法进行多态继承。

//用户表
class User
{
public:
    int m_id;
    std::string m_name;

    User(int id, std::string name) : m_id(id), m_name(name) {}
};

// 不同数据库对用户表的数据访问
// 简单工厂模式
class IUser
{
public:
    virtual void insert(User user) = 0;
    virtual void getUser(int id) = 0;
};

定义不同的子类针对不同的数据库访问进行实例化,并重写继承而来的insert和getUser方法

//MySQL数据库访问User表
class MySQLUser : public IUser
{
public:
    void insert(User user) override
    {
        std::cout << "( MySQL ) insert a data userid: " << user.m_id << " and username: " << user.m_name << std::endl;
    }

    void getUser(int id) override
    {
        std::cout << "( MySQL ) get a data where user id is " << id << std::endl;
    }
};

//SQLServer数据库访问User表
class SQLServerUser : public IUser
{
public:
    void insert(User user) override
    {
        std::cout << "( SQLServer ) insert a data userid: " << user.m_id << " and username: " << user.m_name << std::endl;
    }

    void getUser(int id) override
    {
        std::cout << "( SQLServer ) get a data where user id is " << id << std::endl;
    }
};

//Access数据库访问User表
class AccessUser : public IUser
{
public:
    void insert(User user) override
    {
        std::cout << "( Access ) insert a data userid: " << user.m_id << " and username: " << user.m_name << std::endl;
    }

    void getUser(int id) override
    {
        std::cout << "( Access ) get a data where user id is " << id << std::endl;
    }
};

定义一个数据库工厂类DatabaseFactory对基类指针进行不同数据库子类实例,这里通过定义一个数据库的枚举类型进行实例的分发

enum database_type
{
    MYSQL,
    SQLSERVER,
    ACCESS
};

class DatabaseFactory
{
public:
    static IUser* createUser(int&& type)
    {
        IUser *iu = nullptr;
        switch (type)
        {
        case MYSQL:
            iu = new MySQLUser();
            break;
        case SQLSERVER:
            iu = new SQLServerUser();
            break;
        case ACCESS:
            iu = new AccessUser();
            break;
        default:
            break;
        }
        return iu;
    }
};

用户要使用不同的数据库对用户表进行访问,只需告诉工厂类进行访问的具体数据库类型,而无需关注类内是怎么进行实例化和运算的

int main()
{
    User u(202009, "Mr. Lin");
    IUser *iu = DatabaseFactory::createUser(SQLSERVER);
    iu->insert(u);
    iu->getUser(u.m_id);
}

从上面可以看出,用户通过工厂类实现了不同数据库子类的实例化。当需要增加新的子类,即数据库类型时,需要修改三处地方。一是新增一个子类;二是在枚举表中增加一个新的变量;三是在工厂类中的switch语句中,增加case分支,这修改了类的内部实现,违背了开放—封闭原则。

简单工厂模式与无工厂模式相比,其最大优点在于工厂类包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

简单工厂模式的结构图如下:

浅谈设计模式:工厂模式_第1张图片

 

2. 工厂方法模式(Factory Method Pattern)

虽然简单工厂模式在很大程度上解耦了,但是在增加功能时,还是会违背设计模式的开放—封闭原则,因此我们需要对工厂类进行一次重新的定义修改。这里还是以数据库访问数据为例,抽象基类IUser、子类MySQLUser、SQLServerUser和AccessUser与简单工厂模式中的一样。

首先定义一个工厂方法的抽象基类,该类只提供一个创建具体实例的虚方法

// 工厂方法抽象基类
class DatabaseFactoryMethod
{
public:
    virtual IUser* createUser() = 0;
};

定义MySQL工厂、SQLServer工厂和Access工厂,返回具体的实例对象

//MySQL工厂
class MySQLFactory : public DatabaseFactoryMethod
{
public:
    IUser* createUser() override
    {
        return new MySQLUser();
    }
};

//SQLServer工厂
class SQLServerFactory : public DatabaseFactoryMethod
{
public:
    IUser* createUser() override
    {
        return new SQLServerUser();
    }
};

//Access工厂
class AccessFactory : public DatabaseFactoryMethod
{
public:
    IUser* createUser() override
    {
        return new AccessUser();
    }
};

最后用户直接通过工厂子类实例化工厂方法对象,调用getDatabaseType返回具体的运算类实例,再调用insert和getUser方法访问数据。

int main()
{
    User u(202009, "Mr. Lin");
    DatabaseFactoryMethod *db = new SQLServerFactory();
    IUser* iu = db->createUser();
    iu->insert(u);
    iu->getUser(u.m_id);
}

工厂方法模式与简单工厂模式相比,当需要增加新的数据库时,只需增加一个IUser的子类,以及一个DatabaseFactoryMethod的子类即可,虽然类的数量增加了,但是无需像简单工厂模式中需要修改类的内部实现,从而更大程度地解耦。

从工厂类上可以看出,工厂方法模式定义了一个用于创建对象的接口,让子类决定实例化哪一个类,使得一个类的实例化延迟到了子类。

也即是说,将创建具体实例的方式从只通过一个类来全部实现,变成了通过子类继承基类,重写继承接口的方式来创建。

工厂方法模式的结构图如下:

浅谈设计模式:工厂模式_第2张图片

 

3. 抽象工厂模式(Abstract Factory Pattern)

到现在可以看出工厂方法模式已经很好地诠释了设计模式的主要思想,但是在实际情况中数据库要访问多个表,而工厂方法模式里只针对了一个用户表User,因此出现了抽象工厂模式。

在GOF中,抽象工厂是这样定义的,提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

定义多一个部门表Department,以及一个部门表访问抽象基类IDepartment,并派生不同数据库的子类

class Department
{
public:
    int m_id;
    std::string m_name;

    Department(int id, std::string name) : m_id(id), m_name(name) {}
};

class IDepartment
{
public:
    virtual void insert(Department dep) = 0;
    virtual void getDepartment(int id) = 0;
};

class MySQLDepartment : public IDepartment
{
public:
    void insert(Department dep) override
    {
        std::cout << "( MySQL ) insert a data departmentid: " << dep.m_id << " and departmentname: " << dep.m_name << std::endl;
    }

    void getDepartment(int id) override
    {
        std::cout << "( MySQL ) get a data where department id is " << id << std::endl;
    }
};

class SQLServerDepartment : public IDepartment
{
public:
    void insert(Department dep) override
    {
        std::cout << "( SQLServer ) insert a data departmentid: " << dep.m_id << " and departmentname: " << dep.m_name << std::endl;
    }

    void getDepartment(int id) override
    {
        std::cout << "( SQLServer ) get a data where department id is " << id << std::endl;
    }
};

class AccessDepartment : public IDepartment
{
public:
    void insert(Department dep) override
    {
        std::cout << "( Access ) insert a data departmentid: " << dep.m_id << " and departmentname: " << dep.m_name << std::endl;
    }

    void getDepartment(int id) override
    {
        std::cout << "( Access ) get a data where department id is " << id << std::endl;
    }
};

然后在DatabaseFactoryMethod抽象基类中增加虚方法createDepartment,并派生不同数据库工厂重写createDepartment

class IDatabaseFactory
{
public:
    virtual IUser* createUser() = 0;
    virtual IDepartment* createDepartment() = 0;
};

class MySQLFactory : public IDatabaseFactory
{
public:
    IUser* createUser()
    {
        return new MySQLUser();
    }

    IDepartment* createDepartment()
    {
        return new MySQLDepartment();
    }
};

class SQLServerFactory : public IDatabaseFactory
{
public:
    IUser* createUser()
    {
        return new SQLServerUser();
    }

    IDepartment* createDepartment()
    {
        return new SQLServerDepartment();
    }
};

class AccessFactory : public IDatabaseFactory
{
public:
    IUser* createUser()
    {
        return new AccessUser();
    }

    IDepartment* createDepartment()
    {
        return new AccessDepartment();
    }

};

可以看出在新增表的时候,IDatabaseFactory需要增加新的虚方法,而其子类也需要新增方法并重写,这也违背了开放—封闭原则。

但抽象工厂模式还是有两大优点:

  • 易于交换产品系列,由于具体工厂类在应用中只需在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,只需要改变具体工厂即可使用不同的产品配置;
  • 将具体的创建实例过程与客户端分离,客户端通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

                                                                                                                                                                      ——摘自《大话设计模式》

抽象工厂模式的结构图如下:

 

你可能感兴趣的:(设计模式,设计模式,c++)