c++ 设计模式 (三) - 工厂模式


一、为什么需要工厂模式

平时我们创建一个对象大多数情况都是直接 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 实现 DaoFactorygetDao 方法,返回对应的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类武器有一个专用术语叫做产品族。抽象工厂模式就是为了创建一系列以产品族为单位的对象,产品族内各个单独组件关系密切。这样在需要创建大量系列对象时可以大大提高开发效率,降低维护成本
假设上面手枪的例子中,每一类武器中就只生产一种武器(组件),比如就只生产手枪吧,不生产子弹等其他组件了。发现了什么没有?这居然转变为我们之前讲过的工厂方法模式啦!这就是“抽象工厂模式是工厂方法模式的升级”这句话阐述的本质。换句话说,当你的产品族中只生产一种产品的时候,你的抽象工厂模式其实已经退化为工厂方法模式了。反过来说,当一个工厂类生产多种产品时,工厂方法模式就进化为抽象工厂模式

在这里插入图片描述
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类可以创建多个具体产品类,组成(与工厂方法一致):

  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();
}

五、简单工厂模式,工厂方法模式和抽象工厂模式异同

  • 简单工厂模式:
    工厂类是整个模式的关键,包含必要的判断逻辑,能够根据外界给定的信息决定究竟应该创建哪个具体产品类对象
    工厂类集中了所有产品类创建逻辑,系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂,违背了"开放–封闭"原则
  • 工厂方法模式:
    工厂方法模式是简单工厂模式的衍生,实现了可扩展,对每个对象生成了具体工厂。该模式专门用于解决单个对象创建工作,符合开闭原则
    但该模式当对象种类很多时,存在工厂类数量膨胀的问题,如果需要创建的工厂类不是很多,是一种不错的选择
  • 抽象工厂模式:
    它为产品族而生,有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类实例

抽象工厂和工厂方法区别:

  • 工厂方法模式:
    一个抽象产品类,可以派生出多个具体产品类
    一个抽象工厂类,可以派生出多个具体工厂类
    每个具体工厂类只能创建一个具体产品类的实例

  • 抽象工厂模式:
    多个抽象产品类,每个抽象产品类可以派生出多个具体产品类
    一个抽象工厂类,可以派生出多个具体工厂类
    每个具体工厂类可以创建多个具体产品类的实例

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值