Javascript常用设计模式

举报
前端小盆友 发表于 2021/03/26 09:26:12 2021/03/26
【摘要】 设计与模式之前一直以为「设计模式」是一个完整的名词其实「设计」和「模式」是要分开来说的「设计」:5 个常见的设计原则「模式」:代码中常见的"套路",被程序员总结成了相对固定的写法,称之为「模式」也就是说学习"设计模式",首先肯定要学习和理解 5 个设计原则。 五大设计原则-SOLIDS(single)-单一职责原则- 一个程序只做好一件事;如果功能过于复杂就拆分开,每个部分保持独立;O(o...

设计与模式

之前一直以为「设计模式」是一个完整的名词

其实「设计」和「模式」是要分开来说的

「设计」:5 个常见的设计原则
「模式」:代码中常见的"套路",被程序员总结成了相对固定的写法,称之为「模式」
也就是说学习"设计模式",首先肯定要学习和理解 5 个设计原则。

五大设计原则-SOLID

  • S(single)-单一职责原则- 一个程序只做好一件事;如果功能过于复杂就拆分开,每个部分保持独立;
  • O(open-close)-开放封闭原则- 对扩展开放,对修改封闭;增加需求时,扩展新代码,而非修改已有代码;
  • L(Liskov)-里氏置换原则-子类能覆盖父类;父类能出现的地方子类就能出现;
  • I(Interface)-接口独立原则-保持接口的单一独立,避免出现”胖接口“;js中没有接口(typescript除外),使用少;
  • D(Dependence)-依赖倒置原则-编程依赖抽象接口,不要依赖具体实现;使用方只关注接口而不关注具体类的实现;

示例 promise

  • 单一职责原则:每个 then 中的逻辑制作好一件事
  • 开放封闭原则:如果新增需求,扩展 then
// 0.0.1/loadImg.js
function loadImg(src) {
  let promise = new Promise(function (resolve, reject) {
    let img = document.createElement('img');
    img.onload = function () {
      resolve(img);
    }
    img.onerror = function () {
      reject('图片加载失败');
    }
    img.src = src;
  });
  return promise;
}

let imgUrl = 'https://raw.githubusercontent.com/ruizhengyun/images/master/cover/ruizhengyun.cn_.png';
loadImg(imgUrl).then(function (img) {
  console.log(`width: ${img.width}`);
  return img;
}).then(function (img) {
  console.log(`height: ${img.height}`);
}).catch(function (ex) {
  console.log(ex);
});

Javascript 常用设计模式 – 工厂模式

工厂模式也称创建模式,是用于创建对象的一种方式。可以说就是用来代替 new 实例化对象,决定了实例化哪一个类,从而解决解耦问题。

拟物化解读
一个工厂接到一笔订单(传参),然后根据这个订单类型(参数)来安排产品线(实例化哪个类),当然客户可以要求一些产品的工艺属性(抽象工厂)。这其中厂长(工厂模式)只负责调度,即安排产品零件流水线。你应该知道的是,工厂有个特点就是产出体量大、相似度高的产品。如果你要做单一定制化的产品,那这笔订单给工厂就不适用了。

其作用(利)
解耦,通过使用工程方法而不是 new 关键字;
将所有实例化的代码集中在一个位置减少代码重复,降低出错;

具体实现
分步创建一个复杂的对象,解耦封装过程和具体创建组件(分解为零件流水线);
无需关心组件如何组装(厂长在调度);
不暴露创建对象的具体逻辑,将逻辑封装在一个函数中(客户只需要告诉工厂做什么和提一些要求);

适用场景
处理大量具有相同属性的小对象;
对象的构建十分复杂,需要依赖具体环境创建不同实例

分类(抽象程度)
不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。

简单工厂模式

也可以叫静态工厂模式,用一个工厂对象创建同一类对象类的实例。现实生活中,用户在平台还是分等级的,角色不同,权限也不同。
1.ES5 实现

// 0.0.2/es5.simple.factory.js
function Role(options){
    this.role = options.role;
    this.permissions = options.permissions;
}
Role.prototype.show = function (){
    var str = '是一个' + this.role + ', 权限:' + this.permissions.join(', ');
    console.log(str)
}

function simpleFactory(role){
    switch(role) {
        case 'admin':
            return new Role({ 
                role: '管理员', 
                permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']
            });
            break;
        case 'developer':
            return new Role({ 
                role: '开发者', 
                permissions: ['开发', '推送', '提问', '评论']
            });
            break;
        default:
            throw new Error('参数只能为 admin 或 developer');
    }
}

// 实例
const xm = simpleFactory('admin');
xm.show();

const xh = simpleFactory('developer');
xh.show();

const xl = simpleFactory('guest');
xl.show();

2.ES6 实现

// 0.0.2/simple.factory.js
class SimpleFactory {
    constructor(opt) {
        this.role = opt.role;
        this.permissions = opt.permissions;
    }

    // 静态方法
    static create(role) {
        switch (role) {
            case 'admin':
                return new SimpleFactory({
                    role: '管理员',
                    permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']
                });
                break;
            case 'developer':
                return new SimpleFactory({
                    role: '开发者',
                    permissions: ['开发', '推送', '提问', '评论']
                });
                break;
            default:
                throw new Error('参数只能为 admin 或 developer');
        }
    }

    show() {
        const str = `是一个${this.role}, 权限:${this.permissions.join(', ')}`;
        console.log(str);
    }

}

// 实例
const xm = SampleFactory.create('admin');
xm.show();

const xh = SampleFactory.create('developer');
xh.show();

const xl = SampleFactory.create('guest');
xl.show();

// 0.0.2/simple.factory2.js
class Role {
    constructor(options) {
        this.role = options.role;
        this.permissions = options.permissions;
    }
    show() {
        const str = `是一个${this.role}, 权限:${this.permissions.join(', ')}`;
        console.log(str);
    }
}

class SimpleFactory {
    constructor(role) {
        if(typeof this[role] !== 'function') {
            throw new Error('参数只能为 admin 或 developer');
        }
        return this[role]();
    }

    admin() {
        return new Role({
            role: '管理员',
            permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']
        });
    }
    developer() {
        return new Role({
            role: '开发者',
            permissions: ['开发', '推送', '提问', '评论']
        });
    }
}


// 实例
const xm = new SimpleFactory('admin');
xm.show();

const xh = new SimpleFactory('developer');
xh.show();

const xl = new SimpleFactory('guest');
xl.show();

上例中,simpleFactory 就是一个简单工厂,2个实例对应不同的权限,调用工厂函数时,只需传递 admin 或 developer 就可获取对应的实例对象。
1.简单工厂函数适用场景

正确传参,就可以获取所需要的对象,无需知道内部实现细节;
内部逻辑(工厂函数)通过传入参数判断实例化是使用哪些类;
创建对象数量少(稳定),对象的创建逻辑不复杂;

2.简单工厂函数不适用场景

当需要添加新的类时,就需要修改工厂方法,这违背了开放封闭原则(OCP, 对扩展开放、对源码修改封闭)。函数 create 内包含了所有创建对象(构造函数)的判断逻辑代码,如果要增加新的构造函数还需要修改函数 create(判断逻辑代码),当可选参数 role 变得更多时,那函数 create 的判断逻辑代码就变得臃肿起来,难以维护。
不适用创建多类对象;

工厂方法模式

将实际创建对象工作推迟到子类当中,核心类就成了抽象类。这样添加新的类时就无需修改工厂方法,只需要将子类注册进工厂方法的原型对象中即可。

1.ES5 实现,ES5 没有像传统创建类的方式那样创建抽象类,所以工厂方法模式只需参考其核心思想即可。可将工厂方法看做一个实例化对象工厂类(采用安全模式类),将创建对象的基类放在工厂方法类的原型中即可。当需要添加新类时,只需挂载在 FunctionFactory.prototype 上,无需修改工厂方法,也实现了 OCP 原则。

// 0.0.2/es5.function.factory.js
function FunctionFactory(role) {
    if(!(['admin', 'developer'].indexOf(role) > -1)){
        throw new Error('参数只能为 admin 或 developer');
    }
    
    // 安全的工厂方法
    if (this instanceof FunctionFactory) {
        return this[role]();
    }
    return new FunctionFactory(role);
}
FunctionFactory.prototype.show = function () {
    var str = '是一个' + this.role + ', 权限:' + this.permissions.join(', ');
    console.log(str)
}
FunctionFactory.prototype.admin = function (permissions) {
    this.role = '管理员';
    this.permissions = ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论'];
}
FunctionFactory.prototype.developer = function (permissions) {
    this.role = '开发者';
    this.permissions = ['开发', '推送', '提问', '评论'];
}

var xm = FunctionFactory('admin');
xm.show();

var xh = new FunctionFactory('developer');
xh.show();

var xl = new FunctionFactory('guest');
xl.show();

2.ES6 实现,由于 ES6 中还没有 abstract,就用 new.target 来模拟出抽象类(new.target 指向被 new 执行的构造函数),判断 new.target 是否指向了抽象类,如果是就报错。

// 0.0.2/function.factory.js
class FunctionFactoryBase { // 抽象类
    constructor(role) {
        if (new.target === FunctionFactoryBase) {
            throw new Error('抽象类不能实例');
        }
        this.role = role;
    }
}

class FunctionFactory extends FunctionFactoryBase { // 子类
    constructor(role) {
        super(role);
    }

    static create(role) {
        switch (role) {
            case 'admin':
                return new FunctionFactory({
                    role: '管理员',
                    permissions: ['设置', '删除', '新增', '创建', '开发', '推送', '提问', '评论']
                });
                break;
            case 'developer':
                return new FunctionFactory({
                    role: '开发者',
                    permissions: ['开发', '推送', '提问', '评论']
                });
                break;
            default:
                throw new Error('参数只能为 admin 或 developer');
        }
    }

    show() {
        const { role, permissions } = this.role;
        const str = `是一个${role}, 权限:${permissions.join(', ')}`;
        console.log(str)
    }
}

// let xl = new FunctionFactoryBase(); // 此行会报错,注释后方可正常执行后面

let xm = FunctionFactory.create('admin');
xm.show()

let xh = FunctionFactory.create('developer');
xh.show()

let xl = FunctionFactory.create('guest');
xl.show()

抽象工厂模式

抽象工厂只留对外的口子,不做事,留给外界覆盖(子类重写接口方法以便创建的时候指定自己的对象类型)。主要用于对产品类簇的创建,不直接生成实例(简单工厂模式和工厂方法模式都是生成实例)。

  • 抽象类是一种声明但不能使用的类,子类必须先实现其方法才能调用;
  • 可以在抽象类中定义一套规范,供子类去继承实现;
// 0.0.2/abstract.factory2.js
// 抽象工厂
function AbstractFactory(subType, superType) {
    if (typeof AbstractFactory[superType] === 'function') {
        //缓存类
        function F() { }
        //继承父类属性和方法
        F.prototype = new AbstractFactory[superType]();
        //将子类 constructor 指向子类(自己)
        subType.prototype.constructor = subType;
        //子类原型继承缓存类(父类)
        subType.prototype = new F();
    } else {
        //不存在该抽象类抛出错误
        throw new Error('抽象类不存在')
    }
}

// 抽象类
AbstractFactory.Phone = function () {
    this.type = 'Phone';
}
AbstractFactory.Phone.prototype = {
    showType: function () {
        return new Error('Phone 抽象方法 showType 不能调用');
    },
    showPrice: function () {
        return new Error('Phone 抽象方法 showPrice 不能调用');
    },
    showColor: function () {
        return new Error('Phone 抽象方法 showColor 不能调用');
    }
}

AbstractFactory.Pad = function () {
    this.type = 'Pad';
}
AbstractFactory.Pad.prototype = {
    showType: function () {
        return new Error('Pad 抽象方法 showType 不能调用');
    },
    showPrice: function () {
        return new Error('Pad 抽象方法 showPrice 不能调用');
    },
    showColor: function () {
        return new Error('Pad 抽象方法 showColor 不能调用');
    }
}

// 抽象工厂实现对抽象类的继承
function Iphone(type, price, color) {
    this.type = type;
    this.price = price;
    this.color = color;
}

//抽象工厂实现对 Phone 抽象类的继承
AbstractFactory(Iphone, 'Phone');
Iphone.prototype.showType = function () {
    return this.type;
}
Iphone.prototype.showPrice = function () {
    return this.price;
}
Iphone.prototype.showColor = function () {
    return this.color;
}

function Ipad(type, price, color) {
    this.type = type;
    this.price = price;
    this.color = color;
}
AbstractFactory(Ipad, 'Pad');
Ipad.prototype.showType = function () {
    return this.type;
}
Ipad.prototype.showPrice = function () {
    return this.price;
}
Ipad.prototype.showColor = function () {
    return this.color;
}

// 实例
var iphone5s = new Iphone('iphone 5s', 3000, '白色');
console.log('今天刚买了' + iphone5s.showType() + ',价格是' + iphone5s.showPrice() + ',' + iphone5s.showColor())

var iphone8s = new Iphone('iphone 8s', 8000, '白色');
console.log('今天刚买了' + iphone8s.showType() + ',价格是' + iphone8s.showPrice() + ',' + iphone8s.showColor())

var ipad = new Ipad('ipad air', 2000, '骚红色');
console.log('今天刚买了' + ipad.showType() + ',价格是' + ipad.showPrice() + ',' + ipad.showColor())

除此之外还有单例模式、适配器模式、装饰器模式、代理模式、观察者模式、迭代器模式等

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。