编辑
2021-12-07
undefined
00
请注意,本文编写于 1014 天前,最后修改于 28 天前,其中某些信息可能已经过时。

目录

最近翻看之前写过一个项目,其中用到了大量的 switch-case 分支语句,大致的代码结构如下

代码演示

javascript
class A { run() { console.log(A.name, 'run1!'); } } class B { run() { console.log(B.name, 'run2!'); } } class C { run() { console.log(C.name, 'run3!'); } } function fun1(type) { let temp; switch (type) { case 1: temp = new A(); break; case 2: temp = new B(); break; case 3: temp = new C(); break; default: throw new Error('Unsupported types'); break; } temp.run(); } fun1(1);

可以发现每个分支都有个共同的特点,通过构造函数实例化对象 a,调用a.task方法

temp = new A()

但现在有一个需求 给 A,B,C 三个类分别加上getUser方法,用来获取传入的 user,代码如下

javascript
class A { constructor(user) { this.user = user; } getUser() { return this.user; } run() { console.log(A.name, 'run1!'); } } class B { constructor(user) { this.user = user; } getUser() { return this.user; } run() { console.log(B.name, 'run2!'); } } class C { constructor(user) { this.user = user; } getUser() { return this.user; } run() { console.log(C.name, 'run3!'); } } function fun2(type) { let temp; switch (type) { case 1: temp = new A({ username: 'aaa' }); break; case 2: temp = new B({ username: 'bbb' }); break; case 3: temp = new C({ username: 'ccc' }); break; default: throw new Error('Unsupported types'); break; } let user = temp.getUser(); return user; } let user = fun2(1); console.log(user);

从这你也能看的出来,如果我要在添加一个需求的话,我就要分别给这三个类添加方法(当然这个是避免不了的),同时又要定义一个fun3来指定功能,没错,需求和写着代码的都是我。于是我决定重构一些这一部分的代码,为以后方便我后续的修改操作。

重构代码

首先 我们也能到两个部分都有 switch 分支,并且都夹杂着 break 语句,说实话,看的不是很入眼。同时定义了一个 temp 临时参数用于调用,不妨封装成一个函数,专门用来获取该类的实例对象

javascript
function getClass(type) { switch (type) { case 1: return new A({ username: 'aaa' }); case 2: return new B({ username: 'bbb' }); case 3: return new C({ username: 'ccc' }); default: throw new Error('Unsupported types'); } }
javascript
function fun2(type) { let temp = getClass(type); let user = temp.getUser(); return user; }

这样 就能把那个共有的switch-case代码封装成一个工厂函数用于获取。

利用多态来消除分支

但是上面这么写的话,其实是会一个很隐患的问题,比如 C 类忘记写getUser方法了,但是我却调用了temp.getUser(),那么 js 运行到该代码的时候就会报错temp.getUser is not a function,这是 js 特性,并不能通过文本编辑器找到该 bug。但如果是 TypeScript,上面的代码就会提示

类型“A | B | C”上不存在属性“getUser”。 类型“C”上不存在属性“getUser”。ts(2339)

虽说 ES6 可以通过继承,实现对象的多态性,但 ES6 并没有抽象类的。加上我的项目是基于 TypeScript 的,所以用到的也是 TypeScript 的类(也强烈建议使用 TypeScript),故以下的代码也都是基于 TypeScript 所编写的。

typescript
interface User { username: string; } abstract class S { protected user: User; constructor(user) { this.user = user; } abstract getUser(); } class A extends S { getUser(): User { console.log('A'); return this.user; } } class B extends S { getUser(): User { console.log('B'); return this.user; } } class C extends S { getUser(): User { console.log('C'); return this.user; } } function getClass(type): S { switch (type) { case 1: return new A({ username: 'aaa' }); case 2: return new B({ username: 'bbb' }); case 3: return new C({ username: 'ccc' }); default: throw new Error('Unsupported types'); } } function fun3(type) { let temp = getClass(type); let user = temp.getUser(); console.log(user); return user; } fun3(3);

这样,ABC 都是继承与 S 类,并且必须复写 getUser 方法,否则编辑器将会报错,编译出来的 js 代码如下(ES2020 标准,其实也就正常的 JS 继承)

javascript
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== 'function' && b !== null) throw new TypeError('Class extends value ' + String(b) + ' is not a constructor or null'); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); }; })(); var S = /** @class */ (function () { function S(user) { this.user = user; } return S; })(); var A = /** @class */ (function (_super) { __extends(A, _super); function A() { return (_super !== null && _super.apply(this, arguments)) || this; } A.prototype.getUser = function () { console.log('A'); return this.user; }; return A; })(S); var B = /** @class */ (function (_super) { __extends(B, _super); function B() { return (_super !== null && _super.apply(this, arguments)) || this; } B.prototype.getUser = function () { console.log('B'); return this.user; }; return B; })(S); var C = /** @class */ (function (_super) { __extends(C, _super); function C() { return (_super !== null && _super.apply(this, arguments)) || this; } C.prototype.getUser = function () { console.log('C'); return this.user; }; return C; })(S); function getClass(type) { switch (type) { case 1: return new A({ username: 'aaa' }); case 2: return new B({ username: 'bbb' }); case 3: return new C({ username: 'ccc' }); default: throw new Error('Unsupported types'); } } function fun2(type) { var temp = getClass(type); var user = temp.getUser(); console.log(user); return user; } fun2(3);

本文作者:任浪漫

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!