设计模式与原则精要

设计原则

单一职责原则- SRP

开放封闭原则 - OCP

里氏替换原则 - LSP

接口隔离原则 - ISP

依赖倒转原则 - DIP

迪米特法则 - DP

合成复用原则 - CSP

什么时候去解耦

如果两个职责在修改的时候,都是同时变化,就不必解耦开;

如果未来可能会独立复用,可以考虑单一职责。

单一职责原则- SRP

js 复制代码
class UserOptions {
    //修改用户的名称、修改用户的密码
    updateUserInfo(user, type) {
        if (type === "username") {
            this.user.username = user.username;
        } else if (type === "password") {
            this.user.password = user.password;

        }
    }
}
const userOpts = new UserOptions();
userOpts.updateUserInfo({ username: '张三' }, "username");



class UserOptionsSRP {
    updateUserName(username) {
        this.user.username = username;
    }

    updatePassword(password) {
        this.user.password = password;
    }
}

开放封闭原则 - OCP基础

里氏替换原则 - LSP

在设计的子类的时候,可以完整地继承父类的职责,而不是去修改。

组合的方式,可能会优于继承;

多考虑多态逻辑的实现和应用。

接口隔离原则 - ISP

接口隔离原则

意义和目的是,尽量减少接口之间的耦合存在,在大型的软件架构设计中,多个接口的组合使用,好过一个大而全的接口。

1接口尽量小;

2接口要高内聚

依赖倒转原则 - DIP

依赖倒转原则的核心,是减少高层模块和底层模块之间的耦合。

高层模块不应该直接依赖底层模块,两者都应该依赖于抽象

抽象不应该依赖于细节,细节应该依赖于抽象。

设计模式

创建型:工厂、单例、建造者

结构型:装饰器模式、适配器模式、代理模式

行为型:观察者模式、发布订阅模式、策略模式

构造器模式

js 复制代码
function Employee(name, age) {
  this.name = name;
  this.age = age;

  this.say = function () {
    console.log(`我的名字是${this.name},今年${this.age}岁`);
  };
}

var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);

console.log(employee1, employee2);
employee1.say();
employee2.say();

原型模式

js 复制代码
function Employee(name, age) {
  this.name = name;
  this.age = age;
}

Employee.prototype.say = function () {
  console.log(`我的名字是${this.name},今年${this.age}岁`);
};

var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);

console.log(employee1, employee2);
employee1.say();
employee2.say();
js 复制代码
class Employee { 
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    say() {
        console.log(`我的名字是${this.name},今年${this.age}岁`);
    }
}

var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);

console.log(employee1, employee2);
employee1.say();
employee2.say();

建造者模式

js 复制代码
function Car() {

};

Car.prototype.init = function (
    door,
    options,
    color,
    bland
) {
    this.initDoor(door)
        .initOptions(options)
        .initColor(color)
        .initBland(bland)
}


Car.prototype.initDoor = function (door) {
    this.door = door;
    return this;
}


Car.prototype.initOptions = function (options) {
    this.options = options;
    return this;
}

Car.prototype.initColor = function (color) {
    this.color = color;
    return this;
}


Car.prototype.initBland = function (bland) {
    this.bland = bland;
    return this;
}

const MyCar = new Car();
MyCar.initDoor(4)
    .initOptions(['ABS', 'GPS'])
    .initColor('white')
    .initBland('Benz');
console.log(MyCar);

工厂模式

当满足以下任一条件时,即可考虑使用:

​ 对象创建步骤多、逻辑复杂;

​ 需根据条件动态选择创建的对象类型;

​ 希望统一管理对象的生命周期(如复用、监控);

​ 需降低代码对具体类的依赖,便于后续扩展或替换

简单工厂

es5

js 复制代码
function UserFactory(role) {
    function User(role, pages) {
        this.role = role;
        this.pages = pages;
    }
    switch (role) {
        case 'superadmin':
            return new User('superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);
        case 'admin':
            return new User('admin', ['home', 'user-manage', 'news-manage']);
        case 'editor':
            return new User('editor', ['home', 'news-manage']);
        default:
            throw new Error('参数错误');
    }
}

var user = UserFactory('superadmin');
console.log(user);

es6

js 复制代码
class User {
    constructor(role, pages) {
        this.role = role;
        this.pages = pages;
    }
    static UserFactory(role) {

        switch (role) {
            case 'superadmin':
                return new User('superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);
            case 'admin':
                return new User('admin', ['home', 'user-manage', 'news-manage']);
            case 'editor':
                return new User('editor', ['home', 'news-manage']);
            default:
                throw new Error('参数错误');
        }
    }
}


var user = User.UserFactory('superadmin');
console.log(user);

抽象工厂

假如对于实际的生产来说,我的BYD和dongfeng不可能是一个工厂生产

js 复制代码
class AbstratFacrtory {
    createCar() {};
    createTruck() {};
};


class Car {
    constructor() {
        this.door = 4;
        this.options = ['drive', 'drived'];
    }
};

class Truck {
    constructor() {
        this.door = 2;
        this.options = ['drive', 'pullGoods'];
    }
};


class DongfengCar extends Car { 
    constructor(color) {
        super();
        this.bland = 'dongfeng';
        this.color = color;
    }

}

class BYDCar extends Car { 
    constructor(color) {
        super();
        this.bland = 'byd';
        this.color = color;
    }

}

class DongfengTruck extends Truck { 
    constructor() {
        super();
        this.bland = 'dongfeng';
    }

}


class BYDTruck extends Truck { 
    constructor() {
        super();
        this.bland = 'byd';
    }

}


class DongfengFactory {
    createCar(color) {
        return new DongfengCar(color);
    }

    createTruck() {
        return new Truck();
    }
}


class BYDFactory {
    createCar(color) {
        return new BYDCar(color);
    }

    createTruck() {
        return new BYDTruck();
    }
}

const myFactory = new BYDFactory();
const myCar = myFactory.createCar('blue');
console.log(myCar);
js 复制代码
class User {
    constructor(name, role, pages) {
        this.name = name;
        this.role = role;
        this.pages = pages;
    }
    welcome() {
        console.log(`欢迎回来`, this.name);
    }
    dataShow() {
        throw new Error('抽象方法需要被实现');
    }
}


class SuperAdmin extends User {
    constructor(name) {
        super(name, 'superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);
    }
    dataShow() {
        console.log(`superadmin-datashow`);
    }

    addRight() {

    }

    addUser() {

    }
}


class Admin extends User {
    constructor(name) {
        super(name, 'admin', ['home', 'user-manage',  'news-manage']);
    }
    dataShow() {
        console.log(`superadmin-datashow`);
    }

    addRight() {

    }

    addUser() {
        
    }
}

class Editor extends User {
    constructor(name) {
        super(name, 'editor', ['home', 'news-manage', 'new-manage']);
    }
    dataShow() {
        console.log(`superadmin-datashow`);
    }

    addRight() {

    }

    addUser() {
        
    }
}


function getAbstratctUserFactory(role) { 
    switch (role) {
        case 'superadmin':
            return new SuperAdmin('superadmin');
        case 'admin':
            return new Admin('admin');
        case 'editor':
            return new Editor('editor');
        default:
            throw new Error('参数错误');
    }
}

let user = getAbstratctUserFactory('superadmin');
console.log(user);

单例模式

是全局的,唯一的实例

好处

全局唯一

可以实现状态状态管理

坏处

副作用太大

不符合单一职责的原则。

Vue项目中的Vue实例

Node项目中的App实例

Vuex React-Redux中的store

js 复制代码
// ###########################################################构造函数的实现,实例在外部
let singleton;

function Singleton() {
    if (!singleton) {
        singleton = this;
    }
    return singleton;
}

Singleton.prototype.getName = function () {
    console.log('我是单例');
}

let singleton1 = new Singleton();
let singleton2 = new Singleton();

console.log(singleton1 === singleton2);


// ###########################################################实例在内部
function Singleton2() {
    this.instance = null;
}

Singleton2.getInstance = function () {
    if (!this.instance) {
        this.instance = new Singleton2();
    }
}


Singleton2.prototype.getName = function () {
    console.log('我是单例');
}

const single2A = Singleton2.getInstance();
const single2B = Singleton2.getInstance();
console.log(single2A === single2B);


// #####################################################################使用闭包
const Singleton3 = (function () {
    let instance;
    function createInstance() {
        instance = '单例';
        return instance;
    }
    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    }
})();

const ins1 = Singleton3.getInstance();
const ins2 = Singleton3.getInstance();
console.log(ins1 === ins2);

es6

js 复制代码
class Singleton {
    static instance;

    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }
}

const instance1 = new Singleton();
const instance2 = new Singleton();

console.log(instance1 === instance2);

代理模式

VUE

object.defineProperty

new Proxy();

数据缓存的代理

localStorage的缓存 -- localStorage优先使用

观察者模式

js 复制代码
class Subject {
    constructor() {
        this.deps = [];
        // state发生改变时,通知所有的observer
        this.state = 0;
    }

    attach(observer) {
        this.deps.push(observer);
    }

    setState(num) {
        this.state = num;
        this.notifyAllObservers();
    }

    notifyAllObservers() {
        this.deps.forEach(obs => obs.run(this.state))
    }
}


class Observer { 
    constructor(subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
    run() {

    }
}


class BinaryObserver extends Observer { 
    constructor(subject) {
        super(subject);
    }
    run(data) {
        console.log(`hello, this is binaryObserver:  ${data}`);
    }
}
class ArrayObserver extends Observer { 
    constructor(subject) {
        super(subject);
    }
    run(data) {
        console.log(`hello, this is arrayObserver:  ${data}`);
    }
}


const subject = new Subject();
const bobs = new BinaryObserver(subject);
const aobs = new ArrayObserver(subject);

subject.setState(15);
js 复制代码
class Subject {
    constructor() {
        this.observers = [];
    }

    add(observer) {
        this.observers.push(observer);
    }

    remove(observer) {
        const index = this.observers.indexOf(observer);
        if (index !== -1) {
            this.observers.splice(index, 1);
        }
    }

    notify(data) {
        this.observers.forEach(obs => obs.update(data));
    }
}

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

    update(data) {
        console.log(`${this.name} 收到数据:${data}`);
    }
}

const subject = new Subject();
const observer1 = new Observer('张三');
const observer2 = new Observer('李四');

subject.add(observer1);
subject.add(observer2);

setTimeout(() => {
    subject.remove(observer1)
}, 1000);

setTimeout(() => {
    subject.notify('Hello World!');
}, 1000);

发布订阅者模式

解耦代码

装饰器模式

保证原有函数功能不变的同时,增加一个新的功能(AOP面向切面编程)

ES和TypeScript的Decoratori语法

类装饰器,函数(方法)装饰器,属性装饰器

实现方式:class、高阶函数

适配器模式

js 复制代码
class Target {
    api() {
        console.log('this is the api');
        
    }
}

class Adaptee { 
    newLogic() {
        console.log('new logic');
    }
}


class Adaptor extends Target { 
    constructor(adaptee) {
        super();
        this.adaptee = adaptee;
    }

    api() {
        this.adaptee.newLogic();
    }
}

const adaptor = new Adaptor(new Adaptee());
adaptor.api();
js 复制代码
class TencentMap {
    show() {
        console.log('开始渲染腾讯地图');
    }
}


class BaiduMap {
    display() {
        console.log('开始渲染百度地图');
    }
}

class TencentAdapater extends TencentMap {
    constructor() {
        super();
    }

    display() {
        this.show();
    }
}

function renderMap(map) {
    map.display();
}


renderMap(new TencentAdapater());
renderMap(new BaiduMap());

策略模式

js 复制代码
let strategry = {
    A: function (salary) {
        return salary * 4;
    },
    B: function (salary) {
        return salary * 3;
    },
    C: function (salary) {
        return salary * 2;
    }
}

function calBonus(level, salary) {
    return strategry[level](salary);
}

console.log(calBonus('A', 2000));

模块模式

桥接模式

桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

使用场景:一个类存在两个或多个独立变化的维度,且这两个维度都需要进行扩展

优点:

把抽象与实现隔离开,有助于独立地管理各组成部分。

缺点:

每使用一个桥接元素都要增加一次函数调用,这对应用程序的性能会有一些负面影响一一提高了系统的复杂程度。

组合模式

命令模式

宏命令

js 复制代码
class MacroCommand {
    constructor() {
        this.list = [] //子命令对象
    }

    add(command) {
        this.list.push(command)
    }

    execute() {
        for (let item of this.list) {
            item.execute()
        }
    }
}

const Tabs = {
    execute(){
        console.log('选项卡执行');
    }
}

const Swipe = {
    execute(){
        console.log('轮播执行');
    }
}

const macroCommand = new MacroCommand()
macroCommand.add(Tabs)
macroCommand.add(Swipe)

macroCommand.execute()

模版方法模式

js 复制代码
var Container = function (params = {}) {
    var F = function () { };

    F.prototype.init = function () {
        this.getData();
        this.render();
    }

    F.prototype.getData = params.getData || function () {
        throw new Error('必须传入getData方法')
    }

    F.prototype.render = function () {
        console.log('render');
    }
    return F;
};

var Myclass = Container({
    getData() {
        console.log('获取comingsoon');
        return [4, 5, 6]
    }
});
new Myclass().init();

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭

代器模式可以把选代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,

也可以按顺序访问其中的每个元素。

1.为遍历不同数据结构的"集合"提供统一的接口;

2.能遍历访问"集合"数据中的项,不关心项的数据结构

js 复制代码
var obj = {
    codeL: 200,
    name: 'kerwin',
    list: ['aaaa', 'bbbb', 'ccc'],
    [Symbol.iterator]: function () { 
        var index = 0;
        return {
            next: () => {
                if (index < this.list.length) {
                    return {
                        value: this.list[index++],
                        done: false
                    }
                } else {
                    return {
                        value: undefined,
                        done: true
                    }
                }
            }
        }
    }
}

for (let item of obj) {
    console.log(item)
}

职责链模式

使多个对象都有机会处理请求,从而避免了请求的发送者与多个接收者直接的耦合关系,将这些接收者连接

成一条链,顺着这条链传递该请求,直到找到能处理该请求的对象。

优点:

1.符合单一职责,使每个方法中都只有一个职责。

2.符合开放封闭原则,在需求增加时可以很方便的扩充新的责任

3.使用时候不需要知道谁才是真正处理方法,减少大量的if或switch语法。

缺点:

1.团队成员需要对责任链存在共识,否则当看到一个方法莫名其妙的返回一个nxt时一定会很奇怪。

2.出错时不好排查问题,因为不知道到底在哪个责任中出的错,需要从链头开始往后找

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="text" id="input" />
    <button id="btn">注册</button>
    <script>
      btn.onclick = function () {
        checks.check();
      };

      function checkEmpty() {
        if (input.value.length === 0) {
          console.log("这里不能为空");
          return;
        }
        return "next";
      }

      function checkNumber() {
        if (Number.isNaN(+input.value)) {
          console.log("这里只能是数字");
          return;
        }
        return "next";
      }

      function checkLength() {
        if (input.value.length < 6) {
          console.log("长度不能小于6");
          return;
        }
        return "next";
      }

      class Chain {
        constructor(fn) {
          this.checkRule = fn;
          this.nextRule = null;
        }

        addRule(nextRule) {
          this.nextRule = new Chain(nextRule);
          return this.nextRule;
        }

        end() {
          this.nextRule = {
            check: () => "end"
          };
        }

        check() {
          this.checkRule() === "next" ? this.nextRule.check() : null;
        }
      }
      const checks = new Chain(checkEmpty);
      checks.addRule(checkNumber).addRule(checkLength).end();
    </script>
  </body>
</html>
相关推荐
IT_陈寒2 小时前
SpringBoot 性能优化的 7 个冷门技巧,让你的应用快如闪电!
前端·人工智能·后端
清风细雨_林木木2 小时前
flutter 里面的渐变色设置
前端·flutter
yourkin6663 小时前
初识react
前端·javascript·react.js
゜ eVer ㄨ3 小时前
React第四天——hooks
前端·react.js·前端框架
Jyywww1213 小时前
uniapp中pinia(setup语法)使用流程
开发语言·javascript·uni-app
leobertlan3 小时前
好玩系列:脚本和插件使我快乐
前端·程序员·gradle
穿花云烛展3 小时前
实习日记6(select选择的超出问题)
前端
前端搞毛开发工程师3 小时前
Ubuntu 系统 Docker 安装避坑指南
前端·后端
猪哥帅过吴彦祖3 小时前
Flutter 系列教程:布局基础 (下) - Stack 绝对定位和 Expanded 弹性布局
前端·flutter·ios