五大原则+设计模式

594 阅读1分钟

你可以跳过的部分

传统的设计模式有23种,是前人积累下来经验的总结。设计模式离我们其实不远,日常开发中我们或多或少有使用到相关的设计模式,如果熟练使用这些设计模式,我们的代码质量也会提高并且易扩展、易阅读。就像我们玩LOL(英雄联盟),设计模式就像那些套路,好的套路使得你从一开始就很有优势,更加容易赢得比赛。

五大原则

开闭原则--OCP(open closed principle)

对拓展开放、对修改关闭

已有的场景下,对于需要拓展的功能进行开放、拒绝直接的功能修改

class game {
  constructor(name) {
    this.name = name;
  }

  setColor() {
    console.log('这是有颜色的');
  }

  openDialog() {
    console.log('付款框');
  }
}

class LOL extends game {
  openDialog() {
    console.log('折扣');
  }
}

class PUBG extends game {
  setColor() {
    console.log('高亮');
  }
  openDialog() {
    console.log('付款');
  }
}

单一职责原则--SRP(single responsibility principle)

通过解耦让每一个职责更加的独立

一个功能制作一件事

class CAT {
  constructor(command) {
    this.command = command
  }
  eat(food) {
    console.log(`吃了${food}`);
    this.command.drink() // 吃+喝
  }
}

class Drink {
  drink(water) {
    console.log('农夫山泉有点甜');
  }
}

const drink = new Drink() // new一只叫timor的猫
const timorLife = new CAT(drink)
timorLife.eat('猫条') // 吃了猫条 农夫山泉有点甜

依赖倒置原则--DIP(Depend inversion principle)

上层不应依赖下层实现,面向对象进行coding,而不是对实现进行coding,降低需求与实现的耦合

// 评分功能
class Store {
  constructor() {
    this.share = new Share();
    this.rate = new Rate();
  }
}

class Share {
  shareTo() {
    // 分享到不同平台
  }
}

class Rate {
  star(stars) {
    // 评分
  }
}

const store = new Store();
store.rate.stars('5');

// 使用依赖倒置原则对其进行重构
// 目标: 暴露挂载 => 动态挂载
class Rate {
  init(store) {
    store.rate = this;
  }
  store(stars) {
    // 评分
  }
}

class Share {
  init(store) {
    store.share = this;
  }
  shareTo(platform) {
    // 分享到不同平台
  }
}

class Store {
  // 维护模块名单
  static modules = new Map();

  constructor() {
    // 遍历名单做初始化挂载
    for (let module of Store.modules.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(Interface segregation principle)

多个专业的接口比单个胖接口好用

class Game {
  constructor(name) {
    this.name = name;
  }
  run() {
    // 跑
  }
}

class PUGB extends Game {
  constructor() {
    super()
    // pubg constructor
  }
  shot() {
    console.log('射击');
  }
}

class LOL extends Game {
  constructor() {
    super()
    // lol constructor
  }
  mega() {
    console.log('开大招');
  }
}

里氏替换原则--LSP(Richter's substitution principle)

子类能够覆盖父类,父类能够出现的地方子类就能出现

class Game {
  start() {
    // 开机
    console.log('start');
  }
  shutdown() {
    // 关机
    console.log('shutdown');
  }
}

class MobileGame extends Game {
  tombStone() {
    console.log('tombStone');
  }
  play() {
    console.log('playMobileGame');
  }
}

class PC extends Game {
  speed() {
    console.log('speed');
  }
  play() {
    console.log('playPCGAME');
  }
}

设计模式

创建型(创建元素 => 规范创建步骤)

模式场景

  1. 批量生产同类型应用来满足频繁使用同一种类型需求时 - 工厂模式

  2. 当我们需要模块化拆分一个大模块,同时使模块间独立解耦分工 - 建造者模式

  3. 全局只需要一个实例,注重统一一体化 - 单例

实际应用

Button Producer:生产不同类型的按钮 => 生产多个本质相同,利用传参区分不同属性的元素 => 工厂

全局应用 router store => 只需要一个实例 => 单例

页头组件Header: 包含了title、button、breadcum => 生产多重不同类型的元素 => 建造者

工厂模式:生产同类型商品,隐藏创建过程、暴露共同接口

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();

建造者模式:独立生产商品,拆分简单模块、独立执行 => 注重过程与搭配

每个模块独立解耦,而建造者负责创建串联整体系统

例子: 优惠套餐单元,商品 + 皮肤 进行打折售卖

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();
  }
}

单例模式:全局只有一个实例

例子:vue-router

结构型(代码结构)

优化结构的实现方式

模式场景

中间转换参数、保持模块间独立的时候 - 适配器模式

附着于多个组件上,批量动态赋予功能的时候 - 装饰器模式

将代理对象与调用对象分离,不直接调用目标对象 - 代理模式

实际应用

  1. 两个模块:筛选器和表格,需要做一个联动。但筛选器的数据不能直接传入表格,需要做数据结构转换 => 模块之间独立,需要做数据结构转换 => 适配器模式

  2. 目前有按钮、title、icon三个组件。希望开发一个模块,让三个组件同时具备相同功能 => 套一层装甲对于每个组见

有统一的能力提升,且可以动态添加功能进行拓展 => 装饰器模式 3. ul中多个li,每个li上的点击事件 => 利用冒泡做委托,事件绑定在ul上 => 代理模式

适配器模式:适配已有方案

适配独立模块,保证模块间的独立解耦且连接兼容

// 需求: 买了一个港行PS,插座是国标
class HKDevice {
  getPlug() {
    return '港行双圆柱插头';
  }
}

class Target {
  constructor() {
    this.plug = new HKDevice();
  }
  getPlug() {
    return this.plug.getPlug() + '+港行双圆柱转换器';
  }
}

const target = new Target();
target.getPlug();

装饰器模式:增强已有方案

// 设备升级
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();

代理模式:集约流程,使用代理人来替换原始对象

// 游戏防沉迷
class Game {
  play() {
    return "playing";
  }
}

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();

行为型(模块行为总结)

不同的对象之间划分责任和算法的抽象化

模式场景

发出指令,中间层传递命令本身,命中包含执行对象 - 命令模式

通过模板定义执行顺序,做独立操作 - 模板模式

通过观察者,可以让被观察值统一发生变化,触发相应依赖值的统一更新 - 观察者模式

独立职责的单元通过链式执行,逐步操作流程 - 职责链

实际应用

  1. 提交表单进行表单逐行校验,链式调用validate,依次执行 => 职责链

  2. echart准备工作:canvas、config、init、draw(),规划顺序执行 => 模板模式

  3. 调度器在接受到一组新的数据时候,解析数据,并且根据数据类型包裹在对象中传递到下级helper,helper 再去执行相应操作 => 命令模式

  4. 输入框输入的值去判断下拉框显示与否 => 观察input设置show => 观察者模式

命令模式:包裹传递命令

请求以命令的形式包裹在对象中,并传给调用对象

// 接受者
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();

模板模式: 重编排,易扩展

在模板中,定义好每个方法的执行步骤,方法本身关注自己的事情

class Device {
  constructor(executePipeLine) {
    // executePipeLine……
  }
  powerOn() {
    console.log('打开电源');
  }
  login() {
    console.log('登录账号');
  }
  clickIcon() {
    console.log('点击开始游戏');
  }
  enterGame() {
    console.log('进入战场');
  }

  play() {
    this.powerOn();
    this.login();
    this.clickIcon();
    this.enterGame();
  }
}

观察者模式:模块间实时互通

当一个属性发生状态变化,观察者会连续引发所有的相关状态变化

// 通过智能家居一键开始游戏
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);

center.setState('on');

职责链模式:单向链执行

1.链式调用 2.职责独立 3.顺序执行

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('爸');
const mom = new Action('妈');
const wife = new Action('夫人');

dad.setNextAction(mom);
mom.setNextAction(wife);

dad.handle();