一、设计模式的原则
1、开闭原则:开是拓展,闭是修改
2、单一职责原则:岗位职责单一,互不重叠
3、依赖倒置原则:上层不依赖下层实现
4、接口隔离原则
5、里氏替换原则:子类可以拓展,不能改变
二、创建型(创建元素)
1、工厂模式
隐藏创建过程,暴露共同接口
例子:游戏商店里下载初始化游戏,并且可以运行游戏
class Shop{
create(name){
return new Game(name)
}
}
class Game{
constructor(name){
this.name = name
}
init(){
console.log('init')
}
run(){
console.log('run')
}
}
const shop = new Shop()
const pubg = new Game('pubg')
pubg.init()
pubg.run()
2、建造者模式
独立生产商品,拆分简单模块,独立执行,注重过程和搭配
例子:优惠套餐单元,商品和皮肤进行打折售卖
class Product {
constructor(name){
this.name = name
}
init(){
console.log('Product init')
}
}
class Skin {
constructor(name){
this.name = name
}
init(){
console.log('Skin init')
}
}
class Shop {
constructor(){
this.package = '';
}
create(name){
this.package = new PackageBuilder(name);
}
getGamePackage(){
return this.package.getPackage();
}
}
class PackageBuilder{
constructor(name){
this.game = new Product(name);
this.skin = new Skin(name)
}
getPackage(){
return this.game.init() + this.skin.init();
}
}
// 每个模块独立解耦,而建造者负责创建串联正题系统
3、单例模式:
例:vue-router、vuex
// 全局只有一个实例,不能乱
class PlayStation {
constructor(){
this.state = 'off';
}
play(){
if(this.state === 'on'){
console.log('已经在happy了')
return
}
this.state = 'on'
console.log('开始happy了')
}
shutdown(){
if(this.state === 'off'){
console.log('已经关闭')
return
}
this.state = 'off'
console.log('已经关机,请放心')
}
}
PlayStation.instance = undefined;
PlayStation.getInstance = (function(){
return function(){
if(!PlayStation.instance){
PlayStation.instance = new PlayStation();
}
return PlayStation.instance;
}()
})
cosnt ps1 = PlayStation.getInstance();
ps1.play();
cosnt ps1 = PlayStation.getInstance();
ps1.shutdown();
4、小结
模式场景:
-
批量生产同类型应用来满足频繁使用同一种类型需求时—— 工厂模式
-
当我们需要模块化拆分一个大的模块时,同时使模块间独立解耦分工—— 建造者模式
-
全局只需要一个实例,注重统一一体化—— 单例模式 实际应用:
-
button producer:生产多个本质相同,利用传参区分不同属性的元素——>使用工厂
-
全局应用 router store ->只需要一个实例->单例
-
页头组件:Header:包含了title\button\breadcun->生产多重不同赖幸的元素-建造者模式
三、结构型(代码结构)
1、适配器模式
适配器:适配独立模块,保证模块间的独立解耦且链接兼容
例:需求:买了一个港行ps,插座是国标
class HKDevice{
getPlug(){
return '港行双圆柱插头'
}
}
class Target{
constructor(){
this.plug = new HKDevice()
}
getPlug(){
return this.plug.getPlug()+'转换器'
}
}
const target = new Target();
target.getPlug();
2、装饰器模式:增强已有方案
动态将责任附加到对象上
例:设备升级
class Device{
create(){
console.log('PlayStation4')
}
}
class Phone{
create(){
console.log('iPhone18')
}
}
class Decorator{
constructor(device){
this.device = device;
}
create(){
this.device.create();
this.update(device);
}
update(device){
console.log('device +'pro'')
}
}
const device = new Device();
device.create()
const newDevice = new Decorator(device)
newDevice.create()
3、代理模式
使用代理人来替代原始对象 例:游戏防沉迷
class Game{
play(){
}
}
class Player{
constructor(age){
this.age = age
}
}
class GameProxy{
constructor(player){
this.player = player
}
play(){
return (this.player.age <16) ? "too young to play":new Game().play()
}
}
const player = new Player(18);
const game = new GameProxy(player);
game.play();
4、小结
模式场景:
- 中间转换参数,保持模块间独立的时候 ——适配器模式
- 附着于多个组件上,批量动态赋予功能的时候——装饰器模式
- 将代理对象与调用对象分离,不直接调用目标对象——代理模式
实际应用:
- 两个模块:筛选器和表格,需要做一个联动。但筛选器的数据不能直接传入表格,需要做数据结构的转换;模块之间独立,需要做数据结构转换——适配器
- 目前按钮、title、icon三个组件。希望开发一个模块,让三个组件同时具备相同的功能用一层装饰器,有统一的能力提升,且可以动态添加功能进行拓展——装饰器模式
- ul中多个li,每个li上的点击事件,利用冒泡委托,事件绑定在ul上——代理模式
四、行为型(不同的对象之间划分责任和算法的抽象化)
1、命令模式
请求以命令的形式包裹在对象中,并传给调用对象
例:对于游戏角色的控制
//接受者
class Receiver {
execute(){
console.log('角色开始奔跑')
}
}
// 触发者
class Operator {
constructor(command){
this.command = command;
}
run(){
console.log('请给我爬')
this.command.execute()
}
}
//指令器
class Command {
constructor(receiver){
this.receiver = receiver;
}
execute(){
console.log('执行命令')
this.receiver.executer()
}
}
const soldier = new Receiver();
const order = new Command(soldier);
const player = new Operator(order)
player.run();
2、模板模式
基于编排,便于拓展,在模板中定义好方法的执行步骤,方法本身关注与自己的事情
例:成功吃个鸡,要分几步
class Device{
constructor(){
}
powerOn(){
console.log('打开电源')
}
login(){
console.log('登录账号')
}
clickIcon(){
console.log('点击开始游戏')
}
enterGame(){
console.log('进入战场')
}
play(){
this.powerOn();
this.login();
this.clickIcon();
this.enterGame();
}
}
3、观察者模式
当一个属性发生状态改变时,观察者会连续引发所有的相关状态改变
例:通过智能家居一键开始游戏
class MediaCenter{
constructor(){
this.state = '';
this.observers = [];
}
attach(observer){
this.observers.push(observer)
}
getState(){
return this.state;
}
setState(state){
this.state = state;
this.notifyAllobservers();
}
notifyAllobservers(){
this.observers.forEach(ob=>{
ob.update();
})
}
}
class observer{
constructor(name,center){
this.name = name;
this.center = center;
this.center.attach(this);
}
update(){
console.log(`${this.name} update, state:${this.center.getState}`)
}
}
const center = new MediaCenter();
const ps = new Observer('ps',center)
const tv = new Observer('tv',center)
4、职责链
单向链执行,链式调用,职责独立 顺序执行
例:审批打游戏
class Action{
constructor(name){
this.name = name;
this.nextAction = null;
}
setNextAction(action){
this.nextAction = action;
}
handle(){
console.log(`${this.name}请审批`)
if(this.nextAction !== null){
this.nextAction.handle();
}
}
}
const dad= new Action('ba')
const mam= new Action('mam')
const wife= new Action('wife')
dad.setNextAction(mom)
mom.setNextAction(wife)
dad.handle();
5、小结
模式场景:
- 1、发出指令,中间层传递命令本身,命中包含执行对象——命令模式
- 2、通过模板定义执行顺序,做独立操作——模板模式
- 3、通过观察者,可以让被观察者值统一发生变化,触发相应依赖值的统一更新——观察者模式
- 4、独立职责的单元通过链式执行,逐步操作流程——职责链
实际应用
- 提交表单进行表单逐行校验,链式调用validate,依次执行——职责链
- echart准备工作:canvas config init draw(),规划顺序执行——模板模式
- 调度器在接受到一组新的数据时,解析数据,并且根据数据类型包裹在对象中传递到下级helper,再去执行响应操作——命令模式
- 输入框输入的值去判断下拉框显示与否——观察者模式
五、设计模式经典面试题:
某停车场,分3层,每层100车位。每个车位都能监控到车辆的驶入和离开,
车辆进入前,显示每层的空余车位数量;
车辆进入时,摄像头可识别车牌号和时间;
车辆出来时,出口显示器显示车牌号和停车时长。
设计一套系统。
// 车辆
class car {
constructor(num){
this.num = num
}
}
// 停车场,停车场有楼层、显示器、摄像机、拍下的车辆信息
class park {
constructor(floors){
this.floors = floors || []
this.camera = new Camera()
this.screen = new Screen()
this.carList = {}
}
// 进
in(car){
//1、通过摄像头获取车辆信息
const info = this.camera.shot(car)
//2、将车停在停车场
const i = parseInt(Math.random()*100%100)
const place = this.floors[0].places[i]
place.in()
info.place = place
//3、将信息记录下来
this.carList[car.num] = info
}
// 出
out(car){
// 获取信息
const info = this.carList[car.num]
// 清空车位
const place = info.place
place.out()
// 显示时间
this.screen.show(car, info.inTime)
// 清空记录
delete this.carList[car.num]
}
// 空余车位
emptyNum(){
return this.floors.map(floor=>{
return `${floor.index}层还有${floor.emptyPlaceNum()}个空余车位`
})
}
}
// 每层
class Floor {
constructor(index,places){
this.index = index
this.places = places || []
}
emptyPlaceNum(){
let num = 0;
this.places.forEach(p=>{
if(p.empty){
num = num +1
}
})
return num
}
}
// 停车位
class Place {
constructor(empty){
this.empty = true
}
in(){
this.empty = false
}
out(){
this.empty = true
}
}
// 显示器
class Screen {
show(car, inTime){
console.log('车牌号',car.num)
console.log('停车时间',Date.now() - inTime)
}
}
// 摄像机,拍车牌号和时间
class Camera {
shot(car){
return {
num: car.num,
inTime:Date.now()
}
}
}
// 初始化停车场
const floors = []
for(let i=0;i<3;i++){
const places =[]
for(let j=0;j<100;j++){
places[j] = new Place()
}
floors[i] = new Floor(i+1,places)
}
const park = new Park(floors)
// 初始化车辆
const car1 = new Car(100)
const car2 = new Car(200)
const car3 = new Car(300)