说到设计模式,大家想到的就是六大原则,23种模式。这么多模式,并非都要记住,但作为前端开发,对于前端出现率高的设计模式还是有必要了解并掌握的,浅浅掌握9种模式后,整理了这份文章。
那么,我们先了解六大原则
六大原则:
- 依赖倒置原则(Dependence Inversion Principle):高层(业务层)不应该直接调用底层(基础层)模块
- 开闭原则(Open Close Principle):单模块对拓展开放、对修改关闭
- 单一原则(Single Responsibility Principle):单模块负责的职责必须是单一的
- 迪米特法则(Law of Demeter):对外暴露接口应该简单
- 接口隔离原则(Interface Segregation Principle):单个接口(类)都应该按业务隔离开
- 里氏替换原则(Liskov Substitution Principle):子类可以替换父类
六大原则也可以用六个字替换:高内聚低耦合。
- 高层不直接依赖底层:依赖倒置原则
- 内部修改关闭,外部开放扩展:开闭原则
- 聚合单一功能:单一原则
- 低知识接口,对外接口简单:迪米特法则
- 耦合多个接口,不如隔离拆分:接口隔离原则
- 合并复用,子类可以替换父类:里氏替换原则
我们采用模式编写时,要尽可能遵守这六大原则
下面我们一起来看看前端常用的一些设计模式
1. 单例模式
单例模式是一种只允许创建一个实例的模式。在前端开发中,常用于创建全局唯一的对象,例如全局的状态管理器、日志记录器等。单例模式可以保证全局只有一个实例,避免了重复创建和资源浪费的问题。
单例模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class applicationStation { constructor() { this.state = 'off' } play() { if (this.state === 'on') { console.log('已打开') return } this.state = 'on' } shutdown() { if (this.state === 'off') { console.log('已关闭') return } this.state = 'off' } } window.applicationStation = new applicationStation()
const application1 = window.applicationStation const application2 = window.applicationStation
|
以上代码是单例模式的一个示例,通过该模式可以保证全局只有一个实例,避免了重复创建和资源浪费的问题。在这个示例中,Singleton 类只能创建一个实例,如果多次创建,返回的都是同一个实例,因此 instance1 和 instance2 的值是相等的。单例模式常用于创建全局唯一的对象,例如全局的状态管理器、日志记录器等。
2. 工厂模式
工厂模式是一种根据参数的不同创建不同对象的模式。在前端开发中,常用于创建不同类型的组件、插件等。工厂模式可以将对象的创建和使用分离,提高代码的灵活性和可维护性。
工厂模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class setPerson { constructor(obj) { this.pesonObj = obj } creatCard() { } otherFynction(){ } } class Person { constructor(obj) { return new setPerson(obj) } } const person = new Person() const card = person.creatCard({ name:'张三', age:'20', department:'人力资源部门' })
|
以上代码中,Product 类表示要创建的产品,ProductFactory 类实现了工厂模式,通过 createProduct 方法创建产品实例。在使用时,可以通过工厂类创建产品实例,而不需要直接调用产品类的构造函数。通过工厂模式可以将对象的创建和使用分离,提高代码的灵活性和可维护性。
3. 观察者模式
观察者模式是一种对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动更新。在前端开发中,常用于实现事件监听和消息订阅等。观察者模式可以降低对象间的耦合度,提高代码的可读性和可复用性。
观察者模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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 类实现了观察者模式,通过 addObserver 方法添加观察者,通过 notify 方法通知观察者,触发其 update 方法。Observer 类实现了具体的观察者,通过 update 方法接收数据并进行处理。
4. 装饰器模式
装饰器模式是一种在不改变对象自身的基础上,动态地给对象增加新的功能的模式。在前端开发中,常用于实现组件的复用和功能的增强等。装饰器模式可以避免类的继承带来的复杂性和耦合度,提高代码的灵活性和可维护性。
装饰器模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| interface Component { operation(): void; }
class ConcreteComponent implements Component { public operation(): void { console.log("ConcreteComponent: operation."); } }
class Decorator implements Component { protected component: Component;
constructor(component: Component) { this.component = component; }
public operation(): void { console.log("Decorator: operation."); this.component.operation(); } }
class ConcreteDecoratorA extends Decorator { public operation(): void { super.operation(); console.log("ConcreteDecoratorA: operation."); } }
class ConcreteDecoratorB extends Decorator { public operation(): void { super.operation(); console.log("ConcreteDecoratorB: operation."); } }
const concreteComponent = new ConcreteComponent(); const concreteDecoratorA = new ConcreteDecoratorA(concreteComponent); const concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
concreteDecoratorB.operation();
|
以上代码中,Component 接口定义了装饰器模式中的组件的基本操作,ConcreteComponent 类实现了具体的组件,Decorator 类实现了装饰器的基本操作,并通过 protected 属性持有被装饰的组件。ConcreteDecoratorA 和 ConcreteDecoratorB 类分别实现了具体的装饰器操作。在使用时,可以通过多次装饰对象来增加功能,而不需要直接修改原始对象的代码。通过装饰器模式可以动态地增加对象的功能,提高代码的灵活性和可复用性。
5. 代理模式
代理模式是一种通过一个代理对象控制对目标对象的访问的模式。在前端开发中,常用于实现图片懒加载、数据缓存等。代理模式可以保护目标对象,控制其访问和使用,提高代码的安全性和可读性。
代理模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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();
|
以上代码中,target 对象实现了原始功能,proxy 对象实现了代理功能,通过 new Proxy() 创建代理对象。代理对象通过 get 方法拦截对目标对象方法的访问,并在控制台输出信息。通过代理模式可以控制对目标对象的访问和使用,实现数据缓存、权限控制等功能,提高代码的可读性和可维护性。
6. 适配器模式
适配器模式是一种将不同接口转换成统一接口的模式。在前端开发中,常用于实现不同浏览器的兼容、不同数据格式的转换等。适配器模式可以降低系统间的耦合度,提高代码的复用性和可维护性。
适配器模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Adaptee { specificRequest() { return "适配者中的业务代码被调用"; } }
class Target { constructor() { this.adaptee = new Adaptee(); }
request() { let info = this.adaptee.specificRequest(); return `${info} - 转换器 - 适配器代码被调用`; } }
let target = new Target(); target.request();
|
以上代码中,Adaptee 类实现了原始的业务代码,Target 类实现了适配器代码,并通过 new Adaptee() 创建了一个 Adaptee 对象用于调用原始业务代码。在 request 方法中,首先调用 Adaptee 对象的 specificRequest 方法获取原始业务代码的信息,然后进行转换并返回。通过适配器模式可以将原始业务代码和适配器代码分开实现,提高代码的可维护性和可复用性。
7. MVC模式
MVC模式是一种将应用程序分为三个部分:模型、视图和控制器。在前端开发中,常用于实现数据的管理、页面的渲染和交互的处理等。MVC模式可以降低代码的复杂度,提高代码的可维护性和可测试性。
MVC模式的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 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);
|
以上代码中,Model 类实现了应用程序的数据管理,View 类实现了应用程序的视图展示,Controller 类实现了视图和数据的绑定和交互处理。在 Controller 类中,通过调用 model.setData 方法和 view.render 方法实现了数据的修改和页面的重新渲染。通过MVC模式可以将应用程序分解为不同的部分,提高代码的可维护性和可测试性。