学习设计模式第二十七 - 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设计模式(一):简单工厂模式
随机推荐
- [Idea] 在idea中使用jetty debug
1.添加jetty的maven插件 <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId> ...
- 没有了SA密码,无法Windows集成身份登录,DBA怎么办?
一同事反馈SQL无法正常登录了,以前都是通过windows集成身份验证登录进去的(sa密码早忘记了),今天就改了服务器的机器名,现在无论如何都登录不进去. SQL登录时如果采用windows集成身份验 ...
- 【Java 新建项目】使用程序对新项目的各个实体 创建Dao、DaoImpl、Service、ServiceImpl层的文件
首先给出基本Dao层代码: GenericDao.java package com.agen.dao; import java.io.Serializable; import java.util.Co ...
- Maven打包生成可运行bat/sh脚本文件
利用Maven的appassembler-maven-plugin插件,就可以实现自动打包可运行的脚本,还可以跨平台. <plugin> <groupId>org ...
- 阅读jquery源码与js依赖加载的模块化!
阅读源码肯定是先下载有注释的源码 我也是醉了,10309 行代码,在陆续续的一个月之内,看完了,虽有收获但收获不大, 直到又一次看jquery的github,怎么会有cmd????没听过使用jquer ...
- swift-string(字符串的一些语法)
1 isEmpty 返回一个布尔值,确定该字符串是否为空 2 hasPrefix(prefix: String) 函数检查给定的参数字符串是否以 string 为前缀 3 hasSuffix(suff ...
- 【Java】JDK类 CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待. 用给定的计数 初始化 CountDownLatch.由于调用了 countDown() 方法,所以在当前计数到达 ...
- 从一个控制器调到另一个控制器的[UIViewController _loadViewFromNibNamed:bundle:]崩溃
一,现象和分析: 1.崩溃的主要地方是[UIViewController _loadViewFromNibNamed:bundle:] ,是从 A 控制器 push 到 B 控制器后, B 控制器的v ...
- How to make your assembly more secure from referencing by unauthorized bits
Now the security has a trend to become more and more important in our daily work, hence I did some r ...
- LINUX 编译安装 PHP 环境
今天终于有时间 总结一下 linux 的编译安装 php 环境同学给我发了他写的文档 ,基本就可以实现编译安装了我同学文章地址: http://penghui.link/articles/2016/0 ...