前端工程中的设计模式应用(下)

简介: 前端工程中的设计模式应用(下)

更多精彩内容,欢迎观看:

前端工程中的设计模式应用(中):https://developer.aliyun.com/article/1396343


  • 备忘录模式


备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态,允许生成对象状态的快照并在以后将其还原,它不会影响它所处理的对象的内部结构, 也不会影响快照中保存的数据。
备忘录模式结构:

代码示例:

class Originator {
    private state: string;
    constructor(state: string) {
        this.state = state;
        console.log(`Originator: My initial state is: ${state}`);
    }
    public doSomething(): void {
        console.log('Originator: I\'m doing something important.');
        this.state = this.generateRandomString(30);
        console.log(`Originator: and my state has changed to: ${this.state}`);
    }
    private generateRandomString(length: number = 10): string {
        const charSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        return Array
            .apply(null, { length })
            .map(() => charSet.charAt(Math.floor(Math.random() * charSet.length)))
            .join('');
    }
    public save(): Memento {
        return new ConcreteMemento(this.state);
    }
    public restore(memento: Memento): void {
        this.state = memento.getState();
        console.log(`Originator: My state has changed to: ${this.state}`);
    }
}
interface Memento {
    getState(): string;
    getName(): string;
    getDate(): string;
}
class ConcreteMemento implements Memento {
    private state: string;
    private date: string;
    constructor(state: string) {
        this.state = state;
        this.date = new Date().toISOString().slice(0, 19).replace('T', ' ');
    }
    public getState(): string {
        return this.state;
    }
    public getName(): string {
        return `${this.date} / (${this.state.substr(0, 9)}...)`;
    }
    public getDate(): string {
        return this.date;
    }
}
class Caretaker {
    private mementos: Memento[] = [];
    private originator: Originator;
    constructor(originator: Originator) {
        this.originator = originator;
    }
    public backup(): void {
        console.log('\nCaretaker: Saving Originator\'s state...');
        this.mementos.push(this.originator.save());
    }
    public undo(): void {
        if (!this.mementos.length) {
            return;
        }
        const memento = this.mementos.pop();
        console.log(`Caretaker: Restoring state to: ${memento.getName()}`);
        this.originator.restore(memento);
    }
    public showHistory(): void {
        console.log('Caretaker: Here\'s the list of mementos:');
        for (const memento of this.mementos) {
            console.log(memento.getName());
        }
    }
}
// 客户端代码
const originator = new Originator('Super-duper-super-puper-super.');
const caretaker = new Caretaker(originator);
caretaker.backup();
originator.doSomething();
caretaker.backup();
originator.doSomething();
caretaker.backup();
originator.doSomething();
console.log('');
caretaker.showHistory();
console.log('\nClient: Now, let\'s rollback!\n');
caretaker.undo();
console.log('\nClient: Once more!\n');
caretaker.undo();
Originator: My initial state is: Super-duper-super-puper-super.
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: iaVCJVryJwWwbipieensfodeMSWvUY
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: oSUxsOCiZEnohBMQEjwnPWJLGnwGmy
Caretaker: Here's the list of mementos:
2019-02-17 15:14:05 / (Super-dup...)
2019-02-17 15:14:05 / (qXqxgTcLS...)
2019-02-17 15:14:05 / (iaVCJVryJ...)
Client: Now, let's rollback!
Caretaker: Restoring state to: 2019-02-17 15:14:05 / (iaVCJVryJ...)
Originator: My state has changed to: iaVCJVryJwWwbipieensfodeMSWvUY
Client: Once more!
Caretaker: Restoring state to: 2019-02-17 15:14:05 / (qXqxgTcLS...)
Originator: My state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo
  • 策略模式


策略模式是一种行为设计模式, 能让定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
举个现实的例子,比如赶火车,到达火车站的策略有公交车、地铁、出租车等,我们可以根据预算或时间等因素选择其中的一种策略。
策略模式结构:

代码示例:

class Context {
    private strategy: Strategy;
    constructor(strategy: Strategy) {
        this.strategy = strategy;
    }
    public setStrategy(strategy: Strategy) {
        this.strategy = strategy;
    }
    public doSomeBusinessLogic(): void {
        console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');
        const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);
        console.log(result.join(','));
    }
}
interface Strategy {
    doAlgorithm(data: string[]): string[];
}
class ConcreteStrategyA implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.sort();
    }
}
class ConcreteStrategyB implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.reverse();
    }
}
// 客户端代码
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();
console.log('');
console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e
Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
  • 模板方法模式


模板方法模式是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
举个现实的例子,农村自建房有一套固定的模版方法(比如为:打地基 -> 建造主体 -> 封顶 -> 铺设水电 -> 装修),但这其中可提供几个扩展点, 允许潜在的房屋业主调整成品房屋的部分细节,如装修风格,这使得成品房屋会有不同。

模版方法模式结构:

代码示例:

abstract class AbstractClass {
    public templateMethod(): void {
        this.baseOperation1();
        this.requiredOperations1();
        this.baseOperation2();
        this.hook1();
        this.requiredOperation2();
        this.baseOperation3();
        this.hook2();
    }
    protected baseOperation1(): void {
        console.log('AbstractClass says: I am doing the bulk of the work');
    }
    protected baseOperation2(): void {
        console.log('AbstractClass says: But I let subclasses override some operations');
    }
    protected baseOperation3(): void {
        console.log('AbstractClass says: But I am doing the bulk of the work anyway');
    }
    protected abstract requiredOperations1(): void;
    protected abstract requiredOperation2(): void;
    protected hook1(): void { }
    protected hook2(): void { }
}
class ConcreteClass1 extends AbstractClass {
    protected requiredOperations1(): void {
        console.log('ConcreteClass1 says: Implemented Operation1');
    }
    protected requiredOperation2(): void {
        console.log('ConcreteClass1 says: Implemented Operation2');
    }
}
class ConcreteClass2 extends AbstractClass {
    protected requiredOperations1(): void {
        console.log('ConcreteClass2 says: Implemented Operation1');
    }
    protected requiredOperation2(): void {
        console.log('ConcreteClass2 says: Implemented Operation2');
    }
    protected hook1(): void {
        console.log('ConcreteClass2 says: Overridden Hook1');
    }
}
// 客户端代码
function clientCode(abstractClass: AbstractClass) {
    // ...
    abstractClass.templateMethod();
    // ...
}
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass1());
console.log('');
console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass2());
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
  • 职责链模式


职责链模式是一种行为设计模式, 允许将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。


职责链模式结构:


代码示例:

interface Handler {
    setNext(handler: Handler): Handler;
    handle(request: string): string;
}
abstract class AbstractHandler implements Handler
{
    private nextHandler: Handler;
    public setNext(handler: Handler): Handler {
        this.nextHandler = handler;
        return handler;
    }
    public handle(request: string): string {
        if (this.nextHandler) {
            return this.nextHandler.handle(request);
        }
        return null;
    }
}
class MonkeyHandler extends AbstractHandler {
    public handle(request: string): string {
        if (request === 'Banana') {
            return `Monkey: I'll eat the ${request}.`;
        }
        return super.handle(request);
    }
}
class SquirrelHandler extends AbstractHandler {
    public handle(request: string): string {
        if (request === 'Nut') {
            return `Squirrel: I'll eat the ${request}.`;
        }
        return super.handle(request);
    }
}
class DogHandler extends AbstractHandler {
    public handle(request: string): string {
        if (request === 'MeatBall') {
            return `Dog: I'll eat the ${request}.`;
        }
        return super.handle(request);
    }
}
function clientCode(handler: Handler) {
    const foods = ['Nut', 'Banana', 'Cup of coffee'];
    for (const food of foods) {
        console.log(`Client: Who wants a ${food}?`);
        const result = handler.handle(food);
        if (result) {
            console.log(`  ${result}`);
        } else {
            console.log(`  ${food} was left untouched.`);
        }
    }
}
// 客户端代码
const monkey = new MonkeyHandler();
const squirrel = new SquirrelHandler();
const dog = new DogHandler();
monkey.setNext(squirrel).setNext(dog);
console.log('Chain: Monkey > Squirrel > Dog\n');
clientCode(monkey);
console.log('');
console.log('Subchain: Squirrel > Dog\n');
clientCode(squirrel);
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
  Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
  Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut?
  Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
  Banana was left untouched.
Client: Who wants a Cup of coffee?
  Cup of coffee was left untouched.
  • 命令模式


命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。


命令模式结构:


代码示例:

interface Command {
    execute(): void;
}
class SimpleCommand implements Command {
    private payload: string;
    constructor(payload: string) {
        this.payload = payload;
    }
    public execute(): void {
        console.log(`SimpleCommand: See, I can do simple things like printing (${this.payload})`);
    }
}
class ComplexCommand implements Command {
    private receiver: Receiver;
    private a: string;
    private b: string;
    constructor(receiver: Receiver, a: string, b: string) {
        this.receiver = receiver;
        this.a = a;
        this.b = b;
    }
    public execute(): void {
        console.log('ComplexCommand: Complex stuff should be done by a receiver object.');
        this.receiver.doSomething(this.a);
        this.receiver.doSomethingElse(this.b);
    }
}
class Receiver {
    public doSomething(a: string): void {
        console.log(`Receiver: Working on (${a}.)`);
    }
    public doSomethingElse(b: string): void {
        console.log(`Receiver: Also working on (${b}.)`);
    }
}
class Invoker {
    private onStart: Command;
    private onFinish: Command;
    public setOnStart(command: Command): void {
        this.onStart = command;
    }
    public setOnFinish(command: Command): void {
        this.onFinish = command;
    }
    public doSomethingImportant(): void {
        console.log('Invoker: Does anybody want something done before I begin?');
        if (this.isCommand(this.onStart)) {
            this.onStart.execute();
        }
        console.log('Invoker: ...doing something really important...');
        console.log('Invoker: Does anybody want something done after I finish?');
        if (this.isCommand(this.onFinish)) {
            this.onFinish.execute();
        }
    }
    private isCommand(object): object is Command {
        return object.execute !== undefined;
    }
}
// 客户端代码
const invoker = new Invoker();
invoker.setOnStart(new SimpleCommand('Say Hi!'));
const receiver = new Receiver();
invoker.setOnFinish(new ComplexCommand(receiver, 'Send email', 'Save report'));
invoker.doSomethingImportant();
Invoker: Does anybody want something done before I begin?
SimpleCommand: See, I can do simple things like printing (Say Hi!)
Invoker: ...doing something really important...
Invoker: Does anybody want something done after I finish?
ComplexCommand: Complex stuff should be done by a receiver object.
Receiver: Working on (Send email.)
Receiver: Also working on (Save report.)

参考文献


Alexander Shvets, Dive-into Design Patterns[M], Finelybook, 2019.


团队介绍

我们是大淘宝技术-营销与平台策略技术-营销产品团队,主要负责淘宝好价、百亿补贴、聚划算、天天特卖等大淘宝核心产品及各类大型的营销会场活动,同时我们也是前端智能化的先锋队,有方舟、千帆、imgcook、智能UI等多种技术产品。


相关文章
|
13天前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
|
19天前
|
设计模式 存储 供应链
前端必须掌握的设计模式——观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,实现了一种订阅机制。它包含两个角色:**观察者**(订阅消息、接收通知并执行操作)和**被观察者**(维护观察者列表、发送通知)。两者通过一对多的关系实现解耦,当被观察者状态改变时,会通知所有订阅的观察者。例如,商店老板作为被观察者,记录客户的需求并在商品到货时通知他们。前端应用中,如DOM事件注册、MutationObserver等也体现了这一模式。
|
1月前
|
设计模式 前端开发 调度
前端必须掌握的设计模式——工厂模式
工厂模式是一种创建型设计模式,通过工厂媒介提供统一接口来创建对象,客户端只需告知创建需求,具体逻辑由工厂处理。工厂模式分为简单工厂、标准工厂和抽象工厂,分别适用于不同场景下的对象创建需求。简单工厂利用静态方法创建对象,标准工厂通过具体工厂类减少耦合,抽象工厂则用于创建一系列相关或依赖对象的家族。
|
12天前
|
设计模式 存储 缓存
前端必须掌握的设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,旨在将多分支复杂逻辑解耦。每个分支类只关心自身实现,无需处理策略切换。它避免了大量if-else或switch-case代码,符合开闭原则。常见应用场景包括表单验证、风格切换和缓存调度等。通过定义接口和上下文类,策略模式实现了灵活的逻辑分离与扩展。例如,在国际化需求中,可根据语言切换不同的词条包,使代码更加简洁优雅。总结来说,策略模式简化了多条件判断,提升了代码的可维护性和扩展性。
|
17天前
|
设计模式 消息中间件 供应链
前端必须掌握的设计模式——发布订阅模式
发布订阅模式(Publish-Subscribe Pattern)是一种设计模式,类似于观察者模式,但通过引入第三方中介实现发布者和订阅者的解耦。发布者不再直接通知订阅者,而是将消息发送给中介,由中介负责分发给订阅者。这种方式提高了异步支持和安全性,适合复杂、高并发场景,如消息队列和流处理系统。代码实现中,通过定义发布者、订阅者和中介接口,确保消息的正确传递。此模式在前端开发中广泛应用,例如Vue的数据双向绑定。
|
29天前
|
设计模式 前端开发 JavaScript
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
|
23天前
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式
|
1月前
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
|
26天前
|
设计模式 JSON 前端开发
前端必须掌握的设计模式——适配器模式
适配器模式是一种结构型设计模式,用于使接口不兼容的对象能够相互合作。通过在客户端和系统之间引入一个“中间层”适配器,将不同类型的输入数据转换为系统能处理的标准格式,减轻系统的负担,提高扩展性和可维护性。例如,MacBook的扩展坞将多种接口(如HDMI、USB)转换为Type-C接口,实现多接口兼容。
|
2月前
|
JavaScript 前端开发 测试技术
构建高效可维护的前端应用
构建高效可维护的前端应用