Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
廠方法模式
工廠方法模式是類的創建模式。工廠方法模式的用意是定義一個創建產品對象的工廠接口,將實際創建工廠推遲到子類中。
工廠方法模式
工廠方法模式是對簡單工廠模式進一步抽象的結果。
假如是不使用反射的工廠方法模式,那么所有的if...
else if...else都放在工廠類中,勢必造成工廠類的無限臃腫
這時候就需要工廠方法模式來處理這個問題了。工廠方法模式中,核心的工廠類不再負責所有對象的創建,而是將具體的創建工作交給子類去做。這個類則搖身一變變成了一個抽象工廠角色,僅僅負責給出具體工廠子類必須實現的接口。
這一步的改進,使得系統可以在不修改具體工廠角色的情況下引入新的產品。
工廠方法模式結構
使用工廠方法模式的系統涉及到以下角色:
1、抽象工廠角色
擔任這個角色的是工廠方法模式的核心,任何在模式中創建對象的工廠類必須實現這個接口
2、具體工廠角色
擔任這個角色的是實現了工廠接口的具體Java類,具體工廠角色與業務密切相關,并且隨著使用者的調用以創建導出類
3、抽象導出角色
工廠方法模式所創建的對象的超類
4、具體導出角色
這個角色實現了抽象導出角色所聲明的接口,工廠方法模式所創建的每一個對象都是某個具體角色導出角色的實例
工廠方法模式的示例
有一個抽象工廠,導出文件的工廠:
它有兩個實現類,分別是導出HTML文件的工廠和導出PDF文件的工廠:
抽象產品角色,一個導出文件:
具體產品角色:
模擬客戶端調用,實例化出一個具體的工廠角色,根據傳入的參數返回不同的產品角色:
返回結果為:
導出財務版HTML文件
當然,也可以修改代碼,看自己喜好,實例化出不同的子類并且調用export方法打印。
工廠方法模式在Java中的應用及解讀
拿ThreadFactory舉個例子,顧名思義,這是一個生產線程的接口:
具體的線程工廠可以implements這個接口并實現newThread(Runnable r)方法,來生產具體線程工廠想要生產的線程。JDK在Executors給開發者提供了一個靜態內部類DefaultThreadFactory,當然開發者也可以自行實現這個接口,寫自定義的線程工廠。
總結
對于系統的設計應該足夠靈活并盡可能降低代碼之間的耦合度,當修改或增加一個新的功能時,使得使用者盡可能修改少的地方即可。假如設計不夠靈活,將無法面對多變的需求,可能一個極小的需求變更,都會使代碼結構發生改變,并導致其他使用的人都要修改他們的代碼。牽一發而動全身,系統日后的維護將變得艱難。
ava設計模式
本系列文章共23篇,詳細介紹GOF (Gang Of Four)所定義的23種設計模式。共分為三大類對應標題中的3大招,每類中的每一種設計模式對應3大招中的某一式:
第1招-創建型(共5式):單例模式,工廠方法模式,抽象工廠模式,建造者模式,原型模式;
第2招-行為型(共11式):觀察者模式,策略模式,命令模式,狀態模式,解釋器模式,迭代器模式,中介者模式,訪問者模式,備忘錄模式,責任鏈模式,模板方法模式;
第3招-結構型(共7式):適配器模式,外觀模式,代理模式,裝飾者模式,組合模式,橋接模式,享元模式。
UML (Unified Modeling Language, 統一建模語言):9大圖——構件圖、類圖、對象圖、序列圖、協作圖、活動圖、狀態機圖、用例圖、部署圖;6關系——泛化=實現>組合>聚合>關聯>依賴。
OO(Object-Oriented, 面向對象):面向對象的程序設計方法將數據及對數據的操作封裝在一起形成一個相互依賴不可分離的整體,即對象;對同類型的對象抽象出其共性形成類。
定義
應用場景
優缺點
Java代碼實例
小結
工廠方法模式:對象的工廠,將類的實例化過程延遲到子類進行。
說到工廠模式就不得不提一下工廠模式三姐妹,簡單工廠模式、工廠方法模式和抽象工廠模式。其中簡單工廠模式存在諸多違背設計原則的問題,未被列入23種設計模式。而工廠方法模式解決了這些問題,特別是“開放關閉原則”,提供了工廠類的抽象,實現了可擴展性。抽象工廠模式不是本文的重點,留到下節介紹。
工廠方法模式涉及到四個角色:具體產品、抽象產品、具體工廠、抽象工廠。
工廠方法模式UML圖
產品種類不確定,后期可能添加新的產品時;
當前類并不知道需要創建的是什么子類時;
不想在父類進行實例化工作,想讓子類決定時。
優點:
良好的擴展性,當需要添加新的產品時無需修改原來的類,只需添加一個新的產品類和一個工廠類即可;
代碼結構清晰,產品類是產品類,工廠負責創建,客戶端無需知道各種細節,解耦了產品的創建過程;
增加了維護性,將創建過程放到了工廠類里,某一產品的創建發生變化時不會影響產品本身;
缺點:
類的個數爆發式增長,每添加一種產品就需要添加兩個類,如果產品過多則導致類過多,增加類定義的內存開銷;
在實現工廠方法模式這前我們先來看一下簡單工廠模式:
1.創建抽象產品IPhone;
2.創建具體產品MiPhone和ApplePhone;
抽象產品和具體產品
3.創建簡單工廠PhoneFactory;
4.使用工廠類進行創建產品;
簡單工廠類及客戶端類
實現工廠方法模式需要四步:
1.定義產品接口;
2.添加具體產品實現;
抽象產品及具體產品
3.定義創建工廠接口;
4.添加具體工廠實現;
抽象工廠和具體工廠
5.客戶端使用。
客戶端使用
工廠模式三姐妹中最常用的是簡單工廠模式,但簡單工廠模式違背了設計原則中的開放關閉原則。而工廠方法模式解決了此問題,并且完美使用了里氏替換原則和依賴倒置原則。對于可能出現多種類別的實例實現時,建議使用工廠方法模式進行解耦。
工廠模式(Factory Pattern),根據不同的名稱輸入返回不同類的實例,一般用來創建同一類對象。工廠模式的主要思想是將對象的創建與對象的實現分離。
在類似場景中,這些例子有以下特點:
工廠模式分為簡單工廠模式、工廠方法模式、抽象工廠模式。
簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例
簡單工廠模式包含如下角色:
類圖: AmericanoCoffee、LatteCoffee和CappuccinoCoffee都是繼承Coffee
代碼
abstract class Coffee {
constructor(public name: string) {
}
}
class AmericanoCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class LatteCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class CappuccinoCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class Café {
static order(name: string) {
switch (name) {
case 'Americano':
return new AmericanoCoffee('美式咖啡');
case 'Latte':
return new LatteCoffee('拿鐵咖啡');
case 'Cappuccino':
return new LatteCoffee('卡布奇諾');
default:
return null;
}
}
}
console.log(Café.order('Americano'));
console.log(Café.order('Latte'));
console.log(Café.order('Cappuccino'));
jQuery
class jQuery{
constructor(selector){
let elements = Array.from(document.querySelectorAll(selector));
let length = elements?elements.length:0;
for(let i=0;i<length;i++){
this[i]=elements[i];
}
this.length = length;
}
html(html){
if(html){
this[0].innerHTML=html;
}else{
return this[0].innerHTML;
}
}
}
window.$ = function(selector){
return new jQuery(selector);
}
和原生的 document.createElement 類似,Vue 和 React 這種具有虛擬 DOM 樹(Virtual Dom Tree)機制的框架在生成虛擬 DOM 的時候,都提供了 createElement 方法用來生成 VNode,用來作為真實 DOM 節點的映射
// Vue
createElement('h3', { class: 'main-title' }, [
createElement('img', { class: 'avatar', attrs: { src: '../avatar.jpg' } }),
createElement('p', { class: 'user-desc' }, '放棄不難,但堅持一定很酷')
])
// React
React.createElement('h3', { className: 'user-info' },
React.createElement('img', { src: '../avatar.jpg', className: 'avatar' }),
React.createElement('p', { className: 'user-desc' }, '放棄不難,但堅持一定很酷')
)
createElement 函數結構大概如下:
class Vnode (tag, data, children) { ... }
function createElement(tag, data, children) {
return new Vnode(tag, data, children)
}
可以看到 createElement 函數內會進行 VNode 的具體創建,創建的過程是很復雜的,而框架提供的 createElement 工廠方法封裝了復雜的創建與驗證過程,對于使用者來說就很方便了。
工廠模式在源碼中應用頻繁,以 vue-router 中的源碼為例,代碼位置:vue-router/src/index.js
// src/index.js
export default class VueRouter {
constructor(options) {
this.mode = mode // 路由模式
switch (mode) { // 簡單工廠
case 'history': // history 方式
this.history = new HTML5History(this, options.base)
break
case 'hash': // hash 方式
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract': // abstract 方式
this.history = new AbstractHistory(this, options.base)
break
default:
// ... 初始化失敗報錯
}
}
}
稍微解釋一下這里的源碼。mode 是路由創建的模式,這里有三種 History、Hash、Abstract,前兩種我們已經很熟悉了,History 是 H5 的路由方式,Hash 是路由中帶 # 的路由方式,Abstract 代表非瀏覽器環境中路由方式,比如 Node、weex 等;this.history 用來保存路由實例,vue-router 中使用了工廠模式的思想來獲得響應路由控制類的實例。
源碼里沒有把工廠方法的產品創建流程封裝出來,而是直接將產品實例的創建流程暴露在 VueRouter 的構造函數中,在被 new 的時候創建對應產品實例,相當于 VueRouter的構造函數就是一個工廠方法。
如果一個系統不是 SPA (Single Page Application,單頁應用),而是是 MPA(Multi Page Application,多頁應用),那么就需要創建多個 VueRouter 的實例,此時 VueRouter 的構造函數也就是工廠方法將會被多次執行,以分別獲得不同實例。
簡單工廠模式是根據輸入的不同返回不同產品的實例;而工廠方式的主要思想是增加抽象工廠類,將不同產品的創建分離到不同的工廠中,工廠類的職責比較單一。
abstract class Coffee {
constructor(public name: string) {
}
}
abstract class Factory {
abstract createCoffee(): Coffee;
}
class AmericanoCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class AmericanoCoffeeFactory extends Factory {
createCoffee() {
return new AmericanoCoffee('美式咖啡')
}
}
class LatteCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class LatteCoffeeFactory extends Factory {
createCoffee() {
return new LatteCoffee('拿鐵咖啡')
}
}
class CappuccinoCoffee extends Coffee {
constructor(public name: string) {
super(name);
}
}
class CappuccinoFactory extends Factory {
createCoffee() {
return new CappuccinoCoffee('卡布奇諾')
}
}
class Café {
static order(name: string) {
switch (name) {
case 'Americano':
return new AmericanoCoffeeFactory().createCoffee();
case 'Latte':
return new LatteCoffeeFactory().createCoffee();
case 'Cappuccino':
return new CappuccinoFactory().createCoffee();
default:
return null;
}
}
}
console.log(Café.order('Americano'));
console.log(Café.order('Latte'));
console.log(Café.order('Cappuccino'));
const settings={
'Americano': AmericanoCoffeeFactory,
'Latte': LatteCoffeeFactory,
'Cappuccino': CappuccinoFactory
}
console.log(new settings('Americano').createCoffee());
console.log(new settings('Latte').createCoffee());
console.log(new settings('Cappuccino').createCoffee());
帶來了額外的系統復雜度,增加了抽象性。
那在什么時候使用工廠模式呢:
什么時候不該用工廠模式:
濫用只是增加了不必要的系統復雜度,過猶不及。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。