文章目錄
- 一、為什麼需要工廠模式
- 二、簡單工廠模式(Simple Factory)
- 三、工廠方法模式(Factory Method)
- 四、抽象工廠模式(Abstract Factory)
- 五、簡單工廠模式,工廠方法模式和抽象工廠模式異同
一、為什麼需要工廠模式
平時我們建立一個對象大多數情況都是直接 new 一下不就有了嗎?為什麼非要按照工廠模式來建立對象呢?
為友善了解,下面舉個簡單的例子,如下代碼:
public class Service {
Dao dao = new JpaDao(); ❶
void service()
{
// 使用 dao 進行一系列持久層操作
}
}
這是一個經典的Service層,調用 Dao 層API完成資料庫操作
假設現在這個團隊使用的持久層架構是JPA,是以他們把所有的資料庫操作都封裝在 JpaDao 這個工具類中,然後在Service類裡面通過
new
得到一個執行個體,并在具體業務方法中使用它來完成資料庫操作
這代碼執行結果會有問題嗎?當然不會,業務邏輯正常執行完成
但是思考一下,這樣的代碼會有啥問題?
假設某一天,經過團隊讨論決定不用JPA這玩意了,還是使用 Mybatis。于是團隊将Mybatis封裝了一個 MybatisDao 工具包,打算用它來替換之前工程中使用的 JpaDao。現在問題來了,原來
Dao dao = new JpaDao();
這一行是寫死在每一個Service類中,現在需要将所有Service類中的
Dao dao = new JpaDao();
修改為
Dao dao = new MybatisDao();
。如果整個工程就隻有幾個Service類那還好辦,改就改了,如果有成千上萬個呢?而且要是下次又換個别的架構呢?
這個代碼就違反了設計原則中的開閉原則:對擴充開放,對修改關閉,即可以加入新的代碼,但是絕對不允許修改以前寫好的代碼
這個代碼關鍵問題在哪裡,才會導緻這種不可收拾的結果呢?
在這個代碼中,Service和JpaDao是緊耦合的,也就意味着每個Service你隻能使用JpaDao,要想使用其它的,沒有其他選擇,必須修改代碼
有什麼辦法可以讓它解耦?問題關鍵在于,在❶處是直接
new
出來一個非常具體的底層對象,是以要想解耦,必須拿掉這個
new
的邏輯。可是對象還是得建立啊,
new
拿掉了還怎麼建立呢?
new
當然還是要的,但是得讓他挪個地,優化代碼如下:
public class Service {
Dao dao = DaoFactory.getDao(); ❶
void service()
{
// 使用 dao 進行一系列持久層操作
}
}
public class DaoFactory {
public static Dao getDao()
{
return new JpaDao(); ❷
}
}
這樣優化後有一個明顯好處,以後不管換啥持久層實作,所有的Service類是不需要再改動了,隻需要修改一下DaoFactory類❷處,将其改為
return new MybatisDao();
就可以了,這樣一來修改代碼的工作量極大減少。但是,雖然代碼修改工作量少了,可是根本問題還是沒有解決,
DaoFactory
的代碼還是得修改,還是違反了開閉原則。由此,引入工廠模式
二、簡單工廠模式(Simple Factory)
簡單工廠模式又稱靜态工廠方法模式,這種工廠模式中擷取對象的方法有兩種方式,通過對傳入的字段判斷達到傳回不同類型對象的目的
- 通過靜态函數方式,不需要執行個體化工廠對象
public class Factory {
public static <R, T> R getInstance(T type) {
// 根據傳入的type不同做if else判斷傳回不同類型的對象
}
}
- 将擷取對象的方法定義成普通方法,需要執行個體化工廠對象,這種方式又叫做執行個體工廠模式
public class Factory {
public <R, T> R getInstance(T type) {
// 根據傳入的type不同做if else判斷傳回不同類型的對象
}
}
簡單工廠模式确實在一定程度上實作代碼的解耦,而這種解耦的特點在于将對象的建立和使用分離,其實這個特點也是所有建立型模式的共性
假設
DaoFactory.getDao();
傳回的JpaDao和MybatisDao對象建立過程非常繁瑣冗長,如果不使用工廠模式,對象的建立邏輯就會散落在項目中各個Service類,造成代碼重複備援
但這種模式的缺點也很明顯,其本質在于通過一個傳入的參數,做
if else
判斷來達到傳回不同類型對象的目的。如果需要增加新的類型,就不得不去修改原來的代碼,違反開閉原則
其組成如下:
- 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
- 具體産品類:工廠類所建立的具體産品類,由具體類實作
- 具體工廠類:含有if-else判斷邏輯,由具體類實作
#include <iostream>
typedef enum ClassType
{
SINGCORE_A,
SINGCORE_B,
} ClassType;
class SingCore // 抽象産品
{
public :
virtual void Show( ) = 0;
};
class SingCoreA : public SingCore // 具體産品A
{
public :
void Show( )
{
std::cout <<"SingCore A..." <<std::endl;
}
};
class SingCoreB : public SingCore // 具體産品B
{
public :
void Show( )
{
std::cout <<"SingCore B..." <<std::endl;
}
};
class Factory // 具體工廠
{
public :
SingCore* CreateSingCore(ClassType classType)
{
switch(classType)
{
case SINGCORE_A :
{
return new SingCoreA( );
}
case SINGCORE_B :
{
return new SingCoreB( );
}
}
}
};
int main()
{
Factory *factory = new Factory( );
factory->CreateSingCore(SINGCORE_A)->Show( );
factory->CreateSingCore(SINGCORE_B)->Show( );
return 0;
}
三、工廠方法模式(Factory Method)
簡單工廠模式之是以違反開閉原則,關鍵在于它把所有對象的建立都集中在同一個工廠類裡面了!是以當新增新對象類型時,必然會修改這個共享工廠類,違反開閉原則自然不可避免
既然問題關鍵在于,所有對象的建立都跟這個唯一的工廠類耦合了,那麼每種對象各自都配置一個單獨的工廠類,這個工廠類隻建立各自類型的對象,那不就解決耦合的問題了嗎?
這就引出了工廠模式的第二種形式——工廠方法模式。工廠方法模式是在簡單工廠模式基礎上進一步抽象,将工廠類設計成抽象類或接口,不同的種類對象實作各自的工廠實體類。建立對象時,隻需要提供對應的産品類和該産品的工廠類即可
class DaoFactory{
Dao getDao();
}
class JpaDaoFactory implements DaoFactory{
public Dao getDao()
{
return new JpaDao();
}
}
class MybatisDaoFactory implements DaoFactory{
public Dao getDao()
{
return new MybatisDao();
}
}
public class Service{
private Dao dao;
public Service(DaoFactory daoFactory)
{
this.dao = daoFactory.getDao();
}
void service()
{
// 使用dao進行一系列持久層操作
}
}
從上面例子不難看出,工廠方法模式輕松解決了簡單工廠模式的問題,符合開閉原則。當需要切換另外某持久層實作,比如JDBC,此時隻需要提供一個對應的
JDBCDaoFactory
實作
DaoFactory
的
getDao
方法,傳回對應的Dao對象即可,對于原先代碼再不需要做任何修改
此方法也有很明顯的缺點:每個類型的對象都會有一個與之對應的工廠類,如果對象的類型不是很多那還好,但是如果對象的類型非常多,意味着會需要建立很多的工廠實作類,造成類數量膨脹,對後續維護帶來很大麻煩
其組成:
- 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
- 具體産品類:工廠類所建立的具體産品類,由具體類實作
- 抽象工廠類:具體工廠類要繼承的基類,由抽象類來實作
- 具體工廠類:含有一定的判斷邏輯,由具體類來實作
#include <iostream>
class SingCore // 抽象産品類
{
public :
virtual void Show( ) = 0;
};
class SingCoreA : public SingCore // 具體産品類A
{
public :
void Show( )
{
std::cout <<"SingCore A..." <<std::endl;
}
};
class SingCoreB : public SingCore // 具體産品類B
{
public :
void Show( )
{
std::cout <<"SingCore B..." <<std::endl;
}
};
class Factory // 抽象工廠類
{
public:
virtual SingCore* CreateSingCore( ) = 0;
};
class FactoryA : public Factory // 具體工廠類A
{
public :
SingCoreA* CreateSingCore( )
{
return new SingCoreA( );
}
};
class FactoryB : public Factory // 具體工廠類B
{
public :
SingCoreB* CreateSingCore( )
{
return new SingCoreB( );
}
};
int main( )
{
Factory *factoryA = new FactoryA( );
Factory *factoryB = new FactoryB( );
factoryA->CreateSingCore( )->Show( );
factoryB->CreateSingCore( )->Show( );
return 0;
}
四、抽象工廠模式(Abstract Factory)
有時候對象類型很多,但是彼此之間有依賴或者關系很緊密,屬于同一個系列的東西,完全可以一起建立,沒必要為每個對象單獨建立一個工廠實作類了。但是使用工廠方法模式,還是不得不為每個對象單獨建立一個工廠實作類,是以非常繁瑣
假設現在需要建立一個手槍對象,但是有手槍,肯定需要配備子彈吧?也就是說還要附帶建立一個子彈對象。那如果使用上面的工廠方法模式能不能完成這一需求呢?當然沒問題,隻要定義一個手槍和子彈對象的公共工廠接口,然後各自建立不同的工廠實作類即可。但是這樣的話,需要有兩個工廠實作類。如果還需要配備其它和手槍相關的裝備,還需要再繼續建立一個對應的工廠實作類。再繼續延伸,要是還有另外一種類的手槍,需要再這麼來一波操作,工廠實作類的數量将繼續翻倍。以此類推,這也是上面總結的工廠方法模式的缺點。當随着建立的對象類型越來越多,工廠實作類數量也跟着膨脹
這個例子中,建立的對象都是和手槍緊密相關的,你要建立手槍,其餘元件也是需要跟着建立的,那這個時候也就沒必要再單獨為每一個元件配置一個工廠實作類了,直接在同一個工廠類中一起建立,這樣不就解決工廠實作類數量膨脹問題了嗎?就是我們接下來要聊的抽象工廠模式了,其可以看成是工廠方法模式的更新
public interface Weapon {}
interface Gun extends Weapon{}
interface Bullet extends Weapon{}
class GunA implements Gun{}
class BulletA implements Bullet{}
class GunB implements Gun{}
class BulletB implements Bullet{}
public interface WeaponFactory
{
Gun getGun();
Bullet getBullet();
}
// A類武器工廠,用來建立A類武器,包括A類手槍和A類子彈
class AWeaponFactory implements WeaponFactory
{
public Gun getGun()
{
return new GunA();
}
public Bullet getBullet()
{
return new BulletA();
}
}
// B類武器工廠,用來建立B類武器,包括B類手槍和B類子彈
class BWeaponFactory implements WeaponFactory
{
public Gun getGun()
{
return new GunB();
}
public Bullet getBullet()
{
return new BulletB();
}
}
上面的A類武器和B類武器有一個專用術語叫做産品族。抽象工廠模式就是為了建立一系列以産品族為機關的對象,産品族内各個單獨元件關系密切。這樣在需要建立大量系列對象時可以大大提高開發效率,降低維護成本
假設上面手槍的例子中,每一類武器中就隻生産一種武器(元件),比如就隻生産手槍吧,不生産子彈等其他元件了。發現了什麼沒有?這居然轉變為我們之前講過的工廠方法模式啦!這就是“抽象工廠模式是工廠方法模式的更新”這句話闡述的本質。換句話說,當你的産品族中隻生産一種産品的時候,你的抽象工廠模式其實已經退化為工廠方法模式了。反過來說,當一個工廠類生産多種産品時,工廠方法模式就進化為抽象工廠模式
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPnJma5YUYzUzVa9mTywEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcugTM5UTMxETM3ATNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
多個抽象産品類,每個抽象産品類可以派生出多個具體産品類。一個抽象工廠類,可以派生出多個具體工廠類。每個具體工廠類可以建立多個具體産品類,組成(與工廠方法一緻):
- 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
- 具體産品類:工廠類所建立的具體産品類,由具體類實作
- 抽象工廠類:具體工廠類要繼承的基類,由抽象類來實作
- 具體工廠類:含有一定的判斷邏輯,由具體類來實作
#include <iostream>
class Gun // 抽象産品簇Gun
{
public:
virtual void showGun( ) const = 0;
};
class GunA: public Gun // 産品簇Gun的具體産品A
{
public:
void showGun( ) const
{
std::cout<<"i'm GunA"<<std::endl;
}
};
class GunB: public Gun // 産品簇Gun的具體産品B
{
public:
void showGun( ) const
{
std::cout<<"i'm GunB"<<std::endl;
}
};
class Bullet // 抽象産品簇Bullet
{
public:
virtual void showBullet( ) const = 0;
};
class BulletA : public Bullet // 産品簇Bullet的具體産品A
{
public:
void showBullet() const
{
std::cout<<"i'm BulletA"<<std::endl;
}
};
class BulletB : public Bullet // 産品簇Bullet的具體産品B
{
public:
void showBullet( ) const
{
std::cout<<"i'm BulletB"<<std::endl;
}
};
class WeaponFactory // 抽象工廠類
{
public:
virtual Gun* createGun() = 0;
virtual Bullet* createBullet() = 0;
};
class WeaponFactoryA :public WeaponFactory // 具體工廠類A
{
public:
Gun* createGun()
{
return new GunA();
}
Bullet* createBullet()
{
return new BulletA();
}
};
class WeaponFactoryB : public WeaponFactory // 具體工廠類B
{
public:
Gun* createGun()
{
return new GunB();
}
Bullet* createBullet()
{
return new BulletB();
}
};
int main()
{
WeaponFactory* factoryA = new WeaponFactoryA();
factoryA->createGun()->showGun();
factoryA->createBullet()->showBullet();
WeaponFactory* factoryB = new WeaponFactoryB();
factoryB->createGun()->showGun();
factoryB->createBullet()->showBullet();
}
五、簡單工廠模式,工廠方法模式和抽象工廠模式異同
-
簡單工廠模式:
工廠類是整個模式的關鍵,包含必要的判斷邏輯,能夠根據外界給定的資訊決定究竟應該建立哪個具體産品類對象
工廠類集中了所有産品類建立邏輯,系統擴充困難,一旦添加新産品就不得不修改工廠邏輯,有可能造成工廠邏輯過于複雜,違背了"開放–封閉"原則
-
工廠方法模式:
工廠方法模式是簡單工廠模式的衍生,實作了可擴充,對每個對象生成了具體工廠。該模式專門用于解決單個對象建立工作,符合開閉原則
但該模式當對象種類很多時,存在工廠類數量膨脹的問題,如果需要建立的工廠類不是很多,是一種不錯的選擇
-
抽象工廠模式:
它為産品族而生,有多個抽象産品類,每個抽象産品類可以派生出多個具體産品類,一個抽象工廠類,可以派生出多個具體工廠類,每個具體工廠類可以建立多個具體産品類執行個體
抽象工廠和工廠方法差別:
-
工廠方法模式:
一個抽象産品類,可以派生出多個具體産品類
一個抽象工廠類,可以派生出多個具體工廠類
每個具體工廠類隻能建立一個具體産品類的執行個體
-
抽象工廠模式:
多個抽象産品類,每個抽象産品類可以派生出多個具體産品類
一個抽象工廠類,可以派生出多個具體工廠類
每個具體工廠類可以建立多個具體産品類的執行個體