我们经常听说:“写代码要有良好的封装,要高内聚,低耦合”。那怎样才算良好的封装,我们为什么要封装呢?其实封装有这样几个好处:
封装好的代码,内部变量不会污染外部。可以作为一个模块给外部调用。外部调用者不需要知道实现的细节,只需要按照约定的规范使用就行了。对扩展开放,对修改关闭,即开闭原则。外部不能修改模块,既保证了模块内部的正确性,又可以留出扩展接口,使用灵活。
JS 生态已经有很多模块了,有些模块封装得非常好,我们使用起来很方便,比如 jQuery,Vue 等。如果我们仔细去看这些模块的源码,我们会发现他们的封装都是有规律可循的。这些规律总结起来就是设计模式,用于代码封装的设计模式主要有工厂模式
,创建者模式
,单例模式
,原型模式
四种。下面我们结合一些框架源码来看看这四种设计模式:
工厂模式的名字就很直白,封装的模块就像一个工厂一样批量的产出需要的对象。常见工厂模式的一个特征就是调用的时候不需要使用new
,而且传入的参数比较简单。但是调用次数可能比较频繁,经常需要产出不同的对象,频繁调用时不用new
也方便很多。一个工厂模式的代码结构如下所示:
function factory(type) {
switch(type) {
case 'type1':
return new Type1();
case 'type2':
return new Type2();
case 'type3':
return new Type3();
}
}
上述代码中,我们传入了type
,然后工厂根据不同的type
来创建不同的对象。
下面来看看用工厂模式的例子,假如我们有如下需求:
我们项目需要一个弹窗,弹窗有几种:消息型弹窗,确认型弹窗,取消型弹窗,他们的颜色和内容可能是不一样的。
针对这几种弹窗,我们先来分别建一个类:
function infoPopup(content, color) {}
function confirmPopup(content, color) {}
function cancelPopup(content, color) {}
如果我们直接使用这几个类,就是这样的:
let infoPopup1 = new infoPopup(content, color);
let infoPopup2 = new infoPopup(content, color);
let confirmPopup1 = new confirmPopup(content, color);
...
每次用的时候都要去new
对应的弹窗类,我们用工厂模式改造下,就是这样:
// 新加一个方法popup把这几个类都包装起来
function popup(type, content, color) {
switch(type) {
case 'infoPopup':
return new infoPopup(content, color);
case 'confirmPopup':
return new confirmPopup(content, color);
case 'cancelPopup':
return new cancelPopup(content, color);
}
}
然后我们使用popup
就不用new
了,直接调用函数就行:
let infoPopup1 = popup('infoPopup', content, color);
上述代码虽然实现了工厂模式,但是switch
始终感觉不是很优雅。我们使用面向对象改造下popup
,将它改为一个类,将不同类型的弹窗挂载在这个类上成为工厂方法: