- 背景
- 抽象工廠模式
- 優點與缺點
- 參考文章
背景
現在我需要開發一個相機操作模組,它可能在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
下執行。即對於抽象產品BaslerCamera
和SickCamera
,要麼例項化LinuxBaslerCamera
和LinuxSickCamera
,要麼例項化WindowsBaslerCamera
和WindowsSickCamera
。
可以說不同的相機被劃分在Linux相機和Window相機這兩個產品族下,因此我們不需要為每一個品牌的相機都去實現一組對應的工廠類,而是隻使用兩個工廠WindowsCameraFactory
和LinuxCameraFactory
去管理各自對應平臺下的相機的建立過程。
那麼工廠類的程式碼就會變成這樣:
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();
}
};
這就引出了抽象工廠模式
抽象工廠模式
抽象工廠模式,提供一個建立一系列相關或相互依賴物件的介面,而無需指定他們具體的類
AbstractProductA
和AbstractProductB
是兩個抽象產品,之所以為抽象,是因為他們可能有多種不同的實現,就剛才的例子來說,抽象產品就是BaslerCamera
和SickCamera
。ProductA1
,ProductA2
,ProductB1
,ProductB2
就是對兩個抽象產品的具體分類的實現,對應例子中的LinuxBaslerCamera
,WindowsBaslerCamera
,LinuxSickCamera
,WindowsSickCamera
。
AbstractFactory
是一個抽象工廠基類,對應例子中的CameraFactory
,它裡面應該包含產品族中每個產品建立的抽象方法。ConcreteFactory1
和ConcreteFactory2
是具體工廠,對應例子中的LinuxCameraFactory
和WindowsCameraFactory
。
對於客戶端,通常是在程式碼中建立一個具體工廠的例項(這個例項就對應著一個產品族),使用這個工廠例項再建立產品族內具體的產品物件。
客戶端程式碼如下:
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),那麼除了要增加
HuarayCamera
,WindowsHuarayCamera
,LinuxHuarayCamera
三個產品類之外(這是必要的),還要修改CameraFactory
,LinuxCameraFactory
和WindowsCameraFactory
這三個工廠類,違反了開閉原則。 - 客戶端違法開閉原則:客戶端在開始的時候都要
CameraFactory* camera_factory = new LinuxCameraFactory();
,若是要更換為Windows平臺,則還需手動修改例項化的型別,違反了開閉原則。而且如果客戶端不止一個,則每一個客戶端都需要手動修改,效率低。
對於抽象工廠模式的改進方法,將在下一篇文章中討論。
參考文章
1.《大話設計模式》