• 设计模式学习总结:(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;
    }

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

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

  • 相关阅读:
    Blender 2.8 [学习笔记-001] 基本设置
    Blender 2.8 [学习笔记] 编辑模式下显示边长、面积等信息
    第十八章节 BJROBOT 安卓手机 APP 建地图【ROS全开源阿克曼转向智能网联无人驾驶车】
    第十七章节 BJROBOT opencv_apps 图像处理示例【ROS全开源阿克曼转向智能网联无人驾驶车】
    第十六章节 BJROBOT 开机自启动服务【ROS全开源阿克曼转向智能网联无人驾驶车】
    第十五章节 BJROBOT cartographer 算法构建地图【ROS全开源阿克曼转向智能网联无人驾驶车】
    第14章节 BJROBOT karto 算法构建地图【ROS全开源阿克曼转向智能网联无人驾驶车】
    第13章节 BJROBOT 雷达跟随【ROS全开源阿克曼转向智能网联无人驾驶车】
    第十二章节 BJROBOT 摄像头寻线 【ROS全开源阿克曼转向智能网联无人驾驶车】
    第十章节 BJROBOT PID 动态调节【ROS全开源阿克曼转向智能网联无人驾驶车】
  • 原文地址:https://www.cnblogs.com/wuweixin/p/5446334.html
Copyright © 2020-2023  润新知