js23种设计模式可以分为三大类:行为型模式、结构型模式、创建型模式。
- 创建型模式——关注于分离对象的创建和使用
- 结构型模式——描述如何将类或者对象结合在一起形成更大的结构
- 行为型模式——不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用
创建型:创建型模式的作用是将模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰 ——就像乐高,相同的零件组合拼接成各种结构,使用不同零件的过程中我们不需要关注这些零件如果被生产出来。
结构型:描述如何将类或者对象组装在一起形成更大的结构-——就像搭积木,可以通过简单积木的组合形成功能更强大更复杂的的结构。
接下来我们去看看具体的创建型模式分类里的工厂模式。
首先说到工厂模式,不得不提到这个与之非常相似的构造器模式,它与工厂模式有很多 相似之处。
构造器
这里我有一个非常简单的场景,我需要记录小组成员信息,那么需要创建一些对象用来描述成员姓名和年龄,这时我写出如下代码:
const aa = {
name: 'aa',
age: 25
}
const bb = {
name: 'bb',
age: 24
}
可是如果小组成员太多的话,我们不能每一位成员都要这样手动去创建一个对象呀。
还好我们知道,ECMAScript的构造函数就是能创建对象的函数,那么我们有了如下写法:
//构造器
function User(realName, age) {
this.name = realName;
this.age = age;
}
const user = new User(realName, age);
那么之后当我们创建对象的时候,只需要进行一下简单的调用。在这个过程中,我们使用构造函数去初始化对象,就是应用了构造器模式。
记不记得我们前面说,创建型模式关注于分离对象的创建和使用。
这里我们可以看到,构造器将 name、age 赋值给对象的过程封装起来,确保每个对象都有这些属性,但同时可以确保name、age取值的不同。我们是不是可以说,构造器本质上是去抽象了每个对象实例的变与不变,不变的是每个对象上都可以有相同的属性,变化的是这些属性的取值可以不同。
好,这是抽象对象的变与不变,那么抽象不同构造函数(类)之间的变与不变,便是我们这一part的主角:工厂模式。
工厂模式
接下来我们添加一需求,上面每位成员我们还需要录入他的工种及工种职责,由于大家的工种不一样,这个时候我们就需要不同的构造函数了:
function Coder(name, age) {
this.name = name
this.age = age
this.career = 'coder'
this.work = ['写代码','技术分享','取奶茶']
}
function ProductManager(name, age) {
this.name = name
this.age = age
this.career = 'productManager'
this.work = ['提需求','开会','点奶茶']
}
我们可以发现这两个构造函数都存在变与不变的部分,不变的是他们都拥有name,age,career,work这四个属性,变化的是不同的工种,对应的工作不同。那么接下来我们再判断不同工种应该返回什么样的对象时,可以把这个逻辑写在如下这样的函数里。
function User(name, age, career, work) {
this.name = name
this.age = age
this.career = career
this.work = work
}
function Factory(name, age, career) {
let work
swith(career) {
case 'coder':
work = ['写代码','技术分享','取奶茶']
break
case 'boss':
work = ['提需求','开会','点奶茶']
break
case 'xxx':
//其他工种
break
return new User(name, age, career, work)
这样我们就无需创建多个构造函数,只需要调用Factory无脑传参就可以了。
此时这个Factory函数做的事就属于工厂模式,我们可以看到它封装了创建对象的过程,而我们要做的就是无脑传参就可以了。
应用实例
上述例子还是很容易理解的,那我们接下来来一个前端应用实例看看,这里我举一个前端开发中常用的消息提示框的例子。
编辑切换为居中
添加图片注释,不超过 140 字(可选)
这个例子中,我们有几个简单元素:三个按钮和一个消息提示框。需求是,我需要在点击成功按钮时,将提示框的背景色设置为绿色,点击警告按钮时,将背景色设置为黄色,失败时就是红色,这里我通过点击按钮时,切换消息提示框的class来实现简单的html代码如下:
//body:
<body>
<div class="wrapper">
<!-- <div class="modal error">
<header>sdf</header>
</div> -->
<div class="btn-wrap">
<button data-status="S">成功</button>
<button data-status="W">警告</button>
<button data-status="E">失败</button>
</div>
</div>
</body>
//style:
<style>
.modal.S {
background-color: #67c23a;
}
.modal.E {
background-color: #f56c6c;
}
.modal.W {
background-color: #e6a23c;
}
</style>
然后是我们给按钮绑定的点击事件的js代码如下:
const changeStatus = (status: string) => {
switch (status) {
case "S":
oModal.className = "modal success";
break;
case "W":
oModal.className = "modal warning";
break;
case "E":
oModal.className = "modal error";
break;
default:
break;
}
};
其中,最主要的是,changeStatus这个函数,这里我们只是做了简单的class切换。
那如果此时,产品经理慢慢走到你面前,说:“我要点击成功的时候,你给我放个五彩斑斓的烟花,点击警告的时候在控制台打印个警告信息,点击错误的时候跳转登陆页~巴拉巴拉吧......”,我们知道,产品经理的嘴是捂不住的,那我们怎么办,难道要在这个changeStatus函数里原地更改每个状态对应要做的事吗?
显然这样做是不好的,明显违背了我们经常强调的开闭原则,那么此时,我们可以借助工厂模式来实现这一需求。
这里其实跟前面那个根据不同工种返回不同构造函数的例子很像,我们可以造一个工厂函数,根据点击的不同按钮传入不同的状态,然后自动去实例化不同状态对应的类,实现如下:
//*枚举状态
export enum MType {
success = "S",
warning = "W",
error = "E",
}
//*创建一个公共类,承载一些公共的方法属性
class Modal {
status: MType;
constructor(status: MType) {
this.status = status;
}
get className(): string {
let classStr = "modal ";
switch (this.status) {
case MType.success:
classStr += "success";
break;
case MType.warning:
classStr += "warning";
break;
case MType.error:
classStr += "error";
break;
default:
break;
}
return classStr;
}
}
//*不同的状态对应的类分开写,可以在对应类里实现各自不同的功能扩展
class SuccessModal extends Modal {
constructor() {
super(MType.success);
}
//放一个五彩斑斓的烟花
}
class WarningModal extends Modal {
constructor() {
super(MType.warning);
}
//控制台打印信息
}
class ErrorModal extends Modal {
constructor() {
super(MType.error);
}
//跳转登陆页
}
//*工厂函数,通过传入的状态来自动帮我们实例化相应的类
class ModalFactory {
dom: HTMLElement;
constructor(dom1: HTMLElement) {
this.dom = dom1;
}
modal: any = null;
create(status: MType) {
switch (status) {
case MType.success:
this.modal = new SuccessModal();
break;
case MType.warning:
this.modal = new WarningModal();
break;
case MType.error:
this.modal = new ErrorModal();
break;
default:
break;
}
this.dom.className = this.modal.className;
}
}
export default ModalFactory;
那么我们使用的时候,就可以在点击事件里这样使用:
const handleClick = (e: Event) => {
const tar = e.target as HTMLElement;
const tagName = tar.tagName.toLowerCase();
if (tagName === "button") {
const status = tar.dataset.status;
modalFactory.create(status as MType); //通过传入的状态来自动实例化相应的类
}
};
这样,我们就可以愉快地扩展功能了。
现在我们一起来总结一下什么是工厂模式:工厂模式其实就是将创建对象的过程单独封装。顺便我们还能无脑传参!
美的自动感应洗手机 OXS-2800
知乎
¥99.00
去购买