• 设计模式-抽象工厂模式


    抽象工厂模式Abstract Factory):

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

      

        也就是说上面我有两个项目A和B,然后每个项目有两个子类1,2。如果是按照简单工厂或者是工厂方法的话,应该是一个大接口出来,然后细分四个子类,然后再在工厂里根据需求实例化哪个类(简单工厂),或者是直接定义工厂抽象接口,然后再分别实现一个工厂子类来负责实现具体的实体(工厂方法),而抽象工厂则的思路则是既然A和B都有方法1和2,那么我们就可以直接在工厂接口里把1和2写死,然后再细分出来两个产品A和B,这样当我们扩展一个产品C的时候(当然这里默认C也是有两个方法1和2),我们只要在对应的产品方面增加出来一个C,然后再在工厂方面增加一个子类就行了,这样的话,两个模块都是增加而不是修改,所以满足OCP,这就是抽象工厂的思路,抽象工厂看上去比较重,我们可以采取反射的技术去优化这个地方,当然简单工厂和工厂方法也可以采取反射的思路(它们两个用反射优化完就长得一样了)。下面通过例子来继续解释这个思路。

    需求:有两个产品PC和Phone,同时每个产品都分为两个系统,WIndows和Linux,系统方面以后可能存在扩充,设计一个方便系统扩充的架构思路。通过抽象工厂思路实现上面要求:

    UML结构如下



    抽象工厂实现代码

    #pragma once
    #include <iostream>
    using namespace std;
    
    class CInterfaceOS
    {
    public:
    	virtual void StartOs() = 0;
    	virtual void CloseOs() = 0;
    };
    
    class CWindows : public CInterfaceOS
    {
    public:
    	void StartOs()
    	{
    		cout<<"StartWindowsOs"<<endl;
    	}
    	void CloseOs()
    	{
    		cout<<"CloseWindowsOs"<<endl;
    	}
    };
    
    class CLinux : public CInterfaceOS
    {
    public:
    	void StartOs()
    	{
    		cout<<"StartLinuxOs"<<endl;
    	}
    	void CloseOs()
    	{
    		cout<<"CloseLinuxOs"<<endl;
    	}
    };
    
    class CWindowsPc : public CWindows
    {
    public:
    	void StartOs()
    	{
    		cout<<"WindowsPc:";
    		CWindows::StartOs();
    		
    	}
    	void CloseOs()
    	{
    		cout<<"WindowsPc:";
    		CWindows::CloseOs();
    	}
    };
    
    class CWindowsPhone : public CWindows
    {
    public:
    	void StartOs()
    	{
    		cout<<"WindowsPhone:";
    		CWindows::StartOs();
    
    	}
    	void CloseOs()
    	{
    		cout<<"WindowsPhone:";
    		CWindows::CloseOs();
    	}
    };
    
    class CLinuxPc : public CLinux
    {
    public:
    	void StartOs()
    	{
    		cout<<"LinuxPc:";
    		CLinux::StartOs();
    
    	}
    	void CloseOs()
    	{
    		cout<<"LinuxPc:";
    		CLinux::CloseOs();
    	}
    };
    
    class CLinuxPhone : public CLinux
    {
    public:
    	void StartOs()
    	{
    		cout<<"LinuxPhone:";
    		CLinux::StartOs();
    
    	}
    	void CloseOs()
    	{
    		cout<<"LinuxPhone:";
    		CLinux::CloseOs();
    	}
    };
    
    class CInterfaceFactory
    {
    public:
    	virtual CInterfaceOS * CreatePcOs() = 0;
    	virtual CInterfaceOS * CreatePhoneOs() = 0;
    };
    
    
    class CCreateFactoryWindows : public CInterfaceFactory
    {
    public:
    	CInterfaceOS * CreatePcOs()
    	{
    		return new CWindowsPc();
    	}
    	CInterfaceOS * CreatePhoneOs()
    	{
    		return new CWindowsPhone();
    	}
    };
    
    
    class CCreateFactoryLinux : public CInterfaceFactory
    {
    public:
    	CInterfaceOS * CreatePcOs()
    	{
    		return new CLinuxPc();
    	}
    	CInterfaceOS * CreatePhoneOs()
    	{
    		return new CLinuxPhone();
    	}
    };

    客户端调用代码

    // TTT.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include "AbstractFactory.h"
    #include <iostream>
    #include <windows.h>
    using namespace std;
    
    int main()
    {
    	CInterfaceFactory *pHashFactory[10] = {0};
    	CInterfaceOS *pHashOs[10] = {0};
    	int nHashFactoryId = -1 ,nHashOsId = -1;
    	
    	pHashFactory[++nHashFactoryId] = new CCreateFactoryWindows();
    	pHashFactory[++nHashFactoryId] = new CCreateFactoryLinux();
    	for(int i = 0 ;i <= nHashFactoryId ;i ++)
    	{
    		pHashOs[++nHashOsId] = pHashFactory[i]->CreatePcOs();
    		pHashOs[++nHashOsId] = pHashFactory[i]->CreatePhoneOs();
    	}
    	for(int i = 0 ;i <= nHashOsId ;i ++)
    	{
    		pHashOs[i]->StartOs();
    		pHashOs[i]->CloseOs();
    	}
    
    	for(int i = 0 ;i <= nHashFactoryId ;i ++)
    	{
    		delete pHashFactory[i];
    	}
    	for(int i = 0 ;i <= nHashOsId ;i ++)
    	{
    		delete pHashOs[i];
    	}
    	return 0;
    }
    结果



    优化
        如上就是抽象工厂的基本实现,比较优雅,当扩充OS的时候是满足OCP的,当然上面的代码如果是扩充硬件,比如增加一个平板,那将是一个无比蛋疼的扩展,所以开发前自己衡量好。还有就是因为下面两个原因,在需要的时候有必要给抽象工厂进行一次优化:
      1.抽象工厂本身过于复杂,在方便维护扩展的同时也增加了维护的难度(有可能有人看上去会吃力或者蒙圈)。
      2.看上面,在客户端使用的时候客户端要了解所有工厂类,然后决定自己new哪个工厂,这样如果一个系统有100个地方用了一个数据库,那么我们100个地方都写了new但是如果换数据库的时候就算服务方面很容易就扩充了,但是客户端调用方面则要修改100个地方的new。
    所以抽象工厂有必要进行优化(简单工厂和工厂方法一样可以优化),采取的方式就是利用反射技术(当然有的语言不支持这个技术),大体思路就是我们把工厂相关的东西全都去掉,比如上面那个就是去掉三个类 CCreateFactoryLinux,CCreateFactoryWindows,CInterfaceFactory。然后增加这个(反射):

    const string g_Key = "A";
    class CCreateBody
    {
    	CInterfaceOS * CreatePcOs()
    	{
    		 //根据相关语言进行反射(g_Key为key值)
    		 //return new sg_Key();
    	}
    	CInterfaceOS * CreatePhoneOs()
    	{
    		//根据相关语言进行反射(g_Key为key值)
    		//return new sg_Key();
    	}
    };
    


    总结
        最大的好处便是易于交换产品系列,由于具体工厂类,在一个应用中只需要在吃实话的时候出现一次,也就使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。可以这么理解,一个东西可以分成两部分,其中一部分是比较固定的,另一部分需要扩展,这个时候可以考虑使用抽象工厂。
        它让具体的创建实例过程与客户端分离,客户端通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
        缺点就是过于复杂,并且客户端使用的时候要知道太多的工厂类,耦合度太高,不过还好可以考虑反射优化。



  • 相关阅读:
    Codeforces 166E. Tetrahedron
    Codeforce 687A. NP-Hard Problem
    Codeforces 570C. Replacement
    Codeforces 554B. Ohana Cleans Up
    Codeforces 482A. Diverse Permutation
    Codeforces 431C. k-Tree
    Codeforces 750B. Spider Man
    Codeforces 463A. Caisa and Sugar
    Codeforces 701B. Cells Not Under Attack
    Codeforces 445A. DZY Loves Chessboard
  • 原文地址:https://www.cnblogs.com/csnd/p/12062362.html
Copyright © 2020-2023  润新知