对设计模式的理解?常见的设计模式有哪些?
设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下,要怎么解决问题的一种方案; 设计模式能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免会引起麻烦的紧耦合,以增强软件设计面对并适应变化的能力。
1、单列模式(常用)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。 如只有一辆,一旦借出去则不能再借给别人:
使用场景:常用于创建全局唯一的对象,例如全局的状态管理器、日志记录器等。
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
createInstance() {
const object = { name: "example" };
return object;
}
getInstance() {
if (!Singleton.instance) {
Singleton.instance = this.createInstance();
}
return Singleton.instance;
}
}
// 使用示例
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true
2、工厂模式(重点)
工厂模式是用来创建对象的一种最常用的设计模式,不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂
其就像工厂一样重复的产生类似的产品,工厂模式只需要我们传入正确的参数,就能生产类似的对象。根据参数的不同创建不同对象。
工厂模式通常会分成3个角色:
- 工厂角色-负责实现创建所有实例的内部逻辑。
- 抽象产品角色-是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
- 具体产品角色-是创建目标,所有创建的对象都充当这个角色的某个具体类的实例
3、策略模式
策略模式,就是定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。 举个例子,公司的年终奖是根据员工的工资和绩效来考核的,绩效为A的人,年终奖为工资的4倍,绩效为B的人,年终奖为工资的3倍,绩效为C的人,年终奖为工资的2倍 至少分成两部分:
- 策略类(可变),策略类封装了具体的算法,并负责具体的计算过程
- 环境类(不变),接受客户的请求,随后将请求委托给某一个策略类
4、代理模式
为对象提供一个代用品或占位符,以便控制对它的访问。
例如实现图片懒加载的功能,先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面
// es6 新增Proxy可以直接实现代理模式
const target = {
method() {
console.log("Target method.");
}
};
const proxy = new Proxy(target, {
get(target, prop) {
console.log(`Called ${prop} method.`);
return target[prop];
}
});
// 使用示例
proxy.method(); // "Called method method. Target method."
5、发布订阅、观察者模式(重点)
一、观察者模式
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {
console.log(`Received data: ${data}`);
}
}
// 使用示例
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify("Hello World!");
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。
如:订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸
二、发布订阅模式
发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在
同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在。
三、区别
- 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)
6、适配器模式
适配器模式是一种将不同接口转换成统一接口的模式。
使用场景:常用于实现不同浏览器的兼容、不同数据格式的转换等。
class Adaptee {
specificRequest() {
return "适配者中的业务代码被调用";
}
}
class Target {
constructor() {
this.adaptee = new Adaptee();
}
request() {
let info = this.adaptee.specificRequest();
return `${info} - 转换器 - 适配器代码被调用`;
}
}
// 使用示例
let target = new Target();
var res = target.request();
console.log(res) // "适配者中的业务代码被调用 - 转换器 - 适配器代码被调用"
7、MVC 模式
MVC模式是一种将应用程序分为三个部分:模型、视图和控制器
使用场景:常用于实现数据的管理、页面的渲染和交互的处理等。
class Model {
constructor() {
this.data = {
name: "example",
age: 18,
gender: "male"
};
}
setData(key, value) {
this.data[key] = value;
}
getData() {
return this.data;
}
}
class View {
constructor() {
this.container = document.createElement("div");
}
render(data) {
const { name, age, gender } = data;
this.container.innerHTML = `
<p>Name: ${name}</p>
<p>Age: ${age}</p>
<p>Gender: ${gender}</p>
`;
document.body.appendChild(this.container);
}
}
class Controller {
constructor(model, view) {
this.model = model;
this.view = view;
this.view.render(this.model.getData());
}
setData(key, value) {
this.model.setData(key, value);
this.view.render(this.model.getData());
}
}
// 使用示例
const model = new Model();
const view = new View();
const controller = new Controller(model, view);
controller.setData("age", 20);