天天看點

c++ 設計模式 (三) - 工廠模式一、為什麼需要工廠模式二、簡單工廠模式(Simple Factory)三、工廠方法模式(Factory Method)四、抽象工廠模式(Abstract Factory)五、簡單工廠模式,工廠方法模式和抽象工廠模式異同

文章目錄

  • 一、為什麼需要工廠模式
  • 二、簡單工廠模式(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)

簡單工廠模式又稱靜态工廠方法模式,這種工廠模式中擷取對象的方法有兩種方式,通過對傳入的字段判斷達到傳回不同類型對象的目的

  1. 通過靜态函數方式,不需要執行個體化工廠對象
public class Factory {
    	public static <R, T> R getInstance(T type) {
        	// 根據傳入的type不同做if else判斷傳回不同類型的對象
    	}
	}
           
  1. 将擷取對象的方法定義成普通方法,需要執行個體化工廠對象,這種方式又叫做執行個體工廠模式
public class Factory {
    	public <R, T> R getInstance(T type) {
        	// 根據傳入的type不同做if else判斷傳回不同類型的對象
    	}
	}
           

簡單工廠模式确實在一定程度上實作代碼的解耦,而這種解耦的特點在于将對象的建立和使用分離,其實這個特點也是所有建立型模式的共性

假設

DaoFactory.getDao();

傳回的JpaDao和MybatisDao對象建立過程非常繁瑣冗長,如果不使用工廠模式,對象的建立邏輯就會散落在項目中各個Service類,造成代碼重複備援

但這種模式的缺點也很明顯,其本質在于通過一個傳入的參數,做

if else

判斷來達到傳回不同類型對象的目的。如果需要增加新的類型,就不得不去修改原來的代碼,違反開閉原則

其組成如下:

  1. 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
  2. 具體産品類:工廠類所建立的具體産品類,由具體類實作
  3. 具體工廠類:含有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對象即可,對于原先代碼再不需要做任何修改

此方法也有很明顯的缺點:每個類型的對象都會有一個與之對應的工廠類,如果對象的類型不是很多那還好,但是如果對象的類型非常多,意味着會需要建立很多的工廠實作類,造成類數量膨脹,對後續維護帶來很大麻煩

其組成:

  1. 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
  2. 具體産品類:工廠類所建立的具體産品類,由具體類實作
  3. 抽象工廠類:具體工廠類要繼承的基類,由抽象類來實作
  4. 具體工廠類:含有一定的判斷邏輯,由具體類來實作
#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類武器有一個專用術語叫做産品族。抽象工廠模式就是為了建立一系列以産品族為機關的對象,産品族内各個單獨元件關系密切。這樣在需要建立大量系列對象時可以大大提高開發效率,降低維護成本

假設上面手槍的例子中,每一類武器中就隻生産一種武器(元件),比如就隻生産手槍吧,不生産子彈等其他元件了。發現了什麼沒有?這居然轉變為我們之前講過的工廠方法模式啦!這就是“抽象工廠模式是工廠方法模式的更新”這句話闡述的本質。換句話說,當你的産品族中隻生産一種産品的時候,你的抽象工廠模式其實已經退化為工廠方法模式了。反過來說,當一個工廠類生産多種産品時,工廠方法模式就進化為抽象工廠模式

c++ 設計模式 (三) - 工廠模式一、為什麼需要工廠模式二、簡單工廠模式(Simple Factory)三、工廠方法模式(Factory Method)四、抽象工廠模式(Abstract Factory)五、簡單工廠模式,工廠方法模式和抽象工廠模式異同

多個抽象産品類,每個抽象産品類可以派生出多個具體産品類。一個抽象工廠類,可以派生出多個具體工廠類。每個具體工廠類可以建立多個具體産品類,組成(與工廠方法一緻):

  1. 抽象産品類:具體産品類要繼承的基類,由抽象類來實作
  2. 具體産品類:工廠類所建立的具體産品類,由具體類實作
  3. 抽象工廠類:具體工廠類要繼承的基類,由抽象類來實作
  4. 具體工廠類:含有一定的判斷邏輯,由具體類來實作
#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();
}
           

五、簡單工廠模式,工廠方法模式和抽象工廠模式異同

  • 簡單工廠模式:

    工廠類是整個模式的關鍵,包含必要的判斷邏輯,能夠根據外界給定的資訊決定究竟應該建立哪個具體産品類對象

    工廠類集中了所有産品類建立邏輯,系統擴充困難,一旦添加新産品就不得不修改工廠邏輯,有可能造成工廠邏輯過于複雜,違背了"開放–封閉"原則

  • 工廠方法模式:

    工廠方法模式是簡單工廠模式的衍生,實作了可擴充,對每個對象生成了具體工廠。該模式專門用于解決單個對象建立工作,符合開閉原則

    但該模式當對象種類很多時,存在工廠類數量膨脹的問題,如果需要建立的工廠類不是很多,是一種不錯的選擇

  • 抽象工廠模式:

    它為産品族而生,有多個抽象産品類,每個抽象産品類可以派生出多個具體産品類,一個抽象工廠類,可以派生出多個具體工廠類,每個具體工廠類可以建立多個具體産品類執行個體

抽象工廠和工廠方法差別:

  • 工廠方法模式:

    一個抽象産品類,可以派生出多個具體産品類

    一個抽象工廠類,可以派生出多個具體工廠類

    每個具體工廠類隻能建立一個具體産品類的執行個體

  • 抽象工廠模式:

    多個抽象産品類,每個抽象産品類可以派生出多個具體産品類

    一個抽象工廠類,可以派生出多個具體工廠類

    每個具體工廠類可以建立多個具體産品類的執行個體

繼續閱讀