解讀設計模式----簡單工廠模式(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.pswp.cn/news/453676.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453676.shtml
英文地址,請注明出處:http://en.pswp.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;域名訪問不了 …