設計模式學習(二)工廠模式——抽象工廠模式

paw5zx發表於2024-07-05

目錄
  • 背景
  • 抽象工廠模式
  • 優點與缺點
  • 參考文章

背景

現在我需要開發一個相機操作模組,它可能在Windows下執行,也可能在Linux下執行。由於在廠家提供的SDK中,Windows下的SDK和Linux下的SDK是有區別的,因此對於一個品牌的相機,我們要建立兩個類去封裝這兩個不同平臺下的API。

我們先使用工廠方法模式去設計(以Basler相機為例),類圖如下:

對應的程式碼(就不用智慧指標了,要不然類圖不好畫):

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }
};

//客戶端
int main()
{
	//如果更換平臺,客戶端程式碼只需要修改這一處
    CameraFactory* cameraFactory = new LinuxCameraFactory();
    
    BaslerCamera* camera = cameraFactory->CreateBaslerCamera();
    
    camera->OpenCamera();
    
    return 0;
}

現在若新增了一個品牌的相機:Sick,那麼按照工廠方法模式的設計思路,就會為其建立出對應的抽象工廠類和具體工廠類(具體程式碼略)。

但是進一步分析可以發現,對於這個模組,它要麼在Windows下執行,要麼在Linux下執行。即對於抽象產品BaslerCameraSickCamera,要麼例項化LinuxBaslerCameraLinuxSickCamera,要麼例項化WindowsBaslerCameraWindowsSickCamera

可以說不同的相機被劃分在Linux相機和Window相機這兩個產品族下,因此我們不需要為每一個品牌的相機都去實現一組對應的工廠類,而是隻使用兩個工廠WindowsCameraFactoryLinuxCameraFactory去管理各自對應平臺下的相機的建立過程。

那麼工廠類的程式碼就會變成這樣:

class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

這就引出了抽象工廠模式

抽象工廠模式

抽象工廠模式,提供一個建立一系列相關或相互依賴物件的介面,而無需指定他們具體的類

AbstractProductAAbstractProductB是兩個抽象產品,之所以為抽象,是因為他們可能有多種不同的實現,就剛才的例子來說,抽象產品就是BaslerCameraSickCameraProductA1ProductA2ProductB1ProductB2就是對兩個抽象產品的具體分類的實現,對應例子中的LinuxBaslerCameraWindowsBaslerCameraLinuxSickCameraWindowsSickCamera

AbstractFactory是一個抽象工廠基類,對應例子中的CameraFactory,它裡面應該包含產品族中每個產品建立的抽象方法。ConcreteFactory1ConcreteFactory2是具體工廠,對應例子中的LinuxCameraFactoryWindowsCameraFactory

對於客戶端,通常是在程式碼中建立一個具體工廠的例項(這個例項就對應著一個產品族),使用這個工廠例項再建立產品族內具體的產品物件。

客戶端程式碼如下:

int main()
{
    /*
    若在windows平臺,只需將本句改為:
    CameraFactory* cameraFactory = new WindowsCameraFactory();
    */
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

完整程式碼如下:

class BaslerCamera
{
public:
    virtual ~BaslerCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxBaslerCamera : public BaslerCamera
{
public:
    ~LinuxBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsBaslerCamera : public BaslerCamera
{
public:
    ~WindowsBaslerCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class SickCamera
{
public:
    virtual ~SickCamera() = default;
    virtual bool OpenCamera() = 0;
};

class LinuxSickCamera : public SickCamera
{
public:
    ~LinuxSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};

class WindowsSickCamera : public SickCamera
{
public:
    ~WindowsSickCamera() override = default;
    bool OpenCamera() override
    {
        return true;
    }
};


class CameraFactory
{
public:
    virtual ~CameraFactory() = default;
    virtual BaslerCamera* CreateBaslerCamera() = 0;
    virtual SickCamera* CreateSickCamera() = 0;
};

class LinuxCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new LinuxBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new LinuxSickCamera();
    }
};

class WindowsCameraFactory : public CameraFactory
{
public:
    BaslerCamera* CreateBaslerCamera() override
    {
        return new WindowsBaslerCamera();
    }

    SickCamera* CreateSickCamera() override
    {
        return new WindowsSickCamera();
    }
};

int main()
{
    //若在windows平臺,只需將本句改為CameraFactory* cameraFactory = new WindowsCameraFactory();
    CameraFactory* camera_factory = new LinuxCameraFactory();
    
    BaslerCamera* basler_camera = camera_factory->CreateBaslerCamera();
    basler_camera->OpenCamera();

    SickCamera* sick_camera = camera_factory->CreateSickCamera();
    sick_camera->OpenCamera();

    return 0;
}

優點與缺點

優點:

  • 易於更改產品族:工廠的例項化過程在一個客戶端只需要出現一次,修改方便

缺點:

  • 提供方違反開閉原則:如果現在在每個產品族內新增一個品牌相機(如Huaray),那麼除了要增加HuarayCameraWindowsHuarayCameraLinuxHuarayCamera三個產品類之外(這是必要的),還要修改CameraFactoryLinuxCameraFactoryWindowsCameraFactory這三個工廠類,違反了開閉原則。
  • 客戶端違法開閉原則:客戶端在開始的時候都要CameraFactory* camera_factory = new LinuxCameraFactory();,若是要更換為Windows平臺,則還需手動修改例項化的型別,違反了開閉原則。而且如果客戶端不止一個,則每一個客戶端都需要手動修改,效率低。

對於抽象工廠模式的改進方法,將在下一篇文章中討論。

參考文章

1.《大話設計模式》

相關文章