設計模式之適配器模式

適配器的定義

將一個類的藉口轉換爲用戶希望的另一個接口。適配器模式,讓那些接口不兼容的類的可以一起工作

適配器模式別名: 包裝模式, 它即可以作爲類解構模式,也可以使用對象解構模式。

在適配器模式定義中所提及的接口是指廣義的接口,它可以表示一個方法或者方法的集合。

適配器模式的優點

  • 將目標接口和適配接口解耦,通過引用一個適配器接口來重用現有的接口,無需修改原有接口
  • 增加了類的透明性合複用性, 將具體的業務實現封裝在適配器接口中,對於客戶端而言,是透明的,而且提高了適配着的複用性
  • 靈活性和擴展性都很好,通過使用配置文件,可以很方便的更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器完全符合開閉原則

適配器的缺點

  • 一次只能適配一個需要適配的接口,不能同時適配多個
  • 過多的使用適配器,會讓系統非常零亂,不易整體進行把握。

示例Demo

這裏用筆記本電腦的電源適配器作爲一個Demo。通常我們的標準電壓是220v, 筆記本電腦的額定電壓是19.5v,標準的電壓過大,所以我們需要一個適配器,將電壓轉換爲19.5v然後在輸入給電腦使用。

代碼實現思路:

  1. 創建電源類Power, 默認電源 charge 等於220v
  2. 穿件適配器ADptor, 在內部接口電壓,保存爲power, 自定義charge方法,在內部進行轉換,然後輸出結果
  3. 創建筆記本實例Notepad, 並將適配器實例化後保存在Notepad類的實例上
  4. 創建Notepad的實例,調用use方法
// 適配器模式
// 1. 電源實例
class Power {
	charge () {
		return  '220w'
	}
}

// 2. 適配器
class Adptor {
	constructor () {
		this.power = new Power()
	}
	charge () {
		let v = this.power.charge
		return `${v()} => 19.5v`
	}
}

// 3. 筆記本電腦類
class Notepad {
	constructor () {
		this.adptor = new Adptor
	}
	use () {
		console.log(this.adptor.charge())
	}
}
// 4. 筆記本電腦實例
let client = new Notepad()
client.use()


使用場景

  • 接口參數適配

    /*
    1. 參數適配
    2. 返回值的適配
     */
    function ajax(options) {
    	let defaultOptions = {
    		method: 'GET',
    		dataType: 'json'
    	}
    	// es5 寫法
    	/*for (let key in  options) {
    		defaultOptions[key] = options[key] || defaultOptions[key]
    	}*/
    	// es6 寫法
    	defaultOptions = { ...defaultOptions, ...options}
    	console.log(defaultOptions)
    }
    ajax({
    	url: 'http://abc.com',
    	method: 'POST',
    	success: function (data) {}
    })
    
    
  • 將普通函數promiseify

    // promisify
    /*
    需求: 我們現在經常使用promise方法,但是並不是所有的方法都支持promise回調,
    我們就可以自己封裝一個promsieify函數,將方法變成promsie函數
    1. 使用fs.readFile左右做demoe、
     */
    const fs = require('fs')
    /**
     * @desc 將普通函數轉化爲promsie
     * @return {function} 返回包裝後的promise函數
     */
    function promisify (fn) {
    	return function (...args) {
    		return new Promise(function (resolve, reject) {
    			fn(...args, function (err, ...args) {
    				if (err) reject(err)
    				resolve(res)
    			})
    		})
    	}
    
    }
    
    let readFile = promisify(fs.readfile)
    (async function read () {
    	let one = await readFile('1.txt', 'utf8')
    	let two = await readFile('2.txt', 'utf8')
    	let three = await readFile('3.txt', 'utf8')
    
    	console.log(one, two, three)
    })()
    
    
  • 代碼兼容處理

    // 開發中總是會有恨的老的代碼,然後我們隨着時間的推移,很多東西已經不用之前的庫了,我們爲了不改之前的改之前的代碼,可以自己寫一個適配
    // 假設我們使用jQuery的ajax請求,現在改用fetch
    
    // 設置代理
    window.$ = {
    	ajax (options) {
    		return fetch({
    			method: options.type || 'GET',
    			body: JSON.stringify(options.data || {}),
    			responseType: options.responseType
    		})
    	}
    }
    
    // 調用
    $.ajax({
    	type: 'get',
    	responseType: 'json',
    	data: {a: 'a', b: 'B'}
    })
    
    
  • vue中的計算屬性

    <!DOCTYPE html>
    <html>
    <head>
    	<title>適配器模式示例</title>
    </head>
    <body>
    	<div id="root">
    		<p>{{title}}</p>
    		<p>{{great}}</p>
    	</div>
    	<!-- 生產環境版本,優化了尺寸和速度 -->
    	<script src="https://cdn.jsdelivr.net/npm/vue"></script>
    	<script type="text/javascript">
    		let app = new Vue({
    			el: '#root',
    			data: {
    				title: 'Hello World',
    				name: 'leo'
    			},
    			computed: {
    				// 計算屬性就是一種適配器模式的應用
    				great () {
    					return this.title.split(' ')[0] + ' ' + this.name
    				}
    			}
    		})
    	</script>
    </body>
    </html>
    
    

總結

多學習,反正不管怎麼樣,頭髮都會越來越少。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章