工厂模式

  1. /* 工厂模式 */
  2. // 没有设计模式,如果以后增加有新增,都需要新的class
  3. class CannedOrange_3401 {}
  4. class CannedApple_3401 {}
  5. function eatCan_3401(c: CannedOrange_3401 | CannedApple_3401) {
  6. }
  7. // 每次都要创建对应的,冗余
  8. let o = new CannedOrange_3401();
  9. eatCan_3401(o)
  10. // 采用设计模式 工厂模式
  11. // 先定义抽象类,然后继承抽象类
  12. abstract class Can_3402 {}
  13. class CannedOrange_3402 extends Can_3402 {}
  14. class CannedApple_3402 extends Can_3402 {}
  15. // 定义类型别名
  16. type CanType_3402 = 'orange' | 'apple';
  17. // 通过工厂模式,对传入的类型别名进行判断,然后返回对应的实例
  18. class CanFactory_3402 {
  19. static create(type: CanType_3402) {
  20. if (type === 'orange') {
  21. return new CannedOrange_3402();
  22. } else if (type === 'apple') {
  23. return new CannedApple_3402();
  24. }
  25. }
  26. }
  27. // 工厂模式,使用示例
  28. let p = CanFactory_3402.create('apple');

image.png

答案

1
2

解析

工厂模式常用的实例化对象模式,用于批量生产对象。
题目使用了工厂模式。大家可以仔细看看哦。解决本题的思路是类的静态属性,所有的实例共享类的静态属性。所有编号分别为 1,2。

单例模式

/* 单例模式 */
class DiaLog_3403 {
    private static instance: DiaLog = null;
    static getInstance() {
        if (this.instance === null) {
            this.instance = new DiaLog();
        }
        return this.instance;
    }
    // 仅允许通过 Dialog.getInstance() 获取全局唯一实例
    private constructor() {}
}

const a_3403 = new DiaLog_3403; // 报错,创建一个实例
const b_3403 = new DiaLog_3403; // 报错,只能获取一个实例

const c_3403 = DiaLog_3403.getInstance();
const d_3403 = DiaLog_3403.getInstance();

console.log(c_3403 === d_3403); // true

image.png

适配器模式

/* 适配器模式 */
// 业务改造前的接口
import Api from "./3-4/Api";
import getInfo from "./3-4/getInfo";

let api = new Api();
getInfo(api);

// 业务改造后为newApi
import NewApi from "./3-4/NewApi";
let newApi = new NewApi();
getInfo(newApi);// 报错,因api与NewApi接口不一致,此时需要适配器
import Api from "./Api";
import NewApi from "./NewApi";

export default class NewApiAdapter extends Api {
    newApi: NewApi;

    constructor(newApi: NewApi) {
        super();
        this.newApi = newApi;
    }

    getName(): void {    //getName为原Api,最终设配为新NewApi
        return this.newApi.get_Name();
    }
    getAge(): void {
        return this.newApi.get_Age();
    }
    getGender(): void {
        return this.newApi.get_Gender();
    }
}
// 正确最终的写法
// import Api_3405 from "./3-4_Api"; // 不需要原api了
import getInfo_3405 from "./3-4/getInfo";   // 封装函数无需改变
import NewApi_3405 from "./3-4/NewApi";
import NewApiAdapter_3405 from "./3-4/NewApAdapter";

let newApi_3405 = new NewApi_3405();
let apiAdapter_3405 = new NewApiAdapter(newApi);
getInfo(apiAdapter_3405);

image.png

答案

3
6

解析

适配器模式主要作用使两个不兼容的东西可以兼容工作。
第一版和第二版分别是两个版本的累加求和器,由于数据源数据类型不同,我们需要选择不同的版本。现在引入适配器兼容版,我们不需要关心数据源的数据类型,直接使用 sumV3 进行求和即可。sumV3 内部不做重新实现,直接使用原有的版本处理即可。答案比较简单 3, 6。

装饰器模式

不侵入原有的类的情况、改变或拓展它

// 原有的类
export default class Phone_3406 {
    getPrice() {
        return 1000;
    }
    call() {
        console.log('calling');
    }
}
import Phone from "./Phone";

// 创建装饰器
// 添加手机壳
export default function addCase(P: typeof Phone) {
    return class extends P {
        getPrice() {
            return super.getPrice() + 100;
        }
    }
}
// 使用装饰器,最后代码调整为
import  addCase  from "./addCase";

@addCase
export default class Phone_3406 {   //报错 @语法是实验性功能,tsconfig的experimentalDecorators 为true
    getPrice() {
        return 1000;
    }
    call() {
        console.log('calling');
    }
}
// 最后使用并创建实例
import Phone from "./P3/3-4/Phone";
let p = new Phone();
console.log(p.getPrice());    //1100

image.png

答案

20

解析

装饰者模式在不改变对象本身的情况下,在程序运行阶段为对象动态增加功能。
ES6 开始用 @ 标识装饰器,即 @decorator,题目中的类 animalDecorator 是一个类装饰器,类装饰器会在运行时当作函数被调用,类的构造函数将作为其唯一的参数。
装饰器中使用了泛型相关知识 <>(我们将在泛型章节详细介绍)。
为了更好的理解装饰器,我们看看 tsc 编译后的核心代码

const animalDecorator = (constructor) => {
    return class extends constructor {
        constructor() {
            super(...arguments);
            this.age = 20;
        }
    };
};
let Animal = class Animal {
    constructor(age) {
        this.age = age;
    }
};
Animal = __decorate([
    animalDecorator
], Animal);
new Animal(10).age;

类装饰器 animalDecorator 返回了一个匿名类,记做匿名类 A 下面简称类 A 。类 A 继承于类 Animal 的构造函数。运行时候会先执行类 Animal 的构造函数,在执行类 A 的构造函数,这样也就达到了装饰器的目的。所以答案为 20。

观察者模式

type Handler1 = (data?: any) => void;
type Handler2 = (...args: any) => void;


class EventEmitter {
    listeners: {
        [eventName: string]: Handler1[] | Handler2[]
    }
    constructor () {
        this.listeners = {} // 首次需创建一个空对象
    }

    // 创建事件
    addListener(eventName: string, handler: Handler1 | Handler2) {
        if(!(eventName in this.listeners)){
            this.listeners[eventName] = []
        }
        this.listeners[eventName].push(handler)

        return this //  返回this链式调用
    }

    // 删除事件
    removeListener(eventName: string, handler: Handler1 | Handler2) {
         if(!(eventName in this.listeners)){
            console.warn(`不存在 ${eventName} 方法`)
            return
        }
        //因为判断得出Boolean,但[eventName]必须是Handler类,所以直接从[eventName]移除掉了remove值
        this.listeners[eventName] = this.listeners[eventName].filter(
            (item) => item !== handler
        );
        return this
    }

    // 触发事件
    emit(eventName: string, ...args: any) {
        if(!(eventName in this.listeners)){
            console.log(`不存在 ${eventName} 方法`)
            return
        }

        this.listeners[eventName].forEach((handler) => {
            handler(...args)
        })

        return this
    }

    emit1(eventName: string, data: string) {
        const handlerArgs = Array.prototype.slice.call(arguments, 1);
        // console.log('h', Array.prototype.slice.call(arguments, 0))
        if(!(eventName in this.listeners)){
            console.log(`不存在 ${eventName} 方法`)
            return
        }

        this.listeners[eventName].forEach((handler) => {
            handler(...handlerArgs)
        })

        return this
    }
}

let a_num = 10
let b_num = 20

let listenerFun_2 =  (value: string, num: number) => {
    console.log(`custom clicked 2: ${value} , ${num}`);
}

let listenerFun_4 =  () => {
    console.log(`custom clicked 4: remove`);
}

let listenerFun_5 =  (value: string) => {
    console.log(`custom clicked 5: ${value}`);
}


const eventEmitter = new EventEmitter();
eventEmitter.addListener('custom', (value: string) => {
    console.log(`custom clicked 1: ${value}`);
})

eventEmitter.addListener('custom', listenerFun_2)

eventEmitter.addListener('custom', () => {
    console.log(`custom clicked 3: null`);
})

eventEmitter.addListener('custom', listenerFun_5)

eventEmitter.emit('custom', a_num, b_num)   //...args传参,每次使用‘custom’,都会运行该名称数组下的所有函数

eventEmitter.emit1('custom', '123') //  不同的触发方式,只有传参的差别

eventEmitter.removeListener('custom', listenerFun_2);

image.png

答案

get 0
set 10

解析

观察者模式又叫发布订阅模式,当发布者的状态发生变化时,所有订阅者就会得到通知。
类 Animal 的 set age 函数和 get age 函数是发布行为的触发者。publishSubscribeStation 函数是发布行为的订阅者。
题目中使用了 get 和 set 关键字,对属性 _age 设置存值函数(setter)和取值函数(getter),每当取 age 属性时,就会调用 get age 函数,然后触犯发布订阅。每当设置 age 属性时,就会调用 set age 函数,然后触犯发布订阅。
题目逻辑本身比较简单,答案为 get 0,set 10。