设计模式之四:工厂模式

引言:除了使用new操作符之外,还有更多制造对象的方法。同时,实例化这个活动不应该总是公开地进行。

1.简单工厂模式

这里有一些相关的具体类,要在运行时有一些具体条件来决定究竟实例化哪个类。这样的代码(if..elseif..elseif),一旦有变化或扩展,就必须重新打开进行检查和修改。

cpp 复制代码
Pizza orderPizza(String type)
{
	Pizza* pizza;

	if (type.equals("cheese"))
	{
		pizza = new CheesePizza();
	}
	else if (type.equals("greek"))
	{
		pizza = new GreekPizza();
	}
	else if (type.equals("pepperoni"))
	{
		pizza = new PepperoniPizza();
	}

	pizza->prepare();
	pizza->bake();
	pizza->cut();
	pizza->box();
	
	return pizza;

}

我们知道其中的if..elseif..elseif代码部分会改变,因此,我们阔以将创建pizza的代码移到一个专职创建pizza的对象中去。这个新对象就叫做"工厂",一旦有了SimplePizzaFactory,orderPizza就变成了此对象的客户。

SimplePizzaFactory可以有多个客户,并且需要修改时,只需要修改这个类即可。

(利用静态方法定义一个简单的工厂,被称为静态工厂。它不能通过继承来改变创建方法的行为)

cpp 复制代码
class SimplePizzaFactory
{
public:

	Pizza createPizza(String type)
	{
		Pizza* pizza = nullptr;

		if (type.equals("cheese"))
		{
			pizza = new CheesePizza();
		}
		else if (type.equals("greek"))
		{
			pizza = new GreekPizza();
		}
		else if (type.equals("pepperoni"))
		{
			pizza = new PepperoniPizza();
		}

		return pizza;
	}
cpp 复制代码
class PizzaStore
{
private:
	SimplePizzaFactory* factory;

public:
	PizzaStore(SimplePizzaFactory* factory)
	{
		this->factory = factory;
	}

	Pizza orderPizza(String type)
	{
        // 使用工厂对象的创建方法替换new操作符
		Pizza* pizza = factory->createPizza();

		pizza->prepare();
		pizza->bake();
		pizza->cut();
		pizza->box();
		return pizza;
	}
}

简单工厂其实并不是一种设计模式,反而像一种编程习惯。

2.工厂方法

由于Pizza店生意火爆,需要连锁模式加盟,这个时候该怎么做呢?

利用SimplePizzaFactory写出三种不同的工厂,如NYPizzaFactory。

cpp 复制代码
NYPizzaFactory* nyFactory = new NYPizzaFactory();
PizzaStore* nyStore = new PizzaStore(nyFactory);
nyStore->orderPizza("Veggie");

但你发现,加盟店虽然是用你的工厂创建Pizza,但是流程却不一样,他们不切片,或者使用其它厂商的盒子。因此,你希望支持他们的操作,把加盟店和创建Pizza捆绑在一起的同时,又保持一定的弹性(之前制作Pizza的代码绑定在PizzaStore里,大家都一样,没有弹性)。

(这里我没理解原文,开始说不切片、不用相同盒子,为了支持这个操作,除了把createPizza做成抽象方法,还应该把prepare, bake这个方法也封装成一个抽象方法才对)

因此,我们重新将createPizza方法放到PizzaStore,并将其设置为"抽象方法",最后为每个区域创建一个PizzaStore的子类。

cpp 复制代码
class PizzaStore()
{
public:
	Pizza* orderPizza(String type)
	{
		Pizza* pizza = createPizza();

		pizza->prepare();
		pizza->bake();
		pizza->cut();
		pizza->box();
		return pizza;
	}

	Pizza* createPizza(String type) = 0;
}

现在拥有PizzaStore作为超类,NYPizzaStore等只需继承它,自行决定如何制造Pizza。同时,PizzaStore已经有一个不错的订单系统,我们希望不同加盟商都用这个系统,因此,我们把orderPizza直接在超类中实现。子类负责createPizza方法(允许子类做决定)。

由于Pizza是抽象的,orderPizza()并不知道哪些具体类参与,这就是解耦。

(原本由一个对象负责所有具体类的实例化,现在通过对PizzaStore做一些小转变,变成由一群子类负责实例化)

工厂方法模式通过让子类决定该创建的对象是什么,来达到对象创建过程封装的目的。

依赖倒置原则:要依赖抽象,不要依赖具体类(当你实例化一个类的时候,就是在依赖它的具体类)。这个原则说明了:不能让高层组件依赖底层组件,而且,不论高层或者底层组件,两者都应该依赖于抽象。

若你在orderPizza方法中写出下面这样的代码:

上面代码问题在于,它依赖每个Pizza类型,因为他在orderPizza里面,实例化了这个具体类型。虽然我们有了一个抽象Pizza,但我们在代码中创建了具体的Pizza,所有这个抽象没什么用。而使用工厂方法可以解决这个问题。

你可以注意到,底层组件竟然在依赖高层的抽象,这就是依赖倒置。

下面几个指导方针可以帮助你遵守此原则:

  • 变量不可以持有具体类的引用(如果用new,就会持有具体类的引用,可以使用工厂来避开这样做法)
  • 不要让类派生自具体类(如果派生自具体类,就会依赖具体类)
  • 不要覆盖基类中已实现的方法(如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象,基类中已实现的方法,应该由所有子类共享)

当然,要完全遵守这些方针也不太可能,但我们应该把这些方针内化成思考的一部分。那么在设计时,就会指导何时有足够的理由违反这样的原则。

3.抽象工厂模式

现在需要建造一家原料工厂,供给各家加盟店。因为各加盟店需求不一样,因此需要有不同的工厂。

cpp 复制代码
class PizzaIngredientFactory
{
public:
	
	Dough createDough() = 0;
	Sauce createSauce() = 0;
	Cheese createCheese() = 0;
	Veggies[] createVeggies() = 0;
	Pepperoni createPepperoni() = 0;
	Clams createClam() = 0;
}

具体的工厂,如纽约工厂:

cpp 复制代码
class NYPizzaIngredientFactory : public PizzaIngredientFactory
{
public:
	Dough* createDough()
	{
		return new ThinCrustDough();
	}

	Sauce* createSauce()
	{
		return new MarinaraSauce();
	}

	Cheese* createCheese()
	{
		return new ReggianoCheese();
	}

	Veggies*[] createVeggies()
	{
		Veggies* veggies[] = { new Garlic(), new Onion() };
		return veggies;
	}

	Pepperoni createPepperoni()
	{
		return new SlicedPepperoni();
	}

	Clams createClam()
	{
		return new FreshClams();
	}
}

再来改造Pizza类:

cpp 复制代码
class Pizza
{
private:
	String* name;
	Dough* dough;
	Sauce* sauce;
	Veggies* veggies[];
	Cheese* cheese;
	Pepperoni* pepperoni;
	Clams* clam;

public:
	void prepare() = 0;

	void bake()
	{
		std::cout << "Bake for 25 mins at 350" << std::endl;
	}

	void cut()
	{
		std::cout << "Cutting pizza into slices" << std::endl;
	}

	void box()
	{
		std::cout << "Place pizza into box" << std::endl;
	}

	void setName(String name)
	{
		this->name = name;
	}
};
cpp 复制代码
// 不同区域的pizza用的工厂和种类数不一样

class CheesePizza : public Pizza
{
private:
	PizzaIngredientFactory* ingredientFactory;

public:
	CheesePizza(PizzaIngredientFactory* ingredientFactory)
	{
		this->ingredientFactory = ingredientFactory;
	}
	
	void prepare()
	{
		dough = ingredientFactory.createDough();
		sauce = ingredientFactory.createSauce();
		cheese = ingredientFactory.createCheese();
	}
};
cpp 复制代码
class NYPizzaStore : public PizzaStore
{
public:
	Pizza* createPizza(String type)
	{
		Pizza* pizza = nullptr;
		PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();

		if (type.equals("cheese"))
		{
			pizza = new CheesePizza(ingredientFactory);
		}
		else if (type.equals("veggie"))
		{
			pizza = new ClamPizza(ingredientFactory);
		}
		else if ()
		{

		}
		
		return pizza;
	}
}

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

如果从上述工厂方法模式的定义可以指导,抽象工厂每个方法都是工厂方法。

(工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象。抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中)

相关推荐
李广坤24 分钟前
状态模式(State Pattern)
设计模式
李广坤2 小时前
观察者模式(Observer Pattern)
设计模式
李广坤2 小时前
中介者模式(Mediator Pattern)
设计模式
李广坤3 小时前
迭代器模式(Iterator Pattern)
设计模式
李广坤3 小时前
解释器模式(Interpreter Pattern)
设计模式
阿无,6 小时前
java23种设计模式之前言
设计模式
Asort7 小时前
JavaScript设计模式(八):组合模式(Composite)——构建灵活可扩展的树形对象结构
前端·javascript·设计模式
数据智能老司机7 小时前
数据工程设计模式——数据基础
大数据·设计模式·架构
笨手笨脚の9 小时前
设计模式-代理模式
设计模式·代理模式·aop·动态代理·结构型设计模式
Overboom17 小时前
[C++] --- 常用设计模式
开发语言·c++·设计模式