设计模式:针对设计问题的通用解决方案。
学习设计模式的原因:
什么时候需要设计模式:
程序要对扩展开放,对修改关闭。=> 函数式编程
//目标:已有的场景下,对于需要拓展的功能进行开放、拒绝直接的对于系统功能进行修改
//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()
模块只做一件事,模块的职责越单一越好。通过解耦让每一个职责更加的独立。
//目标:一个功能模块只做一件事情
// 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);
上层模块不要依赖于具体的下层模块,应依赖于抽象。
依赖收集机制:
//目标:上层应用面向抽象进行编程,而不是面向实现=>降低需求与实现的耦合。
//需求:分享功能
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);
接口要细化,功能要单一,一个接口不要调用太多方法,使其能力单一。
重构组件划分+状态机/命名空间:
//目标:多个专业接口比单个胖接口好用
//游戏中台:快速生产游戏
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() {}
}
主要关注于继承,意义是任何使用父类的地方都可以用子类去替换。父类能出现的地方,子类一定能出现。
重点:维护核心,抽离妥协、增加分层。
//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游戏}
}
工厂模式、建造者模式、单例模式
适配器模式、装饰器模式、代理模式
命令模式、模板模式、观察者模式