学习设计模式第二十七 - 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,解除分支判断带来的耦合。
总结
在简单工厂模式中。工厂类是整个模式的关键所在,它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来,仅仅需要负责"消费"对象就可以了,而不必管这些对象究竟是如何创建的具体细节,这样就明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
学习设计模式第二十七 - GoF之外简单工厂模式的更多相关文章
- Java 设计模式系列(二)简单工厂模式和工厂方法模式
Java 设计模式系列(二)简单工厂模式和工厂方法模式 实现了创建者和调用者的分离.分为:简单工厂模式.工厂方法模式.抽象工厂模式 简单工厂模式.工厂方法模式都很简单,就不详细介绍了. 一.简单工厂 ...
- [Python设计模式] 第1章 计算器——简单工厂模式
github地址:https://github.com/cheesezh/python_design_patterns 写在前面的话 """ 读书的时候上过<设计模 ...
- 设计模式(C#)——02简单工厂模式
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来.通俗来说,你只关心怎么用,不用关心怎么做 ...
- 【2016-10-17】【坚持学习】【Day8】【简单工厂模式】
今天学习简单工厂模式, 结构 一个抽象产品 多个具体产品 一个工厂类,通过传入参数,new出不同的产品 代码: abstract class Product { //所有产品类的公共业务方法 publ ...
- Javascript设计模式理论与实战:简单工厂模式
通常我们创建对象最常规的方法就是使用new关键字调用构造函数,这会导致对象之间的依赖性.工厂模式是一种有助于消除类之间依赖性的设计模式,它使用一个方法来决定要实例化哪一个类.本文详细介绍了简单工厂模式 ...
- 设计模式(Java语言)- 简单工厂模式
简单工厂模式有称为静态工厂模式,属于设计模式中的创建型模式.简单工厂模式通过对外提供一个静态方法来统一为类创建实例.简单工厂模式的目的是实现类与类之间解耦,其次是客户端不需要知道这个对象是如何被穿创建 ...
- 设计模式(二)——Java简单工厂模式
简单工厂模式 案例: 披萨的项目(要便于披萨种类的扩展,要便于维护) 1)披萨的种类很多(比如 GreekPizz.CheesePizz 等) 2)披萨的制作有 prepare,bake, cut, ...
- PYTHON设计模式,创建型之简单工厂模式
这个系统,感觉思路清爽,,相信多练练,多思考,就会熟悉的.. http://www.jianshu.com/p/2450b785c329 #!/usr/bin/evn python #coding:u ...
- PHP设计模式(一):简单工厂模式
随机推荐
- PHP AES的加密解密
AES加密算法 密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DE ...
- Python 爬取网站资源文件
爬虫原理: 以下来自知乎解释 首先你要明白爬虫怎样工作.想象你是一只蜘蛛,现在你被放到了互联“网”上.那么,你需要把所有的网页都看一遍.怎么办呢?没问题呀,你就随便从某个地方开始,比如说人民日报的首页 ...
- C# 正则表达式总结
正则表达式 是一种匹配输入文本的模式..Net 框架提供了允许这种匹配的正则表达式引擎.模式由一个或多个字符.运算符和结构组成. 下面列出了用于定义正则表达式的各种类别的字符.运算符和结构. 字符转义 ...
- CGI与fastcgi与php-fpm与php-cgi的关系
cgi是一个协议,它规定了服务器Nginx会将那些数据传送给PHP-cgi fastcgi也可以说是一个协议.fastcgi是对cgi的性能的一次提高.fastcgi会先启动一个master,解析配置 ...
- react native 刷新机制----通知
在项目中,不知道大家有没有遇到这样的一个问题,比如说有两个页面A,B.A页面中有某个按钮点击后可以跳转到B页面,现在有一个需求就是,我在B页面中做了某些操作,然后点击回退按钮,回到A页面,A页面中的数 ...
- shell处理输入
1.在运行脚本时指定参数,直接在脚本名称后边跟随需要添加的参数,在运行的过程中,$0代表程序名,$1代表第一个参数,$2代表第二个参数,一直到第九个,从第十个参数开始需要变成${10}等,即需要添加花 ...
- InnoDB还是MyISAM 再谈MySQL存储引擎的选择
两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁.而MyISAM不支持.所以MyISAM往往就容易被人认为只适合在小项目中使用. 我作为使用MySQL的用户角度出发,Innodb和My ...
- shr 右移测试
fdword :DWORD; procedure TForm10.btn1Click(Sender: TObject); var temp:DWORD; begin fdword :=; //7866 ...
- 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...
- storm学习好文链接
大圆的那些事:http://www.cnblogs.com/panfeng412/tag/Storm/ xcc的博客:http://blog.csdn.net/damacheng/article/ca ...