学习设计模式第二十七 - 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设计模式(一):简单工厂模式
随机推荐
- centos 6.6 系统中配置sendmail和dovecot
网上介绍sendmail的文章千百种,很少有跟着做下来一次成功的.多少都有些说的不准确的地方. 我给大家共享一下我经过实验环境测试,完全可行的方法. 1.软件准备 操作系统:centos6.6我选择c ...
- 【Java EE 学习 75 下】【数据采集系统第七天】【二进制运算实现权限管理】【使用反射初始化权限表】【权限捕获拦截器动态添加权限】
一.使用反射动态添加权限 在该系统中,我使用struts2的时候非常规范,访问的Action的形式都是"ActionClassName_MethodName.action?参数列表" ...
- C++ 系列:多线程资源收集
Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...
- PL/SQL连接Oracle数据库,中文乱码,显示问号
问题描述: 登陆PL/SQL,执行SQL语句后,输出的中文标题显示成问号????:条件包含中文,则无数据. 如果不是中文,需要修改注册表值,方法如下: 进入注册表:Win+r,输入re ...
- 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载
一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...
- webrtc中APM(AudioProcessing module)的使用
一,实例化和配置 AudioProcessing* apm = AudioProcessing::Create(0); //这里的0指的是channelID,只是一个标注那个通道的表示 apm-> ...
- cookies插件,记住cookies
今天同事交给了我一个记住cookies插件,首先先去网上下载一个jquery.cookie.js文件文件下载 <!DOCTYPE html PUBLIC "-//W3C//DTD HT ...
- #pg学习#postgresql的安装
1.按照官网给的步骤编译安装(Mac安装是比较容易的,相比Liunx) cd /Users/renlipeng/Desktop/postgresql-9.5.1 ./configure --prefi ...
- Idea 实时编译 和 热部署
实时编译 idea自动保存编写好的文件,但是不会编译,想要编译需要按ctrl+F9(编译整个项目)ctrl+shift+f9(单个文件),不仅麻烦而且和平常习惯也不相复合.怎么令idea的ctrl+s ...
- ubuntu 用apt-get 安装apache 和php 之后php不能解析的问题
sudo apt-get install apache2 sudo apt-get install php7.0 sudo apt-get install libapache2-mod-php //关 ...