前言:按照惯例我以Head First设计模式的工厂模式例子开始编码学习。并由简单工厂,工厂模式,抽象工厂模式依次演变,归纳他们的相同与不同。
话说Head First认为简单工厂并不是设计模式,而是一种编程习惯,但并不妨碍我们使用它,接下来我们对工厂模式一探究竟。
1、披萨店例子
首先我们要开一个披萨店,对于业务不复杂的情况下我们可以快速的开发出一个披萨店以及订购披萨的逻辑
代码语言:javascript复制 public Pizza OrderPizza() {
Pizza pizza = new Pizza();
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
}
public class Pizza {
//准备
public void Prepare() { }
//烘烤
public void Bake() { }
//切片
public void Cut() { }
//装盒
public void Box() { }
}
如果我们有更多的披萨种类可能需要将Pizza定义成抽象类 在订单里面根据订购的披萨种类返回不同的披萨,我们对披萨进行抽象并改造Order。
代码语言:javascript复制 public class PizzaStore
{
public Pizza OrderPizza(string type)
{
Pizza pizza=null;
if (type == "cheese")
{
pizza = new CheesePizza();
}
else if (type == "viggie") {
pizza = new VeggiePizza();
}
//else if ......
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
}
public abstract class Pizza
{
//准备
public void Prepare() { }
//烘烤
public void Bake() { }
//切片
public void Cut() { }
//装盒
public void Box() { }
}
//奶酪披萨
public class CheesePizza : Pizza
{
}
//素食披萨
public class VeggiePizza : Pizza
{
}
到这里我们可能想到了,如果增加披萨种类或者移除披萨那么我们将对披萨店进行修改。
设计原则对扩展开放,对修改关闭。我们需要将创建披萨的变化封装起来。对此弄出来一个专门创建披萨的“工厂“类。
并采用静态,这样就不需要实例化对象,也遵循了不对实现编程原则。
代码语言:javascript复制 public class PizzaStore
{
public Pizza OrderPizza(string type)
{
Pizza pizza = SimplePizzaFactory.CreatePizza(type);
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
}
public static class SimplePizzaFactory {
public static Pizza CreatePizza(string type) {
Pizza pizza = null;
if (type == "cheese")
{
pizza = new CheesePizza();
}
else if (type == "viggie")
{
pizza = new VeggiePizza();
}
return pizza;
}
}
这样将创建披萨简单的封装起来即是简单工厂(静态工厂),简单工厂也可以不用静态类,但简单工厂并不是一种专门的设计模式(有时候可能会混淆,认为这即是”工厂模式“),更像是我们平时编程都会做的一种习惯。我们将改动封装在一个局部当有变化的时候只需要修改这个工厂类。
2、更多的披萨店
现在我们要开更多的披萨店,例如美国风格披萨店(USSytlePizzaStore)、中国风格披萨店(CHNSytlePizzaStore)。
我们可以采用简单工厂模式,创建两个不同风格的披萨工厂,然后创建两个不同风格的披萨店,不同风格的披萨店使用对应的披萨工厂来获取。
但是我们此时的变化点是披萨店。我们希望披萨店的结构或者流程是按照一定规则的,只是不同风格的披萨。此时我们有更好的解决办法:工厂模式。
接下来我们看如何实现
代码语言:javascript复制 public abstract class PizzaStore
{
public Pizza OrderPizza(string type)
{
Pizza pizza= CreatePizza(type);
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
public abstract Pizza CreatePizza(string type);
}
public class USSytlePizzaStore : PizzaStore
{
public override Pizza CreatePizza(string type)
{
Pizza pizza = null;
if (type == "cheese")
{
pizza = new USStyleCheesePizza();
}
else if (type == "viggie")
{
pizza = new USStyleVeggiePizza();
}
return pizza;
}
}
public class CHNSytlePizzaStore : PizzaStore
{
public override Pizza CreatePizza(string type)
{
Pizza pizza = null;
if (type == "cheese")
{
pizza = new CHNStyleCheesePizza();
}
else if (type == "viggie")
{
pizza = new CHNStyleVeggiePizza();
}
return pizza;
}
}
//US奶酪披萨
public class USStyleCheesePizza : Pizza
{
}
//US素食披萨
public class USStyleVeggiePizza : Pizza
{
}
//CHN奶酪披萨
public class CHNStyleCheesePizza : Pizza
{
}
//CHN素食披萨
public class CHNStyleVeggiePizza : Pizza
{
}
由实现我们可以看到我们将PizzaStore修改成抽象类,不同的披萨店继承抽象类返回自己不同风格的披萨。
这样设计后当增加产品,我们也只是在具体的子类披萨店中修改其中的披萨创建,不会影响披萨店本身流程和其他披萨店的实现。
工厂方法模式:定义了一个创建对象的接口,由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。
工厂方法与简单工厂的区别:工厂方法的子类看起来很像简单工厂。简单工厂把全部的事情在一个地方处理完成,而工厂方法却是创建一个框架,让子类决定如何实现。
3、披萨的不同原料
不同风格的披萨店有不同风格的披萨,而这些披萨的不同风格是来自不同原料造成,所以不同风格的披萨变化的部分是材料。
我们先建造原料和原料工厂,以中国披萨原料工厂为例
代码语言:javascript复制//披萨原料工厂接口
public interface PizzaIngredientFactory {
public Veggie CreateVeggie();
public Cheese CreateCheese();
}
//具体工厂实现
public class CNHPizzaIngredientFactory : PizzaIngredientFactory
{
public Cheese CreateCheese()
{
return new CHNCheese();
}
public Veggie CreateVeggie()
{
return new CHNVeggie();
}
}
public abstract class Veggie
{
}
public class USVeggie : Veggie {
}
public class CHNVeggie : Veggie {
}
public abstract class Cheese
{
}
public class USCheese : Cheese
{
}
public class CHNCheese : Cheese
{
}
然后重做Pizza
代码语言:javascript复制 public abstract class Pizza
{
public String Name;
Veggie veggie;
Cheese cheese;
//准备
public abstract void Prepare();
//烘烤
public void Bake() { }
//切片
public void Cut() { }
//装盒
public void Box() { }
}
加入了原料的抽象 Veggie 和 Cheese,同时我们让Prepare变成抽象方法,让他的具体子类决定用什么材制造不同风格的披萨。接着我们重做子类,以CheesePizza为例
代码语言:javascript复制 //奶酪披萨
public class CheesePizza : Pizza
{
PizzaIngredientFactory IngredientFactory;
public CheesePizza(PizzaIngredientFactory IngredientFactory) {
this.IngredientFactory = IngredientFactory;
}
public override void Prepare()
{
IngredientFactory.CreateCheese();
}
}
修改中国披萨店
代码语言:javascript复制 public class CHNSytlePizzaStore : PizzaStore
{
public override Pizza CreatePizza(string type)
{
Pizza pizza = null;
//创建中国原材料工厂
CNHPizzaIngredientFactory ingredientFactory = new CNHPizzaIngredientFactory();
if (type == "cheese")
{
pizza = new CheesePizza(ingredientFactory);
}
else if (type == "viggie")
{
pizza = new VeggiePizza(ingredientFactory);
}
return pizza;
}
}
通过这一系列的改造我们引入了新类型的工厂,也就是所谓的抽象工厂,抽象工厂用来创造原料。
利用抽象工厂我们代码将从实际工厂解耦,这样如果我们的工厂需要扩展那么我们则可在子类中进行修改扩展。
工厂方法与抽象工厂的异同优缺点
相同:都是用来创建对象。
不同:工厂方法使用的是继承,抽象工厂使用的是组合。
优点:工厂方法只负责从具体类型中解耦,抽象工厂适合将一群相关的产品集合起来。
缺点:抽象工厂扩展接口需要修改每个子类。