--- nav: title: 前端 path: /fea group: title: 💊 设计模式 order: 4 --- ## 结构型 结构型模式封装的是对象之间组合方式的变化,目的在于灵活地表达对象间的配合与依赖关系; ### 适配器模式 将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。 案例:新来的大卫封装了个 **fetch** 请求库,但是公司以前的网络请求方法是基于 **XMLHttpRequest** 的,老板想大卫去改这已经写好的9999个接口的请求,大卫使用了适配器模式去兼容,如下: ```js // RequestUtil 请求库 export default class RequestUtil { // get 方法 static get(url) { return new Promise((resolve, reject) => { fetch(url) .then(response => response.json()) .then(result => { resolve(result) }) .catch(err => reject(err)) }) } // post 方法 static post(url, data) { return new Promise((resolve, reject) => { fetch(url, { method: 'POST', headers: { 'Accept': 'application/json' }, body: data }) .then(response => response.json()) .then(result => resolve(result)) .catch(err => reject(err)) }) } } // 调用 const postResp = await RequestUtils.post('https://nicecoders.github.io', data) // 公司老的库 function Ajax(type, url, data, success, failed){ // 创建ajax对象 var xhr = null; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject('Microsoft.XMLHTTP') } ...(此处省略一系列的业务逻辑细节) var type = type.toUpperCase(); // 识别请求类型 if(type == 'GET'){ if(data){ xhr.open('GET', url + '?' + data, true); //如果有数据就拼接 } // 发送get请求 xhr.send(); } else if(type == 'POST'){ xhr.open('POST', url, true); // 如果需要像 html 表单那样 POST 数据,使用 setRequestHeader() 来添加 http 头。 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 发送post请求 xhr.send(data); } // 处理返回数据 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status == 200){ success(xhr.responseText); } else { if(failed){ failed(xhr.status); } } } } } // 创建适配器函数,入参与旧接口保持一致 async function AjaxAdapter(type, url, data, success, failed) { const type = type.toUpperCase() let result try { // 实际的请求全部由新接口发起 if(type === 'GET') { result = await HttpUtils.get(url) || {} } else if(type === 'POST') { result = await HttpUtils.post(url, data) || {} } // 假设请求成功对应的状态码是1 result.statusCode === 1 && success ? success(result) : failed(result.statusCode) } catch(error) { // 捕捉网络错误 if(failed){ failed(error.statusCode); } } } // 兼容了老的调用方式 Ajax('get', 'https://nicecoders.github.io', data, function(res){ // 成功的回调逻辑 }, function(error){ // 失败的回调逻辑 }) ``` ### 装饰器模式 - 动态地给某个对象添加一些额外的职责,,是一种实现继承的替代方案 - 在不改变原对象的基础上,通过对其进行包装扩展,使原有对象可以满足用户的更复杂需求,而不会影响从这个类中派生的其他对象 - 遵循拓展不修改的 SOLID 原则 #### 简单版 ```jsx import React, { useRef, useState, useEffect } from 'react'; import '@nicecode/css' class Modal { constructor(opt = {}) { const { dom } = opt this.dom = dom } show() { this.dom.innerHTML = '卧槽'; this.dom.style.display = 'block' this.dom.style.width = '200px' this.dom.style.textAlign = 'center' } hide() { this.dom.style.display = 'none'; } } class DecoratorModal { constructor(_oldModal) { this._oldModal = _oldModal } show() { this._oldModal.show() this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角' this._oldModal.dom.style.color = '#aaa' this._oldModal.dom.style.borderRadius = '5px' } hide() { this._oldModal.hide() } } export default () => { const modalRef = useRef(null) const [modal, setModal] = useState(null) // 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录 const openModal = () => { modal.show() } const hideModal = () => { modal.hide() } const decoratorModal = () => { let dom = new DecoratorModal(modal) setModal(dom) } const normalModal = () => { let dom = new Modal({ dom: modalRef.current }) setModal(dom) } useEffect(() => { normalModal() }, []) let style = { margin: '0 6px', } return (