解读设计模式----简单工厂模式(SimpleFactory Pattern),你要什么我就给你什么

本文首发于博客园,地址:http://www.cnblogs.com/beniao/archive/2008/08/09/1263318.html 

 

一、模式概述

     从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现,学习了此模式可以为后面的很多中模式打下基础。那好,我们就来了解下什么是简单工厂模式?

 

     我们来分析一个现实生活中的案例,每天早晨起床洗唰后是干什么呢?吃早餐(这里只针对在外吃早餐的上班族)、坐车(我是穷人只有坐公交,当然很多有钱人都自己有车,这里不考虑这些)去公司上班、是这样的吗?OK,下面就来分析下吃早餐中的故事,大家先看看下面这个图:

                      

     当我们在买早餐的时候,早餐店里都卖得写什么呢?这点你有注意吗?众多食品摆在那里,你只对营业员说你要何种食品,他便会知道给你拿什么样的食品给你,这说明什么呢?如果用面向对象的思想来理解的话,营业员在这里就充当了一个工厂的角色,他负责根据你的请求返回你需要的食品对象。而这一点正是简单工厂模式的意图。

 

二、模式意图

     简单工厂模式根据提供给他的数据,返回几个可能类中的一个类的实例。

 

三、模式UML图     

     下面是简单工厂模式的示意性UML图:

                

     如上图,简单工厂模式UML我画了两种,详细如下:

     ① 只有一个产品对象的简单工厂模式。 

     ② 带有一个抽象产品对象的简单工厂模式。

 

四、模式参与者

      工厂(Factory)角色:接受客户端的请求,通过请求负责创建相应的产品对象。

      抽象产品(AbstractProduct)角色: 是工厂模式所创建对象的父类或是共同拥有的接口。可是抽象类或接口。

      具体产品(ConcreteProduct)对象:工厂模式所创建的对象都是这个角色的实例。

 

五、模式实现

     我们通过上面的分析,已经清晰的知道了工厂模式中的各种角色和职责,那工厂模式通过代码是怎么实现的呢?OK,下面将继续分析上面的吃早餐中的故事,做一个简单的示例实现。

     1、首先我们来看看只有一个产品对象的简单工厂模式的实现。其实这很好理解,就当店里只卖一种食品,这里以馒头为例。

 1 /// <summary>
 2 /// 馒头
 3 /// </summary>
 4 public class SteamedBread
 5 {
 6     /// <summary>
 7     /// 构造方法
 8     /// </summary>
 9     public SteamedBread()
10     { }
11 
12     /// <summary>
13     /// 销售价格
14     /// </summary>
15     private double price=0.5;
16     public double Price
17     {
18         get { return price; }
19         set { price = value; }
20     }
21 }

     

     OK,产品对象建立好了,下面就是创建工厂(Factory)对象了。

 1 /// <summary>
 2 /// 工厂角色
 3 /// </summary>
 4 public class Factory
 5 {
 6     /// <summary>
 7     /// 创建一个馒头(SteamedBread)对象
 8     /// </summary>
 9     /// <returns></returns>
10     public static SteamedBread CreateInstance()
11     {
12         return new SteamedBread();
13     }
14 }

 

     此时,客户端可以这样来调用:

1 public class Client
2 {
3     public static void Main(string[] args)
4     {
5         //通过工厂创建一个产品的实例
6         SteamedBread sb = Factory.CreateInstance();
7         Console.WriteLine("馒头{0}元一个!", sb.Price);
8     }
9 }

 

     如上就完成了一个简单工厂模式的简单实现,一个产品和一个工厂,工厂负责创建这个产品。但是者种实现有一定的缺陷,为每一种产品创建一个静态方法来完成产品对象的创建,如果有多个产品则需要定义多个静态方法分别返回不同的对象,用设计原则来说的话这样的实现不符合依赖倒置原则(DIP)。如果系统里只有一个单独的产品对象,那么采用这种实现是完全可以的。UML图如下:

                            

 

     2、带抽象产品(AbstractProduct)角色的简单工厂模式实现

     从上面分析中得知,这种实现得为每一种产品创建一个静态方法来完成产品对象的创建。虽然带有简单工厂的性质,但是又好象不能够完全体现出简单工厂模式的意图。简单工厂的意图是:根据提供给他的数据,返回几个可能类中的一个类的实例根据意图出发进行分析,要实现完全满足简单工厂模式意图的程序,也就得根据提供给工厂的数据,让工厂根据这个数据来进行判断,然后返回相应的对象。OK,看看是下面这样的吗?

                          

     示意性代码如下:

 1 /// <summary>
 2 /// 食品接口----扮演抽象产品角色
 3 /// </summary>
 4 public interface IFood
 5 {
 6     /// <summary>
 7     /// 每种食品都有销售价格,这里应该作为共性提升到父类或是接口来
 8     /// 由于我们只需要得到价格,所以这里就只提供get属性访问器
 9     /// </summary>
10     double price{get;}
11 }
12 ------------------------------------------------------------------------------------
13 /// <summary>
14 /// 馒头
15 /// </summary>
16 public class SteamedBread:IFood
17 {
18     /// <summary>
19     /// 构造方法
20     /// </summary>
21     public SteamedBread()
22     { }
23 
24     public double price
25     {
26         get
27         {
28             return  0.5;
29         }
30     }
31 }
32 ------------------------------------------------------------------------------------
33 /// <summary>
34 /// 包子
35 /// </summary>
36 public class SteamedStuffed:IFood
37 {
38     public SteamedStuffed()
39     { }
40 
41     /// <summary>
42     /// 销售价格
43     /// </summary>
44     public double price
45     {
46         get
47         {
48             return  0.6;  //0.6元一个
49         }
50     }
51 }
52 ------------------------------------------------------------------------------------
53 /// <summary>
54 /// 工厂角色
55 /// </summary>
56 public class Factory
57 {
58     /// <summary>
59     /// 创建一个馒头(SteamedBread)对象
60     /// </summary>
61     /// <returns></returns>
62     public static IFood CreateInstance(string key)
63     {
64         if (key == "馒头")
65         {
66             return new SteamedBread();
67         }
68         else
69         {
70             return new SteamedStuffed();
71         }
72     }
73 }
74 ------------------------------------------------------------------------------------
75 public class Client
76 {
77     public static void Main(string[] args)
78     {
79         //通过工厂创建一个产品的实例
80         IFood food = Factory.CreateInstance("馒头");
81         Console.WriteLine("馒头{0}元一个!", food.price);
82 
83         food = Factory.CreateInstance("包子");
84         Console.WriteLine("包子{0}元一个!", food.price);
85     }
86 }

 

     此时的设计就已经完全符合简单工厂模式的意图了。顾客(Client)对早餐店营业员(Factory)说,我要“馒头”,于是营业员便根据顾客所提供的数据(馒头),去众多食品中找,找到了然后就拿给顾客。

 

     3、模式的演变实现

     有些情况下Simple Factory可以由抽象产品角色扮演,一个抽象产品类同时是子类的工厂。也就是说,抽象产品角色扮演两种角色和职责,出了基本的定义还还兼任工厂角色的职责,负责产品的创建工作。这里我们在上面的例子基础上适当修改一下OK了,新建立一个抽象类(Evolution):

 1 /// <summary>
 2 /// 兼任抽象产品角色和工厂角色两种角色
 3 /// </summary>
 4 public abstract class Evolution
 5 {
 6     /// <summary>
 7     /// 共性字段
 8     /// </summary>
 9     private double price;
10     public double Price
11     {
12         get { return price; }
13         set { price = value; }
14     }
15 
16 
17     public static Evolution CreateInstance(string key)
18     {
19         if (key == "馒头")
20         {
21             return new SteamedBread();
22         }
23         else
24         {
25             return new SteamedStuffed();
26         }
27     }
28 }

 

     那现在,具体的产品对象的设计也应该修改了,把原来实现于IFood接口改为继承此抽象类,如下:

 1 public class SteamedBread : Evolution
 2 {
 3     public SteamedBread()
 4     {
 5         this.Price = 0.5;  //在构造方法里初始话属性的值
 6     }
 7 }
 8 
 9 public class SteamedStuffed : Evolution
10 {
11     public SteamedStuffed()
12     {
13         this.Price = 0.6;
14     }
15 }

     

     通过上面的演化,此时客户端的调用如下:

1 public class Client
2 {
3     public static void Main(string[] args)
4     {
5         Evolution el = Evolution.CreateInstance("包子");
6         Console.WriteLine("包子{0}元一个!", el.Price);
7     }
8 }

 

     UML草图如下:

                                       

     在实际的开发中,这种演化是很适用的,可以说是一种编程技巧吧。

 

     4、模式的其他演变

     如果系统中只有唯一的一个具体产品对象,那么可以省略抽象产品角色。这一种其实也就是本钱前面的第一种模式实现。

     如果抽象产品角色省略,那么工厂角色就可以与具体产品角色合并。也就是说一个产品类就是自身的工厂。这样把原有三个独立的角色:抽象产品角色、具体产品角色和工厂角色合并为一个,这个类自己负责创建自己的实例。

 

     注:以上演变可以从上面的程序代码中直接修改而来,这里我就不贴代码了。   

 

六、模式优缺点

      工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅"消费"产品。简单工厂模式通过这种做法实现了对责任的分割。

      当产品有复杂的多层等级结构时,工厂类只有自己,以不变应万变,就是模式的缺点。因为工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

      系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂,违背了"开放--封闭"原则(OCP).另外,简单工厂模式通常使用静态工厂方法,这使得无法由子类继承,造成工厂角色无法形成基于继承的等级结构。

 

七、相关模式

      工厂方法模式:每个产品由一个专门的工厂来负责创建。是一种只有唯一一个产品的实现,带有简单工厂的性质。

      抽象工厂模式:大致和工厂方法相同。

      单例模式:单例的实现和上面模式演变中的最后一种很相似,只要把构造器私有便OK。

 

八、参考资料

     Addison-Wesley,1995,p.185. 中文版:《设计模式:可复用的面向对象软件的基础》 李英军等译.

     Alan Sharroway & James r.Trott.中文版:《设计模式精解

     张逸  著《软件设计精要与模式》


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/453676.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Memcached常用操作

memcached是一个高性能的、分布式内存对象缓存系统&#xff0c;应用广泛。 通过缓存数据库查询结果&#xff0c;减少数据库访问次数&#xff0c;以提高动态Web应用的速度、 提高可扩展性。 它可以应对任意多个连接&#xff0c;使用非阻塞的网络IO。由于它的工作机制是在内存中开…

android自定义金额输入键盘_Android 自定义控件 - 仿支付宝数字键盘

原标题&#xff1a;Android 自定义控件 - 仿支付宝数字键盘简介在一些带有支付功能的 App 中&#xff0c;输入的密码一般只能是纯数字&#xff0c;虽然我们可以指定 EditText 输入框只能输入数字&#xff0c;但是为了提供用户的使用体验&#xff0c;我们往往更倾向于使用自定义…

博客目录(python相关)

python 相关 文件格式相关系列 Python 第三方模块之 beautifulsoup&#xff08;bs4&#xff09;- 解析 HTML Python 第三方模块之 ElementTree&#xff08;ET&#xff09;- 解析XML文件 Python 第三方模块之 lxml - 解析 HTML 和 XML 文件 python 第三方模块 yaml - 处理 …

项目主体思索

1&#xff1a;分布式定义; 2&#xff1a;SSO集成方式 3&#xff1a;menu动态菜单的添加 4&#xff1a;tag页面展示; 5&#xff1a;tiles的jsp复用&#xff1b; 暂时就想到这些了&#xff0c;以后继续补充。转载于:https://www.cnblogs.com/siyan/p/8286738.html

centos mysql pid_centos7 mysql The server quit without updating PID file(错误解决)

1 问题[rootlocalhost mysql]# /etc/rc.d/init.d/mysql statusMySQL is not running, but lock file (/var/lock/subsys/mysql[FAILED][rootlocalhost mysql]# /etc/rc.d/init.d/mysql startStarting MySQL...The server quit without updating PID file (/usr/local/mysql/dat…

tfs文件系统之NS配置管理

NameServer简称NS 充当着客户与DS的交互桥梁 1.NS配置文件修改&#xff1a; [public] #log file size default 1GB log_size1073741824 #log file num default 64 log_num 64 #log file level default debug log_leveldebug #main queue size default 10240 task_max_queue_…

插件式架构设计实践:插件式系统架构设计简介

本系列博文将使用微软RIA技术解决方案Silverlight以及扩展性管理框架Managed Extensibility Framework&#xff08;MEF&#xff09;&#xff0c;以插件式架构设计为导线&#xff0c;分享本人在从事基于微软Silverlight技术构建的RIA系统中实施插件式系统架构设计的相关技术和经…

第十章 动态选路协议

RIP 缺陷&#xff1a; Routing Information Protocol RIP没有子网的概念在路由器或链路发生故障后&#xff0c;需要很长的一段时间才能稳定下来采用跳数作为路由度量忽略了其他一些应该考虑的因素度量最大值为15则限制了可以使用RIP的网络的大小OSPF Open Shortest Path First …

五种方式让你在java中读取properties文件内容不再是难题

2019独角兽企业重金招聘Python工程师标准>>> 方式1.通过context:property-placeholder加载配置文件jdbc.properties中的内容 <context:property-placeholder location"classpath:jdbc.properties" ignore-unresolvable"true"/> 上面的配置…

hive metastore mysql_Hive MetaStore的结构

本篇主要是介绍Hive在MySQL中存储的源数据的表结构。Hive MetaStore 数据库表结构图test.pngTBLS记录数据表的信息字段解释TBL_ID在hive中创建表的时候自动生成的一个id&#xff0c;用来表示&#xff0c;主键CREATE_TIME创建的数据表的时间&#xff0c;使用的是时间戳DBS_ID这个…

修炼一名程序员的职业水准

程序就是一系列按步骤进行的操作序列&#xff0c;它有好多种级别&#xff0c;比如最低级的微程序、次低级的汇编程序、高级的各种编程语言程序、最高级的脚本语言程序&#xff0c;也许我列的不对&#xff0c;但没关系&#xff0c;我要说的是不管是那个级别的程序&#xff0c;其…

Rails开发细节《一》

常用命令 rails new new_app cd new_app rake db:create rails server rails generate controller Blog action1 action2 rails generate scaffold Product title:string description:textrails generate model Comment commenter:string body:text post:references rake db…

latex中怎样使公式居中_LaTeX_多行公式对齐居中的同时选择性的加编号

标签: 【转载请注明出处】http://www.cnblogs.com/mashiqi 2016/10/20 一年多没写博文了。今天写一个短的,记录一下使用LaTeX的一些经验。 如何居中多行的公式呢?我试过很多种方法后,觉得下面这个最好用: 1 \begin{flalign*}2 % In this way (this arrange of &), the…

[SDOI2008]Cave 洞穴勘测

题目描述 辉辉热衷于洞穴勘测。 某天&#xff0c;他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测&#xff0c;辉辉发现这片区域由n个洞穴&#xff08;分别编号为1到n&#xff09;以及若干通道组成&#xff0c;并且每条通道连接了恰好两个洞穴。假如两个洞穴可以通…

Linux指令大全

名称&#xff1a;cat 使用权限&#xff1a;所有使用者 使用方式&#xff1a;cat [-AbeEnstTuv] [--help] [--version] fileName 说明&#xff1a;把档案串连接后传到基本输出&#xff08;萤幕或加 > fileName 到另一个档案&#xff09; 参数&#xff1a; -n 或 --number 由 …

mysql宏参数_C语言带参数的宏定义

C语言允许宏带有参数。在宏定义中的参数称为“形式参数”&#xff0c;在宏调用中的参数称为“实际参数”&#xff0c;这点和函数有些类似。对带参数的宏&#xff0c;在展开过程中不仅要进行字符串替换&#xff0c;还要用实参去替换形参。带参宏定义的一般形式为&#xff1a;#de…

自定义过滤器

首先在web.xml中对过滤器的监听 1 <!-- 自定义过滤器 -->2 <filter>3 <filter-name>AscFilter</filter-name>4 <filter-class>com.llh.filter.AscFilter</filter-class>5 </filter>6 <filter-mapping>7 …

[MS Sql Server术语解释]预读,逻辑读,物理读

在MSSQL中使用 SET STATISTICS IO ON 打开IO统计功能之后&#xff0c;每次执行完一个查询就会在下面的【消息】面板中显示本次查询IO的统计信息。 (0 行受影响) 表 demo。扫描计数 1&#xff0c;逻辑读取 622 次&#xff0c;物理读取 0 次&#xff0c;预读 0 次&#xff0c;lob…

mysql 数据库查询测试_MySQL查询测试经验

测试表geoinfo,整个表超过1100万行&#xff0c;表结构&#xff1a;CREATE TABLEgeoinfo (objectidint(11) NOT NULLAUTO_INCREMENT ,latitudedouble NOT NULL,longitudedouble NOT NULL,occupancybit(1) NOT NULL,timedatetime NOT NULL,cabidvarchar(16) NOT NULL,PRIMARY KEY…

更改阿里云域名解析台里某个域名绑定的IP之后不能解析到新IP

1.由于要撤销一组负载均衡&#xff0c;所以需要更改阿里云域名解析台里某个域名由原来绑定的负载均衡公网IP换到服务器公网IP 2.在服务器上nginx指定了域名访问&#xff0c;开启nginx服务 3.暂时关闭该组负载均衡服务 4.实现通过服务器IP可以访问项目&#xff0c;域名访问不了 …