8.7 KiB
8.7 KiB
nav | group | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
结构型
结构型模式封装的是对象之间组合方式的变化,目的在于灵活地表达对象间的配合与依赖关系;
适配器模式
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
案例:新来的大卫封装了个 fetch 请求库,但是公司以前的网络请求方法是基于 XMLHttpRequest 的,老板想大卫去改这已经写好的9999个接口的请求,大卫使用了适配器模式去兼容,如下:
// 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 原则
简单版
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 (
<div className="decorator">
<button style={style} onClick={openModal} >打开弹框</button>
<button style={style} onClick={hideModal} >关闭弹框</button>
<button style={style} onClick={decoratorModal} >添加适配器</button>
<button style={style} onClick={normalModal} >清除适配器</button>
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div>
</div>
)
};
进阶版
import React, { useRef, useEffect } from 'react';
function funcDecorator(type) {
return function (target, name, descriptor) {
if (type === 'class') {
target.prototype.show = () => {
console.log('装饰器处理后的类')
}
return target
/**
* or
* return class NButton {
* show() {
* console.log('装饰器处理后')
* }
* }
**/
} else if (type === 'function') {
const old = descriptor.value
descriptor.value = function(...arg) { // 注意这里需要保留原this作用域,不能使用箭头函数
console.log('----装饰器装饰函数----')
// 原函数
return old.apply(this, arg)
}
}
}
}
// 通过装饰器改变原有的 show 方法
// @funcDecorator('class')
class Button {
show() {
console.log('大卫的思想空间')
}
@funcDecorator('function')
mb() {
console.log('我是sb')
}
}
export default () => {
useEffect(() => {
let dom = new Button();
// dom.show()
// dom.mb()
// console.log(dom)
}, [])
return (
<div>
进阶案例:控制台查看输出结果
</div>
)
}
代理模式
是为一个对象提供一个代用品或占位符,以便控制对它的访问
简单版
import React, { useRef, useEffect } from 'react';
// 普通私密信息
const baseInfo = ['name', 'age', 'career']
// 最私密信息
const privateInfo = ['avatar', 'phone']
// 规定礼物的数据结构由type和value组成
const present = {
type: '巧克力',
value: 60,
}
// 相亲男方
const user = {
isValidated: true,
isVIP: false,
}
// 相亲女方
const girl = {
// 姓名
name: '小美',
// 自我介绍
aboutMe: '...',
// 年龄
age: 24,
// 职业
career: 'teacher',
// 假头像
fakeAvatar: 'xxxx',
// 真实头像
avatar: 'xxxx',
// 手机号
phone: 123456,
// 礼物数组
presents: [],
// 拒收50块以下的礼物
bottomValue: 50,
// 记录最近一次收到的礼物
lastPresent: present,
}
// 掘金婚介所推出了小礼物功能
const JuejinLovers = new Proxy(girl, {
get: function(girl, key) {
if((baseInfo.indexOf(key) !== -1) && !user.isValidated) {
alert('您还没有完成验证哦')
return
}
// 此处我们认为只有验证过的用户才可以购买VIP
if(user.isValidated && privateInfo.indexOf(key) !== -1 && !user.isVIP) {
alert('只有VIP才可以查看该信息哦')
return
}
return girl[key]
},
set: function(girl, key, val) {
// 最近一次送来的礼物会尝试赋值给lastPresent字段
// 需要返回 boolean 判断是否赋值成功
if(key === 'lastPresent') {
if(val.value < girl.bottomValue) {
alert('sorry,您的礼物被拒收了')
return false
}
// 如果没有拒收,则赋值成功,同时并入presents数组
girl.lastPresent = val
girl.presents = [...girl.presents, val]
return true
}
}
})
export default () => {
useEffect(() => {
console.log(JuejinLovers.name)
JuejinLovers.lastPresent = present
console.log(JuejinLovers)
}, [])
return (
<div>hi</div>
)
}