设计模式学习总结:(7)工厂方法模式和抽象工厂模式

工厂方法模式应该算是很容易理解的模式,至少从书上看是这样一回事,但是真正深入去理解它一种存在意义却是不容易的(代码量不够-。-)。从语法上看,无非就是把面向对象的多态特性封装到了内部工程类,实现运行时多态。

意图:

定义一个用于创建对象的接口,让子类决定实例化哪个类。Factory Method使一个类的实例化延迟到其子类。

结构图:

 

简单的代码:

class AbProduct
{
public:
    virtual void sayName()=0;
    AbProduct(string name) :_name(name){}
    virtual ~AbProduct(){}
protected:
    string _name;
};

具体产品:

class DBProduct:public AbProduct
{
public:
    DBProduct(string name) :AbProduct(name){}
    ~DBProduct(){}
    void sayName();
private:
     
};

void DBProduct::sayName()
{
    cout << _name << endl;
}
class TextProduct:public AbProduct
{
public:
    TextProduct(string name) :AbProduct(name){}
    ~TextProduct(){}
    void sayName();
private:

};
void TextProduct::sayName()
{
    cout << _name << endl;
}

抽象工厂:

class AbFactory
{
public:
    AbFactory(){}
    virtual ~AbFactory(){}
    virtual AbProduct * creator(string) = 0;
private:

};

具体工厂:

class DbFactory:public AbFactory
{
public:
    
    ~DbFactory(){}
    AbProduct * creator(string name);
private:

};
AbProduct * DbFactory::creator(string name)
{
    return new DBProduct(name);
}
class TextCreator :public AbFactory
{
public:
    
    ~TextCreator(){}
    AbProduct * creator(string name);
};
AbProduct * TextCreator::creator(string name)
{
    return new TextProduct(name);
}

测试:

int main()
{
        AbFactory *f = new DbFactory();
    AbProduct *d = f->creator("小db");
    
    d->sayName();
    delete f;
    delete d;

    return 0;
}

结果:

直接按照结构来比较抽象。

所以我又写了一些代码

class Client
{
public:
    Client(AbFactory * factory) :_factory(factory){}
    ~Client()
    {
        delete _factory;
    }
    void creatSon(string);
    
private:
    AbFactory *_factory;
    
};
void Client::creatSon(string name)
{
    AbProduct * son = _factory->creator(name);
    cout << "create a son" << endl;
    son->sayName();
    delete son;
}

我是在想,对于类似与creatSon这样的方法,需要在一个方法中动态的确定一个对象,而且这个对象应该不可以是唯一的。那么这个时候这种工厂模式的意义相当的凸显。

然后我还写了一点测试代码,之所以写这么无厘头,只是为了说明有这样一种时候,某个对象中每个执行过程需要动态new一个对象,而且每次调用这个执行过程都不应该是同一个对象,后面我写了一段同一个对象的情况。

先看测试代码:

int main()
{
    AbFactory *f = new DbFactory();
    Client dbClint = Client(f);
    dbClint.creatSon("小db");
    dbClint.creatSon("大db");
    Client textClint = Client(new TextFactory());
    textClint.creatSon("小text");
    return 0;
}

结果:

然后在看一个对比的代码:

class Client
{
public:
    Client(AbProduct * product) :_product(product){}
    ~Client()
    {
        delete _product;
    }
    void onlySonSayName();

private:
    AbProduct *_product;

};
void Client::onlySonSayName()
{
    
    _product->sayName();
}

这样也能实现多态,但是这个过程中不涉及到new,一个动态product已经够用了。

好了,接下来学了抽象工厂模式。相比普通工厂方法模式一个一个的new对象,抽象工厂更多的关注一堆对象之间的new,什么意思呢,在有些情况下,多个对象存在某种不可分割的关系,这个时候,用抽象工厂方法把所有的对象创建一次性组合成一个大工厂,这样显得更加的安全可靠(狂拽酷炫吊炸天)。当然,纸上谈兵还是很容易的,看看书上的一些定义吧。

意图:

提供一个创建一系列相关或相互依赖的对象的接口,而无需指定它们具体的类。

结构图:

具体的例子改写李建忠老师的例子吧,觉得挺有代表性的。比如数据库的操作封装,抽象成为最简单的步骤,就是connect,执行command,reader,最后在关闭,这一样的每一个步骤用一个类来表示,同时,数据库有很多种,为了便于后期扩展,所以都需要抽象出每一个类,因为每一次连接都需要new一个具体的对象,因为每一个连接是相互独立的,所以这个时候用工厂方法在合适不过了。

先看基本的代码:

抽象基类:

class DbConnection
{
public:
    virtual void connect(string)=0;
    DbConnection(){}
    virtual ~DbConnection(){} //虚析构函数其实是很有必要的
};
class Execute
{
public:
    Execute()=default;
    virtual ~Execute(){}
    virtual void execute(string sql_str)=0;
private:

};
class Reader
{
public:
    Reader()=defalut;
    virtual ~Reader(){}
    virtual void read()=0;
private:

};

如果需要扩展:

class MysqlConnect :public DbConnection
{
public:
    MysqlConnect(){}
    virtual ~MysqlConnect(){}
    void connect(string);
};
void MysqlConnect::connect(string conn_str)
{
    cout << "mysql连接中..." << endl;
    //实现代码
    cout << "连接成功" << endl;
}
class MysqlExecute:public Execute
{
public:
    MysqlExecute()=default;
    virtual ~MysqlExecute();
    void execute(string);
private:

};
void MysqlExecute::execute(string sql_str)
{
    cout << "执行mysql语句" << endl;
}
class MysqlReader:public Reader

{
public:
    MysqlReader()=default;
    virtual ~MysqlReader(){}
    void read();
private:

};

void MysqlReader::read()
{
    cout << "mysql数据已经取出" << endl;
}

orcle扩展:

class OlcleConnect :public DbConnection
{
public:
    OlcleConnect(){}
    virtual ~OlcleConnect(){}
    void connect(string);
};
void OlcleConnect::connect(string conn_str)
{
    cout << "Olcle连接中..." << endl;
    //实现代码
    cout << "连接成功" << endl;
}
class OlcleExecute :public Execute
{
public:
    OlcleExecute()=default;
    virtual ~OlcleExecute(){}
    void execute(string);
private:

};
void OlcleExecute::execute(string sql_str)
{
    cout << "执行Olcle语句" << endl;
}
class OlcleReader :public Reader

{
public:
    OlcleReader()=default;
    virtual ~OlcleReader(){}
    void read();
private:

};

void OlcleReader::read()
{
    cout << "Olcle数据已经取出" << endl;
}

最糟糕的实现莫过于此,如果上天给我一次机会,一定不会这么写:

class DbEmployee
{
public:
    void getEmployees()
    {
        MysqlConnect * mysql_conn = new MysqlConnect();
        mysql_conn->connect("210.11.11.11:8000->db");
        MysqlExecute *mysql_exe = new MysqlExecute();
        mysql_exe->execute("select * from student");
        MysqlReader *mysql_read = new MysqlReader();
        mysql_read->read();
//记得删除对象 }
void Close() { //关闭中 } };

应该很容易看出来了,DbEmplee对象之中违背本末倒置原则,在变化中依赖具体的mysql对象,从而导致无法更好扩展orcle对象。

如果是最简单的工厂模式,那么我要写多几个工厂类:

一次性三个抽象工程:

class DbConnectionFactory
{
public:
    virtual DbConnection *createConnection() = 0;
    virtual ~DbConnectionFactory(){}
};

class DbExecuteFactory
{
public:
    virtual Execute *createExecute() = 0;
    virtual ~DbExecuteFactory(){}

private:

};

class DbReaderFactory
{
public:
    virtual Reader *createReader() = 0;
    virtual ~DbReaderFactory(){}

private:

};

扩展mysql工厂:

class MysqlConnectionFactory:public DbConnectionFactory
{
public:
    DbConnection *createConnection();
};
DbConnection * MysqlConnectionFactory::createConnection()
{
    return new MysqlConnect();
}

class MysqlExecuteFactory:public DbExecuteFactory
{
public:
    MysqlExecuteFactory()=default;
    ~MysqlExecuteFactory(){}
    Execute *createExecute();
private:

};
Execute *MysqlExecuteFactory::createExecute()
{
    return new MysqlExecute();
}

class MysqlReaderFactory :public DbReaderFactory
{
public:
    ~MysqlReaderFactory(){}
    Reader *createReader();
};
Reader * MysqlReaderFactory::createReader()
{
    return new MysqlReader();
}

orcle扩展:

class OrcleConnectionFactory :public DbConnectionFactory
{
public:
    DbConnection *createConnection();
};
DbConnection * OrcleConnectionFactory::createConnection()
{
    return new OrcleConnect();
}

class OrcleExecuteFactory :public DbExecuteFactory
{
public:
    OrcleExecuteFactory() = default;
    ~OrcleExecuteFactory(){}
    Execute *createExecute();
private:

};
Execute *OrcleExecuteFactory::createExecute()
{
    return new OrcleExecute();
}

class OrcleReaderFactory :public DbReaderFactory
{
public:
    ~OrcleReaderFactory(){}
    Reader *createReader();
};
Reader * OrcleReaderFactory::createReader()
{
    return new OrcleReader();
}

这个时候,就可以这么封装了。

class DbEmployee
{
public:
    DbEmployee(DbConnectionFactory *conn_f, DbExecuteFactory *Exe_f, DbReaderFactory *read_f)
        :_connFactory(conn_f), _executeFactory(Exe_f), _readerFactory(read_f){}
    ~DbEmployee()
    {
        delete _connFactory;
        delete _executeFactory;
        delete _readerFactory;
    }
    void getEmployees()
    {
        DbConnection * conn = _connFactory->createConnection();
        Execute * executer = _executeFactory->createExecute();
        Reader * reader = _readerFactory->createReader();
        conn->connect("210.11.11.11:3333->db");
        executer->execute("select * from student");
        reader->read();
        
    }
    void close()
    {
        //关闭中
    }
private:
    DbConnectionFactory * _connFactory;
    DbExecuteFactory * _executeFactory;
    DbReaderFactory * _readerFactory;
    
};

支持动态绑定:

int main
{
    DbEmployee good = DbEmployee(new MysqlConnectionFactory, new MysqlExecuteFactory, new MysqlReaderFactory);
    good.getEmployees();
    return 0;
}

运行结果:

到目前为止,我们还是在普通的工厂方法实现,所以上面的Dbemployee类还是有点问题的,比如如果不小心传入了不一致的Factory对象,将导致什么后果,三个Factory对象在实际过程中是有公有上下文,所以这三个factory对象最好还是要放在一起。同时这样写参数也是令人捉急。下面我把上面的工厂在改写一下:

首先改写抽象工厂,注意对比:

class BigFactory
{
public:
    virtual ~BigFactory(){}
    virtual DbConnection *createConnection() = 0;
    virtual Execute *createExecute() = 0;
    virtual Reader *createReader() = 0;

};

这是三个有关联的对象,实际过程中可能有数据交互。

扩展mysql大工厂:

class MysqlBigFactory :public BigFactory
{
public:
    DbConnection *createConnection();
    Execute *createExecute();
    Reader *createReader();
};
DbConnection *MysqlBigFactory::createConnection()
{
    return new MysqlConnect();
}
Execute *MysqlBigFactory::createExecute()
{
    return new MysqlExecute();
}
Reader *MysqlBigFactory::createReader()
{
    return new MysqlReader();
}

orcle:

class OrcleBigFactory :public BigFactory
{
public:
    DbConnection *createConnection();
    Execute *createExecute();
    Reader *createReader();
};
DbConnection *OrcleBigFactory::createConnection()
{
    return new OrcleConnect();
}
Execute *OrcleBigFactory::createExecute()
{
    return new OrcleExecute();
}
Reader *OrcleBigFactory::createReader()
{
    return new OrcleReader();
}

高度封装的DbEmployee对象:

class DbEmployee
{
public:
    DbEmployee(BigFactory *bigFactory)
        :_bigFactory(bigFactory){}
    ~DbEmployee()
    {
        delete _bigFactory;
    }
    void getEmployees()
    {
        DbConnection * conn = _bigFactory->createConnection();
        Execute * executer = _bigFactory->createExecute();
        Reader * reader = _bigFactory->createReader();
        conn->connect("210.11.11.11:3333->db");
        executer->execute("select * from student");
        reader->read();
        
    }
    void close()
    {
        //关闭中
    }
private:
    BigFactory * _bigFactory;
    
};

在使用的时候,就能看出好处了:

int main
{
    DbEmployee mysqlEmp = DbEmployee(new MysqlBigFactory);
    mysqlEmp.getEmployees();
    cout << "对比-----------------------" << endl;
    DbEmployee orcleEmp = DbEmployee(new OrcleBigFactory);
    orcleEmp.getEmployees();
    return 0;
}

使用很简单,只需要传入一个相应工厂对象,就可以在不同数据库之间无缝切换,互不干扰。

好了,一溜烟的全部写了这么多,应该有很多问题,但是大体思路就是这样。

原文地址:https://www.cnblogs.com/wuweixin/p/5446334.html