抽象工厂这个名字比较难以帮助理解,可以把抽象工厂理解为“品牌工厂”或者“家族工厂”。
我们有一个控制数据库连接和操作的类
class EmployeeDAO{
public:
vector<EmployeeDAO> GetEmployees(){
SqlConnection* connection = new SqlConnection();
connection->ConnectionString("...");
SqlCommand* command = new SqlCommand();
command->CommandText("...");
command->SetConnection(connection);
SqlDataReader* reader = command->ExecuteReader();
while(reader->Read()){
}
}
};
我们使用这个类来连接一个数据库,并使用一些SQL语句来对数据库进行操作。然后使用reader来读取SQL语句得到的数据。
由于可以使用的数据库种类很多,比如针对oracle的数据库,我们可能需要另外再一个类来实现不同的实际操作。
在学习了工厂方法之后,我们可以根据工厂模式将SqlConnection,SqlCommand,SqlDataReader改写成以下模式。
class IDBconnection {
public:
virtual void ConnectionString(string) = 0;
};
class SqlConnection : public IDBconnection {
public:
virtual void ConnectionString(string s) {
//do connection
}
};
class OracleConnection : public IDBconnection {
public:
virtual void ConnectionString(string s) {
//do connection
}
};
class IDBconnectionFactory {
public:
virtual IDBconnection* create() = 0;
};
class SqlConnectionFactory : public IDBconnectionFactory {
IDBconnection* create() {
return new SqlConnection();
}
};
class OracleConnectionFactory : public IDBconnectionFactory {
IDBconnection* create() {
return new OracleConnection();
}
};
class ISqlCommand {
public:
virtual void CommandText(string) = 0;
virtual void SetConnection(IDBconnection*) = 0;
virtual IDataReader* ExecuteReader() = 0;
};
class SqlCommand : public ISqlCommand {
public:
virtual void CommandText(string) {
//...
}
virtual void SetConnection(IDBconnection*) {
//...
}
virtual IDataReader* ExecuteReader() {
return new SqlDataReader();
}
};
class OracleCommand : public ISqlCommand {
public:
virtual void CommandText(string) {
//...
}
virtual void SetConnection(IDBconnection*) {
//...
}
virtual IDataReader* ExecuteReader() {
return new OracleDataReader();
}
};
class ISqlCommandFactory {
public:
virtual ISqlCommand* create() = 0;
};
class SqlCommandFactory : public ISqlCommandFactory {
ISqlCommand* create() {
return new SqlCommand();
}
};
class OracleCommandFactory : public ISqlCommandFactory {
ISqlCommand* create() {
return new OracleCommand();
}
};
class IDataReader {
public:
virtual bool Read() = 0;
};
class SqlDataReader : public IDataReader {
public:
virtual bool Read() {
//...
return true;
}
};
class OracleDataReader : public IDataReader {
public:
virtual bool Read() {
//...
return true;
}
};
class EmployeeDAO {
private:
IDBconnection* connection;
ISqlCommand* command;
IDataReader* reader;
public:
vector<EmployeeDAO> GetEmployees(IDBconnectionFactory* connectionFactory, ISqlCommandFactory* commanFactory) {
connection = connectionFactory->create();
connection->ConnectionString("...");
command = commanFactory->create();
command->CommandText("...");
command->SetConnection(connection);
reader = command->ExecuteReader();
while (reader->Read()) {
}
}
};
我们使用工厂模式在运行时创建EmployeeDAO的connection和command指针。
我们使用单纯的工厂模式来创建不同的connection和command的子类来操作不同的数据库。这表面上看起来已经解决了这个问题。
但是,假如出现了这种情况,在调用GetEmployees时,我们传入的参数是,
GetEmployees(SqlConnectionFactory, OracleCommandFactory)
这样的代码当然可以通过编译,但是我们用Oracle数据库的命令其他的数据库,当然会导致一系列的错误。
class IDBFactory {
public:
virtual IDBconnection* create_connection() = 0;
virtual ISqlCommand* create_command() = 0;
};
class SqlFactory : public IDBFactory {
public:
virtual IDBconnection* create_connection() {
return new SqlConnection();
}
virtual ISqlCommand* create_command() {
return new SqlCommand();
}
};
class OracleFactory : public IDBFactory {
public:
virtual IDBconnection* create_connection() {
return new OracleConnection();
}
virtual ISqlCommand* create_command() {
return new OracleCommand();
}
};
class EmployeeDAO {
private:
IDBconnection* connection;
ISqlCommand* command;
IDataReader* reader;
public:
vector<EmployeeDAO> GetEmployees(IDBFactory* factory) {
connection = factory->create_connection();
connection->ConnectionString("...");
command = factory->create_command();
command->CommandText("...");
command->SetConnection(connection);
reader = command->ExecuteReader();
while (reader->Read()) {
}
}
};
将两个connection和command工厂合并,让它只能生产“同一种类的产品”。这样,我们既降低了耦合,又避免“产品种类”不同导致不能使用的情况。
红色是稳定的,蓝色代表了一种变化,绿色代表了另一种变化。
可以对照上面的代码自己寻找一下图中的类和代码中的类的对应关系。
9.抽象工厂_哔哩哔哩_bilibili这里面,将ConreteFactory1,2其实是按sql和oracle分开的。视频中出现了一点小错误。