[大话设计模式C++版] 第15章 就不能不换DB吗? —— 抽象工厂模式

源码可以在这里找到 大话设计模式C++版

最基本的数据访问程序

//User.h
#include <QString>

class User
{
private:
    int m_id;
    QString m_name;

public:
    int getId() {
        return m_id;
    }
    void setId(int id) {
        m_id = id;
    }
    QString getName() {
        return m_name;
    }
    void setName(QString name) {
        m_name = name;
    }
};
//SqlserverUser.h
#pragma execution_character_set("utf-8")
#include "User.h"
#include <memory>
#include <QDebug>
using namespace std;

class SqlserverUser
{
public:
    void insertUser(shared_ptr<User> user) {
        qDebug() << "在SQL Server中给User表增加一条记录";
    }
    shared_ptr<User> getUser(int id) {
        qDebug() << "在SQL Server中根据ID得到User表一条记录";
        return nullptr;
    }
};
//main.cpp
#include "User.h"
#include "SqlserverUser.h"

int main(int argc, char *argv[])
{
    auto user = shared_ptr<User>(new User());
    auto su = shared_ptr<SqlserverUser>(new SqlserverUser());  //与SQL Server耦合
    su->insertUser(user);  //插入用户
    su->getUser(1);  //得到ID为1的用户

    return 0;
}

运行结果:

在SQL Server中给User表增加一条记录
在SQL Server中根据ID得到User表一条记录

问题:这里之所以不能换数据库,原因就在于 auto su = shared_ptr<SqlserverUser>(new SqlserverUser()); 使得su这个对象被框死在 SQL Server 上了。如果这里是灵活的,多态的,那么在执行 su->insertUser(user);su->getUser(1); 时就不用考虑是在用 SQL Server 还是 Access 了。

解决方案:用 工厂方法模式 来封装 new SqlserverUser() 所造成的变化,工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪一个类。

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

在这里插入图片描述

//IUser.h 用户接口类,用于客户端访问,解除与具体数据库访问的耦合
#include "User.h"
#include <memory>
using namespace std;

class IUser
{
public:
    IUser();
    virtual void insertUser(shared_ptr<User> user) = 0;
    virtual shared_ptr<User> getUser(int id) = 0;
};
//SqlserverUser.h 具体数据库类
#pragma execution_character_set("utf-8")
#include "IUser.h"
#include <QDebug>

using namespace std;

class SqlserverUser : public IUser
{
public:
    virtual void insertUser(shared_ptr<User> user) override {
        qDebug() << "在SQL Server中给User表增加一条记录";
    }
    virtual shared_ptr<User> getUser(int id) override {
        qDebug() << "在SQL Server中根据ID得到User表一条记录";
        return nullptr;
    }
};
//AccessUser.h 具体数据库类
#pragma execution_character_set("utf-8")
#include "IUser.h"
#include <QDebug>

using namespace std;

class AccessUser : public IUser
{
public:
    virtual void insertUser(shared_ptr<User> user) override {
        qDebug() << "在Access中给User表增加一条记录";
    }
    virtual shared_ptr<User> getUser(int id) override {
        qDebug() << "在Access中根据ID得到User表一条记录";
        return nullptr;
    }
};
//IFactory.h 抽象工厂接口
#include "IUser.h"

class IFactory
{
public:
    virtual shared_ptr<IUser> createUser() = 0;
};
//SqlServerFactory.h 具体工厂类
#include "IFactory.h"

class SqlServerFactory : public IFactory
{
public:
    virtual shared_ptr<IUser> createUser() override {
        return shared_ptr<IUser>(new SqlserverUser());
    }
};
//AccessFactory.h 具体工厂类
#include "IFactory.h"

class AccessFactory : public IFactory
{
public:
    virtual shared_ptr<IUser> createUser() override {
        return shared_ptr<IUser>(new AccessUser());
    }
};
//main.cpp 客户端代码
#include "User.h"
#include "IFactory.h"
#include "SqlServerFactory.h"

int main(int argc, char *argv[])
{
    shared_ptr<User> user = shared_ptr<User>(new User());
    //若要更改成Access数据库,只需要将本句改成shared_ptr<AccessFactory>(new AccessFactory());
    shared_ptr<IFactory> factory = shared_ptr<SqlServerFactory>(new SqlServerFactory());
    auto iu = factory->createUser();
    iu->insertUser(user);
    iu->getUser(1);

    return 0;
}

现在如果要换数据库,只需更换 SqlServerFactory类 AccessFactory类 就可以了。但是数据库里通常不会只有一个 User表 ,如果要增加部门表 Department表 ,怎么办?

用了抽象工厂模式的数据访问程序

在这里插入图片描述

//Department.h 
#include <QString>

class Department
{
private:
    int m_id;
    QString m_name;

public:
    int getId() {
        return m_id;
    }
    void setId(int id) {
        m_id = id;
    }
    QString getName() {
        return m_name;
    }
    void setName(QString name) {
        m_name = name;
    }
};
//IDepartment.h 部门接口类,用于客户端访问,解除与具体数据库访问的耦合
#include "Department.h"
#include <memory>
using namespace std;

class IDepartment
{
public:
    virtual void insertDepartment(shared_ptr<Department> department) = 0;
    virtual shared_ptr<Department> getDepartment(int id) = 0;
};
//SqlserverDepartment.h
#pragma execution_character_set("utf-8")
#include "IDepartment.h"
#include <QDebug>

class SqlserverDepartment : public IDepartment
{
public:
    virtual void insertDepartment(shared_ptr<Department> department) override {
        qDebug() << "在SQL Server中给Department表增加一条记录";
    }
    virtual shared_ptr<Department> getDepartment(int id) override {
        qDebug() << "在SQL Server中根据ID得到Department表一条记录";
        return nullptr;
    }
};
//AccessDepartment.h
#pragma execution_character_set("utf-8")
#include "IDepartment.h"
#include <QDebug>

class AccessDepartment : public IDepartment
{
public:
    virtual void insertDepartment(shared_ptr<Department> department) override {
        qDebug() << "在Access中给Department表增加一条记录";
    }
    virtual shared_ptr<Department> getDepartment(int id) override {
        qDebug() << "在Access中根据ID得到Department表一条记录";
        return nullptr;
    }
};
//IFactory.h 抽象工厂接口
#include "IUser.h"
#include "IDepartment.h"

class IFactory
{
public:
    virtual shared_ptr<IUser> createUser() = 0;
    virtual shared_ptr<IDepartment> createDepartment() = 0;
};
//SqlServerFactory.h
#include "IFactory.h"

class SqlServerFactory : public IFactory
{
public:
    virtual shared_ptr<IUser> createUser() override {
        return shared_ptr<IUser>(new SqlserverUser());
    }
    virtual shared_ptr<IDepartment> createDepartment() override {
        return shared_ptr<IDepartment>(new SqlserverDepartment());
    }
};
//AccessFactory.h
#include "IFactory.h"

class AccessFactory : public IFactory
{
public:
    AccessFactory();
    virtual shared_ptr<IUser> createUser() override {
        return shared_ptr<IUser>(new AccessUser());
    }
    virtual shared_ptr<IDepartment> createDepartment() override {
        return shared_ptr<AccessDepartment>(new AccessDepartment());
    }
};
//main.cpp 客户端代码
#include "User.h"
#include "Department.h"
#include "IFactory.h"
#include "AccessFactory.h"

int main(int argc, char *argv[])
{
    shared_ptr<User> user = shared_ptr<User>(new User());
    shared_ptr<Department> dept = shared_ptr<Department>(new Department());

    shared_ptr<IFactory> factory = shared_ptr<AccessFactory>(new AccessFactory());

    auto iu = factory->createUser();  //此时已与具体的数据库访问解除了依赖
    iu->insertUser(user);
    iu->getUser(1);

    auto id = factory->createDepartment();  //此时已与具体的数据库访问解除了依赖
    id->insertDepartment(dept);
    id->getDepartment(1);

    return 0;
}

运行结果:

在Access中给User表增加一条记录
在Access中根据ID得到User表一条记录
在Access中给Department表增加一条记录
在Access中根据ID得到Department表一条记录

上面就是抽象工厂模式的代码了,大话设计模式中,还用简单工厂对抽象工厂进行改造,并用 反射 代替简单工厂的 switch-case ,这使用 C#语言 是很容易实现的,C++ 原生不支持 反射 ,因此等以后学会 C++反射 之后再来实现这部分的代码。

Abstract Factory抽象工厂模式 [李建忠C++笔记]

  • 通过“对象创建”模式绕开 new ,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
  • 典型模式
    • Factory Method
    • Abstract Factory
    • Prototype
    • Builder

动机(Motivation)

  • 在软件系统中,经常面临着"一系列相互依赖的对象"创建工作;由于需求的变化,需要创建的对象的具体类型经常变化。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
//数据库访问有关的基类
class IDBConnection {};
class IDBCommand {};
class IDBDataReader {};
class IDBFactory {
public:
	virtual IDBConnection* CreateDBConnection() = 0;
	virtual IDBCommand* CreateDBCommand() = 0;
	virtual IDBDataReader* CreateDBDataReader() = 0;
};

//支持SQL Server
class SqlConnection : public IDBConnection {};
class SqlCommand : public IDBCommand {};
class SqlDataReader : public IDBDataReader {};
class SqlFactory : public IDBFactory {
public:
	virtual IDBConnection* CreateDBConnection() override {
		return new SqlConnection();
	}
	virtual IDBCommand* CreateDBCommand() override {
		return new SqlCommand();
	}
	virtual IDBDataReader* CreateDBDataReader() override {
		return new SqlDataReader();
	}
};

//支持Oracle
class OracleConnection : public IDBConnection {};
class OracleCommand : public IDBCommand {};
class OracleDataReader : public IDBDataReader {};

class EmployeeDA0
{
	IDBFactory* dbFactory;
public:
	vector<EmployeeDA0> GetEmployees() {
		IDBConnection* connection = dbFactory->CreateDBConnection();
		connection->ConnectionString("...");
		
		IDBCommand* command = dbFactory->CreateDBCommand();
		command->CommandText("...");
		command->SetConnect(connection);  //关联性
		
		IDBDataReader* reader = command->ExecuteReader();  //关联性
		while (reader->Read()) {
			
		}
	}
};

模式定义

提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。——《设计模式》GoF

在这里插入图片描述

要点总结

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用 Abstract Factory 模式,这时候使用简单的工厂完全可以。
  • “系列对象”指的是再某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
  • Abstract Factory 模式主要再于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值