引言:除了使用new操作符之外,还有更多制造对象的方法。同时,实例化这个活动不应该总是公开地进行。
1.简单工厂模式
这里有一些相关的具体类,要在运行时有一些具体条件来决定究竟实例化哪个类。这样的代码(if..elseif..elseif),一旦有变化或扩展,就必须重新打开进行检查和修改。
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可以有多个客户,并且需要修改时,只需要修改这个类即可。
(利用静态方法定义一个简单的工厂,被称为静态工厂。它不能通过继承来改变创建方法的行为)
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;
}
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。
NYPizzaFactory* nyFactory = new NYPizzaFactory();
PizzaStore* nyStore = new PizzaStore(nyFactory);
nyStore->orderPizza("Veggie");
但你发现,加盟店虽然是用你的工厂创建Pizza,但是流程却不一样,他们不切片,或者使用其它厂商的盒子。因此,你希望支持他们的操作,把加盟店和创建Pizza捆绑在一起的同时,又保持一定的弹性(之前制作Pizza的代码绑定在PizzaStore里,大家都一样,没有弹性)。
(这里我没理解原文,开始说不切片、不用相同盒子,为了支持这个操作,除了把createPizza做成抽象方法,还应该把prepare, bake这个方法也封装成一个抽象方法才对)
因此,我们重新将createPizza方法放到PizzaStore,并将其设置为“抽象方法”,最后为每个区域创建一个PizzaStore的子类。
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.抽象工厂模式
现在需要建造一家原料工厂,供给各家加盟店。因为各加盟店需求不一样,因此需要有不同的工厂。
class PizzaIngredientFactory
{
public:
Dough createDough() = 0;
Sauce createSauce() = 0;
Cheese createCheese() = 0;
Veggies[] createVeggies() = 0;
Pepperoni createPepperoni() = 0;
Clams createClam() = 0;
}
具体的工厂,如纽约工厂:
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类:
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;
}
};
// 不同区域的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();
}
};
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;
}
}
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
从上述工厂方法模式的定义可以知道,抽象工厂每个方法都是工厂方法。
(工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象。抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中)