工厂模式
/* 工厂模式 */
// 没有设计模式,如果以后增加有新增,都需要新的class
class CannedOrange_3401 {}
class CannedApple_3401 {}
function eatCan_3401(c: CannedOrange_3401 | CannedApple_3401) {
}
// 每次都要创建对应的,冗余
let o = new CannedOrange_3401();
eatCan_3401(o)
// 采用设计模式 工厂模式
// 先定义抽象类,然后继承抽象类
abstract class Can_3402 {}
class CannedOrange_3402 extends Can_3402 {}
class CannedApple_3402 extends Can_3402 {}
// 定义类型别名
type CanType_3402 = 'orange' | 'apple';
// 通过工厂模式,对传入的类型别名进行判断,然后返回对应的实例
class CanFactory_3402 {
static create(type: CanType_3402) {
if (type === 'orange') {
return new CannedOrange_3402();
} else if (type === 'apple') {
return new CannedApple_3402();
}
}
}
// 工厂模式,使用示例
let p = CanFactory_3402.create('apple');
答案
解析
工厂模式常用的实例化对象模式,用于批量生产对象。
题目使用了工厂模式。大家可以仔细看看哦。解决本题的思路是类的静态属性,所有的实例共享类的静态属性。所有编号分别为 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
适配器模式
/* 适配器模式 */
// 业务改造前的接口
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);
答案
解析
适配器模式主要作用使两个不兼容的东西可以兼容工作。
第一版和第二版分别是两个版本的累加求和器,由于数据源数据类型不同,我们需要选择不同的版本。现在引入适配器兼容版,我们不需要关心数据源的数据类型,直接使用 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
答案
解析
装饰者模式在不改变对象本身的情况下,在程序运行阶段为对象动态增加功能。
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);
答案
解析
观察者模式又叫发布订阅模式,当发布者的状态发生变化时,所有订阅者就会得到通知。
类 Animal 的 set age 函数和 get age 函数是发布行为的触发者。publishSubscribeStation 函数是发布行为的订阅者。
题目中使用了 get 和 set 关键字,对属性 _age 设置存值函数(setter)和取值函数(getter),每当取 age 属性时,就会调用 get age 函数,然后触犯发布订阅。每当设置 age 属性时,就会调用 set age 函数,然后触犯发布订阅。
题目逻辑本身比较简单,答案为 get 0,set 10。