• 软件设计模式


    代码的最佳实践 —— 设计模式

    设计模式:针对设计问题的通用解决方案。

    1. 系统性多赢。
    2. 代码编制的真正工程化。

    学习设计模式的原因:

    1. 有利于代码复用。
    2. 有利于代码稳定可拓展。
    3. 有利于代码可读性提升。

    什么时候需要设计模式:

    1. 优先考虑全局设计。
    2. 合理权衡使用需求和维护成本。

    设计模式扮演的角色

    在这里插入图片描述

    1. 帮助组织模块:组织模块间的组成结构
    2. 帮助设计沟通:设计模块间的组成结构
    3. 提高代码质量

    设计原则

    开闭原则OCP

    程序要对扩展开放,对修改关闭。=> 函数式编程

    //目标:已有的场景下,对于需要拓展的功能进行开放、拒绝直接的对于系统功能进行修改
    
    //sprint1 - 中秋节活动 吃鸡高亮 + LOL要弹出折扣
    //render
    if (game === 'PUBG') {高亮}
    else {}
    //event
    if (game === 'LOL') {弹出折扣框}
    else {付款}
    //sprint2 - 要对部分游戏置灰 + 付款页面要显示停止发售
    //render
    if (game === 'PUBG') {高亮}
    else if (game === 'AAA') {灰色}
    else {}
    //event
    if (game === 'LOL') {弹出折扣框}
    else if (game === 'AAA') {break + 提示停止发售} 
    else {付款}
    
    //重构 => 核心化
    //render
    gameManager(game).setColor();
    //event
    gameManager(game).openDialog();
    //game库
    function gameManager(game) {
        return `${game}Manager`;
    }
    //导引
    const LOLManager = {
        setColor() {正常},
        openDialog() {折扣}
    }
    const PUBGManager = {
        setColor() {高亮},
        openDialog() {付款}
    }
    
    //重构 => 默认逻辑抽离
    class game {
        constructor(name) {
            this.name = name;
        }
        setColor() {设置颜色}
        openDialog() {默认}
    }
    class LOL extends game {
        openDialog() {折扣}
    }
    class PUBG extends game {
        setColor() {高亮}
    }
    //function = 拆分 + 排序 + 翻译 => formatter() + spliter() + sorter() + tranlater()
    
    单一职责原则SRP

    模块只做一件事,模块的职责越单一越好。通过解耦让每一个职责更加的独立。

    //目标:一个功能模块只做一件事情
    
    // game store
    class PUBGManager {
        openDialog() {
            // 弹框
            // 计算金额
            setPrice();
        }
    }
    const game = new PUBGManager();
    game.openDialog(); // 弹框 <=> 计算金额 两个模块存在功能上的耦合
    
    // 重构
    // gameManager.js - 业务
    class PUBGManager {
        constructor(command) {
            this.command = command;
        }
        openDialog(price) {
            this.command.setPrice(price);
        }
    }
    // optManager.js - 底层库
    class PriceManager {
        setPrice(price) {
            // 计算金额
        }
    }
    // main.js
    const exe = new PriceManager();
    const game1 = new PUBGManager(exe);
    game1.openDialog(15);
    
    依赖倒置原则DIP

    上层模块不要依赖于具体的下层模块,应依赖于抽象。
    依赖收集机制:

    //目标:上层应用面向抽象进行编程,而不是面向实现=>降低需求与实现的耦合。
    
    //需求:分享功能
    class Store {
    	constructor() {
    		this.share = new Share();
    	}
    }
    class Share {
    	shareTo(platform) {}
    }
    const store = new Store();
    store.share.shareTo('wx');
    //需求:评分功能
    class Store {
    	constructor() {
    		this.share = new Share();
    		this.rate = new Rate();
    	}
    }
    class Rate {
    	star(stars) {}
    }
    const store = new Store();
    store.rate.star('wx');
    
    //目标:底层不改变 + 动态挂载
    class Share {
    	//初始化动作
    	init(store) {
    		store.share= this;
    	}
    	shareTo(platform) {}
    }
    class Rate {
    	//初始化动作
    	init(store) {
    		store.rate = this;
    	}
    	star(stars) {}
    }
    class Store {
    	//维护模块名单
    	static modules = new Map();
    	constructor() {
    		//遍历名单+做初始化挂载
    		for(let module of Store.module.values()) {
    			module.init(this);
    		}
    	}
    	//提供注入功能模块
    	static inject(module) {
    		Store.modules.set(module.constructor.name, module);
    	}
    }
    //依次注册所有模块
    const rate = new Rate();
    Store.inject(rate);
    //初始化商城
    const store = new Store();
    store.rate.star(4);
    
    接口隔离原则ISP

    接口要细化,功能要单一,一个接口不要调用太多方法,使其能力单一。
    重构组件划分+状态机/命名空间:

    //目标:多个专业接口比单个胖接口好用
    
    //游戏中台:快速生产游戏
    class Game {
    	constructor(name) {
    		this.name = name;
    	}
    	run() {}
    	shot() {射击}
    	mega() {大招}
    }
    class PUBG extends Game {
        constructor() {
            // pubg contructor
        }
    }
    class LOL extends Game {
        constructor() {
            // lol contructor
        }
    }
    pubg = new PUBG('pubg');
    pubg.run();
    pubg.shot();
    pubg.mega();
    
    //重构:用多个接口替代,每个接口服务于一个子模块
    class Game {
    	constructor(name) {
    		this.name = name;
    	}
    	run() {}
    }
    class FPS {
        aim() {}
    }
    class MOBA {
        TP() {}
    }
    class PUBG extends Game {
        shot() {}
    }
    
    里氏替换原则LSP

    主要关注于继承,意义是任何使用父类的地方都可以用子类去替换。父类能出现的地方,子类一定能出现。
    重点:维护核心,抽离妥协、增加分层。

    //sprint1
    class Game {
        start() {开机}
        shutdown() {关机}
        play() {开始游戏}
    }
    const game = new Game();
    game.play();
    //sprint2
    class MobileGame extends Game {
        tombStore() {}
        play() {移动端游戏}
    }
    const mobile = new MobileGame();
    mobile.play();
    
    //重构
    class Game {
        start() {开机}
        shutdown() {关机}
    }
    
    class MobileGame extends Game {
        tombStore() {}
        play() {移动端游戏}
    }
    
    class PCGame extends Game {
        speed() {加速器}
        play() {PC游戏}
    }
    
    创建型

    工厂模式、建造者模式、单例模式

    结构型

    适配器模式、装饰器模式、代理模式

    行为型

    命令模式、模板模式、观察者模式

  • 相关阅读:
    MySQL 支持索引类型和DDL语句
    快速排序和归并排序非递归的详解
    el-table 列背景色渐变
    (五)React受控表单、获取DOM
    【算法练习】数组操作
    BuildApkPlugin 自动化编译打包
    GraalVM入门教程
    MySQL索引原理之索引与约束
    ESP8266-Arduino编程实例-ADS1115模数转换器驱动
    【mysql】遇到的问题及解决办法(在centos下)
  • 原文地址:https://blog.csdn.net/weixin_46920847/article/details/126572465