创建型设计模式介绍
在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
创建型模式由两个主导思想构成。一是将系统使用的具体类封装起来,二是隐藏这些具体类的实例创建和结合的方式。
创建型模式又分为对象创建型模式和类创建型模式。对象创建型模式处理对象的创建,类创建型模式处理类的创建。详细地说,对象创建型模式把对象创建的一部分推迟到另一个对象中,而类创建型模式将它对象的创建推迟到子类中。
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
如果使用原型模式,我们只需要调用负责克隆的方法,便能完成同样的功能。> 原型模式的实现关键,是语言本身是否提供了clone方法。ECMAScript 5提供了Object.create方法,可以用来克隆对象。
const Car = function () {this.color = "red";this.brand = "大众";this.mileage = 10000;
};
const car = new Car();
console.log("car:before", car);
// car:before Car { color: 'red', brand: '大众', mileage: 10000 }
car.color = "blue";
car.brand = "BMW";
car.mileage = 999;
car.sayHello = () => console.log("sayHello Func => ", "hello,world!");
console.log("car:after", car);
// car:after Car { color: 'blue', brand: 'BWM', mileage: 999 }
const bentch = Object.create(car);
console.log("bentch:", bentch);
console.log("bentch.color:", bentch.color);
console.log("bentch.brand:", bentch.brand);
console.log("bentch.mileage:", bentch.mileage);
// bentch: Car {}
// bentch.color: blue
// bentch.brand: BWM
// bentch.mileage: 999
bentch.sayHello();
// sayHello Func =>hello,world!
说实在的,原型模式看了好多人在介绍,并没有一个很清晰的认知。只是认识到了这两点:
1.可以这么理解,所谓原型是依赖于使用场景的,它 使用到了 Object.create 方法去克隆了已经存在的对象,并对原来的数据进行改动的时候不会对原来的数据造成影响。
2.感觉这种方法非常简单,适用于比较简单的场景,能迅速的“实例化”一个新的对象出来。
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。
class MessageClass {constructor(config) {if (!MessageClass.instance) {const baseConfig = {color: "red",timeOut: 3000,};this.config = Object.assign(baseConfig, config);MessageClass.instance = this;}return MessageClass.instance;}success(msg) {this.config.color = "green";this.msg = msg;this.print("success", msg);}error(msg) {this.config.color = "red";this.msg = msg;this.print("error", msg);}print(type) {console.log(type,"::","color=",this.config.color,"msg=",this.msg,"timeOut=",this.config.timeOut);}
}
const Message = new MessageClass({ timeOut: 5000 });
const Message2 = new MessageClass({ timeOut: 8000 });
Message.success("成功消息!");
Message.error("错误消息!");
Message2.success("成功消息!");
Message2.error("错误消息!");
console.log("Message === Message2 : ", Message === Message2);
如上所示,我实现了一个 Message 组件,类似 ElementUI 里面的 message 组件的简化版本。
这个无疑是最能诠释单例模式的一个常见的场景了,如打印结果所示,第二次实例化 MessageClass 的时候,入参config并没有生效,这是因为,第一次已经将 intence 创建出来了,以后再也不会走 constroctor 里面的逻辑了,想要修改config,就需要再暴露一个方法去 setConfig 了。
为什么要 Message 要用单例模式呢?因为要保证 每个 Message 不会互相影响,这就要在同一个地址把所有的打印信息放在一起,然后统一去实现动画效果。
其他的使用场景主要是 single-spa 的场景下,例如 vue-router,vuex,dialog,confirm …
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
class User {constructor(role, pages) {this.role = role;this.pages = pages;}static UserFactory(role) {switch (role) {case "superadmin":return new User(role, ["home", "user-manage", "news-manage"]);break;case "admin":return new User(role, ["home", "news-manage"]);break;case "user":return new User(role, ["home"]);break;default:throw "参数错误!";break;}}
}
const sadmin = User.UserFactory("superadmin");
const admin = User.UserFactory("admin");
const user = User.UserFactory("user");
console.log(sadmin);
console.log(admin);
console.log(user);
const err = User.UserFactory("error");
如上所示,是从 B 站视频上的一个例子,用来鉴权所使用的。其实这个逻辑用在这里有点牵强。
但是用来理解套路是够了,就是根据实例化的时候的参数不同,返回不同的对象实例。
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
这个就不粘代码了,工厂模式中可以看到,最终返回的是一个实例后的对象,而抽象工厂模式是使用一个公共的factory方法返回class原型。使用方法也多了一步:
const UserClass = userClassFactory('superadmin')
let user = new UserClass('张三')
建造者模式(生成器模式)
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
我看了一下相关的介绍,这个模式的使用场景不是很常见,但是有个例子还是很容易理解的:
// 类的链式调用
class CarBuilder {constructor(name) {this.name = name;}buildPart1(params) {this.part1 = params;return this;}buildPart2(params) {this.part2 = params;}
}
const bentch = new CarBuilder("bentch");
bentch.buildPart1({ color: "red" }).buildPart2({ color: "blue" });
console.log(bentch);
如上所示,如果有一个按顺序实现的比较复杂的需求,例如一个多层循环的tab结构,就需要先实现头部,然后绑定头部事件,按照头部信息触发点击事件切换内容。
同样,链式调用是最直观的方法,其实正常的逻辑会有一个组装方法,把各个模块分开后由 组装方法 build 在一起。
后记
其实,本文的几段代码都是为了演示设计模式而专门杜撰的,说句不好听的,就是为了用而用。
而实际开发中,一般都是多个模式混合着使用,学习这些模式必须学习到其核心原理,而不是要背会这些代码。
简单总结一下:
1.原型模式:基于语言的原生语法,进行新的对象的创建和构建,适用于比较灵活的场景;
2.单例模式:整个页面中涉及到的所有实例,都来自于同一个,适用于来自于同一个模型的多个不同实例,但是需要统一管理所有的实例的场景下;
3.工厂模式:由一个入口,返回多个同类型的实例对象,适用于同属大类但是又有些许区别的的场景;
4.抽象工厂模式:在工厂模式的基础上,同一个入口返回差别比较大的实例对象原型,适用于比较复杂的一对多的创建场景;
5.建造者模式:将整个大业务进行拆解,进行依次组装,适用于流程比较复杂且有前后依赖关系的场景。
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取