学习设计模式第二十七 - GoF之外简单工厂模式
示例代码来自《深入浅出设计模式》和《大话设计模式》
概述
简单工厂模式又被称为静态工厂模式,属于类的创建型模式。其实质是由一个工厂类根据传入的参量,动态决定应该创建出哪一个产品类的实例。
意图
专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
UML
图1 简单工厂模式的UML图
参与者
这个模式涉及的类或对象:
-
Creator
-
它的角色就是工厂,负责生产各种产品。
-
Product
-
它的角色是产品,是对所有产品的一个统称。在实现过程中,它是具体产品的公共基类。
-
ConcreteProduct
-
它的角色是具体产品,它是每一种产品的具体实现。
来自《大话设计模式》的例子
这是一个很简单的计算器的例子,所有的计算工作被抽象成一个产品对象,而加或减这样一个具体计算被设计为一个具体产品。同时一个工厂类根据用户输入的不同返回具体的计算对象。
例子中涉及到的类与简单工厂模式中标准的类对应关系如下:
-
Product – Operation
-
ConcreteProduct – OperationAdd,OperationSub,OperationMul等
-
Creator – OperationFactory
using System; // 运算类 public class Operation { private double _numberA = 0; private double _numberB = 0; // 数字A public double NumberA { get { return _numberA; } set { _numberA = value; } } // 数字B public double NumberB { get { return _numberB; } set { _numberB = value; } } // 得到运算结果 public virtual double GetResult() { double result = 0; return result; } // 检查输入的字符串是否准确 public static string checkNumberInput(string currentNumber, string inputString) { string result = ""; if (inputString == ".") { if (currentNumber.IndexOf(".") < 0) { if (currentNumber.Length == 0) result = "0" + inputString; else result = currentNumber + inputString; } } else if (currentNumber == "0") { result = inputString; } else { result = currentNumber + inputString; } return result; } } // 加法类 class OperationAdd : Operation { public override double GetResult() { double result = 0; result = NumberA + NumberB; return result; } } // 减法类 class OperationSub : Operation { public override double GetResult() { double result = 0; result = NumberA - NumberB; return result; } } // 乘法类 class OperationMul : Operation { public override double GetResult() { double result = 0; result = NumberA * NumberB; return result; } } // 除法类 class OperationDiv : Operation { public override double GetResult() { double result = 0; if (NumberB == 0) throw new Exception("除数不能为0。"); result = NumberA / NumberB; return result; } } // 平方类 class OperationSqr : Operation { public override double GetResult() { double result = 0; result = NumberB * NumberB; return result; } } // 平方根类 class OperationSqrt : Operation { public override double GetResult() { double result = 0; if (NumberB < 0) throw new Exception("负数不能开平方根。"); result = Math.Sqrt(NumberB); return result; } } // 相反数类 class OperationReverse : Operation { public override double GetResult() { double result = 0; result = -NumberB; return result; } } // 运算类工厂 public class OperationFactory { public static Operation createOperate(string operate) { Operation oper = null; switch (operate) { case "+": { oper = new OperationAdd(); break; } case "-": { oper = new OperationSub(); break; } case "*": { oper = new OperationMul(); break; } case "/": { oper = new OperationDiv(); break; } case "sqr": { oper = new OperationSqr(); break; } case "sqrt": { oper = new OperationSqrt(); break; } case "+/-": { oper = new OperationReverse(); break; } } return oper; } } class Program { static void Main(string[] args) { try { Console.Write("请输入数字A:"); string strNumberA = Console.ReadLine(); Console.Write("请选择运算符号(+、-、*、/):"); string strOperate = Console.ReadLine(); Console.Write("请输入数字B:"); string strNumberB = Console.ReadLine(); string strResult = ""; Operation oper; oper = OperationFactory.createOperate(strOperate); oper.NumberA = Convert.ToDouble(strNumberA); oper.NumberB = Convert.ToDouble(strNumberB); strResult = oper.GetResult().ToString(); Console.WriteLine("结果是:" + strResult); Console.ReadLine(); } catch (Exception ex) { Console.WriteLine("您的输入有错:" + ex.Message); } } }
来自《深入浅出设计模式》的例子
这个例子中使用简单工厂实现了一个比萨店(我们还会用比萨店的例子来展示工厂方法模式和抽象工厂模式的应用),简单比萨工厂负责不同种类比萨的选择。首先我们给出这个示例的UML,然后是代码:
图2 使用比萨店例子的简单工厂UML图
using System; using System.Text; using System.Collections.Generic; namespace DoFactory.HeadFirst.SimpleFactory.PizzaShop { class PizzaTestDrive { static void Main(string[] args) { var factory = new SimplePizzaFactory(); var store = new PizzaStore(factory); var pizza = store.OrderPizza("cheese"); Console.WriteLine("We ordered a " + pizza.Name + "\n"); pizza = store.OrderPizza("veggie"); Console.WriteLine("We ordered a " + pizza.Name + "\n"); // Wait for user Console.ReadKey(); } } #region PizzaStore public class PizzaStore { private SimplePizzaFactory _factory; public PizzaStore(SimplePizzaFactory factory) { this._factory = factory; } public Pizza OrderPizza(string type) { Pizza pizza = _factory.CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } } #endregion #region SimplePizzaFactory public class SimplePizzaFactory { public Pizza CreatePizza(string type) { Pizza pizza = null; switch (type) { case "cheese": pizza = new CheesePizza(); break; case "pepperoni": pizza = new PepperoniPizza(); break; case "clam": pizza = new ClamPizza(); break; case "veggie": pizza = new VeggiePizza(); break; } Console.WriteLine(pizza); return pizza; } } #endregion #region Pizza abstract public class Pizza { private string _name; private string _dough; private string _sauce; private List<string> toppings = new List<string>(); public Pizza(string name, string dough, string sauce) { this._name = name; this._dough = dough; this._sauce = sauce; } public string Name { get { return _name; } set { _name = value; } } public List<string> Toppings { get { return toppings; } } public void Prepare() { Console.WriteLine("Preparing " + _name); } public void Bake() { Console.WriteLine("Baking " + _name); } public void Cut() { Console.WriteLine("Cutting " + _name); } public void Box() { Console.WriteLine("Boxing " + _name); } // code to display pizza name and ingredients public override string ToString() { StringBuilder display = new StringBuilder(); display.Append("---- " + _name + " ----\n"); display.Append(_dough + "\n"); display.Append(_sauce + "\n"); foreach (string topping in toppings) { display.Append(topping + "\n"); } return display.ToString(); } } public class CheesePizza : Pizza { public CheesePizza() : base("Cheese Pizza", "Regular Crust", "Marinara Pizza Sauce") { Toppings.Add("Fresh Mozzarella"); Toppings.Add("Parmesan"); } } public class VeggiePizza : Pizza { public VeggiePizza() : base("Veggie Pizza", "Crust", "Marinara sauce") { Toppings.Add("Shredded mozzarella"); Toppings.Add("Grated parmesan"); Toppings.Add("Diced onion"); Toppings.Add("Sliced mushrooms"); Toppings.Add("Sliced red pepper"); Toppings.Add("Sliced black olives"); } } public class PepperoniPizza : Pizza { public PepperoniPizza() : base("Pepperoni Pizza", "Crust", "Marinara sauce") { Toppings.Add("Sliced Pepperoni"); Toppings.Add("Sliced Onion"); Toppings.Add("Grated parmesan cheese"); } } public class ClamPizza : Pizza { public ClamPizza() : base("Clam Pizza", "Thin crust", "White garlic sauce") { Toppings.Add("Clams"); Toppings.Add("Grated parmesan cheese"); } } #endregion }
实现要点和效果
简单工厂模式把变化集中到工厂中,每次新加一种品种,就要在工厂方法中做相应的修改。
简单工厂模式,每次要使用的时候,必需要知道事先约定好的,区别每个产品的标志符。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
缺点
由于工厂类集中了所有实例的创建逻辑,很容易违反低耦合的设计原则。将全部创建逻辑都集中在了一起,使得逻辑变得十分复杂,而且当有新产品加入时,会进行大量代码的修改工作,对系统的扩展和维护也非常不利。这也正是与开放-封闭原则相对立的,所以为了更好的解耦合出现了工厂方法模式。
所有用简单工厂的地方,都可以考虑用发射技术来去除switch或if,解除分支判断带来的耦合。
总结
在简单工厂模式中。工厂类是整个模式的关键所在,它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来,仅仅需要负责"消费"对象就可以了,而不必管这些对象究竟是如何创建的具体细节,这样就明确区分了各自的职责和权力,有利于整个软件体系结构的优化。