自己敲一遍代码,我理解了常见的设计模式
万物皆对象,没有对象的自己new一个。
结合实际生活经历,理解设计模式,根据自己的理解再写一遍代码更容易理解其思想。
单例模式
定义:一个类只能构造出唯一实例
意义:共享、唯一,例如:redux/vuex 中的 store、JQ 中的$、业务场景中的购物车登录框都是单例模式的应用
单例模式出来的对象可以理解为官网唯一代理(只能创建一个实例),其他代理都是假冒的。
手写一个单例类:
class SingletonLogin {
constructor(name, password) {
this.name = name;
this.password = password;
}
static getInstance(name, password) {
//判断对象是否已经被创建,若创建则返回旧对象
if (!this.instance) {
this.instance = new SingletonLogin(name, password);
}
return this.instance;
}
}
const user1 = new SingletonLogin("张三", "vhsj");
const user2 = new SingletonLogin("李四", "lisi");
const user3 = SingletonLogin.getInstance("张三", "vhsj");
const user4 = SingletonLogin.getInstance("李四", "lisi");
console.log(user1, user2);
// SingletonLogin { name: '张三', password: 'vhsj' } SingletonLogin { name: '李四', password: 'lisi' }
console.log(user3, user4);
// SingletonLogin { name: '张三', password: 'vhsj' } SingletonLogin { name: '张三', password: 'vhsj' }
console.log(user3 === user4);
// true
工厂模式
工厂模式:对创建对象逻辑的封装,或者说对new
的封装,就像是创建对象的一个工厂,故名:工厂模式。
我们不生产实例,我们只是实例的加工厂。
工厂模式一般应用于大型应用,应用工厂模式的例子:
- JQ 中的
$
,之所以没有用new selector
,是因为$()
已经是一个工厂方法; React.createElement()
;Vue.conponent()
- 工厂模式分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
简单工厂模式:
class User {
constructor(name, auth) {
this.name = name;
this.auth = auth;
}
}
class UserFactory {
static createUser(name, auth) {
// 工厂内部封装创建对象的逻辑:
// 权限为admin时,创建对象的auth = 1;权限为user时,auth = 2
// 使用者在外部创建对象的时候不需要关心权限与字段的对应关系,不需要知道赋权的所有逻辑。只需要知道创建了一个管理员或者用户
if (auth === "admin") {
// 赋权逻辑...
return new User(name, 1);
}
if (auth === "user") {
// 赋权逻辑...
return new User(name, 2);
}
}
}
const admin = UserFactory.createUser("lisa", "admin");
const user = UserFactory.createUser("bob", "user");
console.log(admin, user);
观察者模式
概念:观察者监听被观察者的变化,被观察者发生改变时,通知所有的观察者。观察者模式广泛应用于事件监听的实现。
观察者模式:
// 观察者
class Observer {
constructor(fn) {
this.update = fn;
}
}
// 被观察者
class Subject {
constructor() {
this.observerList = [];
}
// 添加观察者
addObserver(observer) {
this.observerList.push(observer);
}
// 被观察者发出广播
notify() {
this.observerList.forEach((o) => {
o.update();
});
}
}
// 创建观察者
const observer_1 = new Observer(() => {
console.log("观察者1 更新了");
});
const observer_2 = new Observer(() => {
console.log("观察者2也更新了");
});
// 创建被观察者实例
const subjectIns = new Subject();
// 给被观察者添加观察者,或者说,观察者订阅被观察者的通知
subjectIns.addObserver(observer_1);
subjectIns.addObserver(observer_2);
// 被观察者发出广播
subjectIns.notify();
秒啊,设计模式的思想只有代码自己写一遍,边写边理解才能正真理解!!!
观察者模式或被称为发布订阅模式,其间区别在于:发布订阅模式比观察者模式多了一个调度中心。
观察者模式也是前端最常用的设计模式
装饰器模式
装饰器模式,对类的一个包装————动态地拓展类的功能。ES7 的装饰器语法,以及 React 中的*高阶组件(HoC)*都是装饰器模式的实现。
挂牌包装公司,工厂生产出来的产品,公司负责挂牌销售。
ES7 的装饰器用法:
function info(target) {
target.property.name = "装饰器添加的name: lisa";
target.property.age = "装饰器添加的age: 16";
}
@info
class Person {}
const p = new Person();
console.log(p);
适配器模式
适配器模式将一个接口准换另一个接口,使其不兼容的类可以一起工作。使用场景就是使旧的接口适配之后适用与新的场景。
生活示例:我有一个微单的相机,但是想用单反相机的镜头,这个时候就需要一个镜头转换器————适配器模式
相机代码实例:
// 我的相机功能
class Microorder {
constructor() {}
takePhoto() {
// 原来接口的功能
return "使用微单相机拍了一张照片";
}
}
// 适配器功能
class Adapter {
constructor() {
this.camera = new Microorder();
}
takePhoto() {
// 适配后接口的功能
return "适配单反镜头," + this.camera.takePhoto();
}
}
const newCamera = new Adapter(Microorder);
console.log(newCamera.takePhoto());
// 适配单反镜头,使用微单相机拍了一张照片
代理模式
代理模式:为一个对象找一个替代对象,便于控制对原对象的访问,这个替代对象就是访问者与目标对象(原对象)的代理。
生活示例:代理相当于明星的经纪人。一个品牌商想找这个明星代言,品牌商需要先联系经纪人,经纪人会先考察品牌商的资质以及明星的排期。经纪人(代理)替明星(目标对象)过滤一遍不确定的信息。
常用代理:事件代理、JQ的$.proxy
、ES6 中的 proxy 都是代理模式实现
ES6 中的代理:
const star = {
name: "Alice",
age: 37,
phone: "10010",
price: "1000000",
};
const agent = new Proxy(star, {
get(target, key) {
if (key === "phone") {
return "经纪人电话:10086";
} else {
return target[key];
}
},
set(target, key, value) {
if (key === "price") {
if (value < target[key]) {
throw new Error("报价太少了");
} else {
target[key] = value;
}
}
},
});
console.log(agent.phone);
// 10086
agent.price = 999999;
// Error: 报价太少了
vue3 的数据双向绑定就是利用的 Proxy 代理
最后是一些开发中遵循的原则
- 单一职责原则:一个类只负责一个功能领域的相应职责。或者说:就一个类而言,只有一个引起其变化的因素。
- 开放封闭原则:核心思想是软件实体(类、模块、函数)是可扩展的、但不可以修改。或者说:对扩展是开放的,对修改是封闭的。
总结
要理解就要经历,看千万文章抵不过根据自己理解的思路写一遍过程。类似康奈尔学习法。