学习设计模式第二十七 - GoF之外简单工厂模式
示例代码来自《深入浅出设计模式》和《大话设计模式》
概述
简单工厂模式又被称为静态工厂模式,属于类的创建型模式。其实质是由一个工厂类根据传入的参量,动态决定应该创建出哪一个产品类的实例。
意图
专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
UML
图1 简单工厂模式的UML图
参与者
这个模式涉及的类或对象:
-
Creator
-
它的角色就是工厂,负责生产各种产品。
-
Product
-
它的角色是产品,是对所有产品的一个统称。在实现过程中,它是具体产品的公共基类。
-
ConcreteProduct
-
它的角色是具体产品,它是每一种产品的具体实现。
来自《大话设计模式》的例子
这是一个很简单的计算器的例子,所有的计算工作被抽象成一个产品对象,而加或减这样一个具体计算被设计为一个具体产品。同时一个工厂类根据用户输入的不同返回具体的计算对象。
例子中涉及到的类与简单工厂模式中标准的类对应关系如下:
-
Product – Operation
-
ConcreteProduct – OperationAdd,OperationSub,OperationMul等
-
Creator – OperationFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | 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图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | 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,解除分支判断带来的耦合。
总结
在简单工厂模式中。工厂类是整个模式的关键所在,它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来,仅仅需要负责"消费"对象就可以了,而不必管这些对象究竟是如何创建的具体细节,这样就明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
2014-04-06 学习设计模式第九 - 适配器模式
2013-04-06 WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形
2011-04-06 .Net学习难点讨论系列15 - 小技巧总结