学习设计模式第二十七 - 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,解除分支判断带来的耦合。

 

总结

在简单工厂模式中。工厂类是整个模式的关键所在,它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来,仅仅需要负责"消费"对象就可以了,而不必管这些对象究竟是如何创建的具体细节,这样就明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

posted @   hystar  阅读(595)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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 - 小技巧总结
点击右上角即可分享
微信分享提示