设计模式在 TypeScript 中的实现
欢迎继续本专栏的第三十八篇文章。在前几期中,我们已逐步深化了对 TypeScript 性能优化的理解,包括避免 any 类型的策略、类型推断的优化技巧,以及在大型项目中的性能考虑。这些实践帮助我们构建了更高效和可维护的代码基础。今天,我们将转向设计模式这一经典主题,探讨如何在 TypeScript 中实现它们。设计模式是解决常见软件设计问题的可复用解决方案,我们将通过示例实现单例、工厂和观察者模式,并利用 TypeScript 的类型系统增强这些模式的可靠性和表达力。设计模式并非一成不变的规则,而是指导原则,在 TypeScript 中,它们能与静态类型结合,提供更强的编译时保证。我们将从设计模式的基础概念入手,逐步深入到每个模式的实现细节,并分析 TypeScript 类型系统如何提升模式的安全性和灵活性。通过由浅入深的讲解、丰富示例和实际场景分析,我们旨在帮助您掌握这些模式的核心,并在项目中应用它们,构建更模块化和可扩展的软件架构。内容将从设计模式的基本原理展开到高级应用,确保您能从简单实现过渡到类型增强的优化,并获得深刻的实践洞见。
理解设计模式在 TypeScript 中的定位
设计模式是软件工程中经过验证的解决方案,用于解决重复出现的特定问题。它们源于 GoF(Gang of Four)的经典著作《设计模式》,分为创建型、结构型和行为型三类。在 TypeScript 中,设计模式定位于桥接面向对象原则与类型安全的结合:TS 的静态类型系统能强化模式的约束,确保实现符合意图,同时减少运行时错误。例如,单例模式保证唯一实例,TS 类型能防止误用;工厂模式抽象创建,类型系统确保返回类型一致;观察者模式处理事件,接口定义订阅合同。
设计模式的定位在于提升代码的可复用性、可维护性和扩展性。在动态的 JavaScript 环境中,模式帮助管理复杂性;TS 通过类型注解和接口,让模式更 robust。例如,利用泛型增强工厂的灵活性,或用类型守卫确保观察者的参数安全。根据软件工程实践,使用设计模式的 TS 项目,代码重构成本可降低 15-25%,尤其在大型应用如企业系统或前端框架中。相比纯 JavaScript,TS 的类型系统让模式从"约定"转为"强制",减少隐蔽 bug。
为什么选择单例、工厂和观察者?它们代表创建型(工厂、单例)和行为型(观察者)的典型,能展示 TS 类型增强的多样性。我们将从每个模式的基础开始,逐步引入 TS 优化,确保您能理解模式如何从简单实现演变为类型安全的结构,同时避免模式滥用导致的复杂性。
设计模式在 TypeScript 中的应用历史与 OOP 语言同步,但 TS 的类型特性让它们更现代化,在框架如 Angular 或 NestJS 中广泛实现依赖注入和事件处理。这让模式成为架构设计的工具,在实际开发中帮助管理状态和交互。
设计模式的基础:创建型、结构型和行为型的概述
在深入具体模式前,理解设计模式的分类有助于把握其作用。创建型模式关注对象创建,如单例确保唯一、工厂抽象实例化;结构型模式组织类和对象,如适配器转换接口;行为型模式定义通信,如观察者处理通知。
在 TypeScript 中,基础模式实现类似于 JS,但类型系统添加约束:接口定义合同,泛型提供灵活,类型守卫确保安全。这让模式不只是结构,还带有编译时验证。例如,创建型模式用类型确保返回对象符合形状。
基础概述为具体模式铺路:我们将重点创建型(单顿、工厂)和行为型(观察者),因为它们常见,且 TS 类型增强显著。
单例模式:确保唯一实例
单例模式是一种创建型模式,确保类只有一个实例,并提供全局访问点。它适合管理共享资源,如配置或数据库连接。
单例模式的基础实现
在 TypeScript 中,基本单例用私有构造函数和静态方法:
typescript
class Singleton {
private static instance: Singleton | null = null;
private constructor() {
// 私有,防止直接 new
}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
public someMethod(): void {
console.log("Singleton method called");
}
}
使用:
typescript
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true
s1.someMethod();
基础:静态 instance 存储唯一对象,getInstance 控制访问。
单例模式的深入实现与优化
懒加载:如上,首次调用创建。
线程安全:在 JS 单线程中无需锁,但 Node 多进程可用。
初始化数据:
typescript
class ConfigSingleton {
private static instance: ConfigSingleton | null = null;
private config: { [key: string]: string } = {};
private constructor(configData: { [key: string]: string }) {
this.config = configData;
}
public static getInstance(configData?: { [key: string]: string }): ConfigSingleton {
if (!ConfigSingleton.instance) {
if (!configData) throw new Error("Config data required for initialization");
ConfigSingleton.instance = new ConfigSingleton(configData);
}
return ConfigSingleton.instance;
}
public get(key: string): string | undefined {
return this.config[key];
}
}
深入:优化添加重置测试,但生产慎用。
单例深入让它适合全局状态,但避免滥用导致耦合。
利用 TypeScript 类型系统增强单例模式
TS 类型增强单例:私有构造确保类型安全,静态方法返回精确类型。
类型增强的基本应用
上示例中,getInstance 返回 Singleton 类型,确保方法可用。
防止误用:
私有构造编译时防 new Singleton()。
类型增强的深入技巧
泛型单例:
typescript
class GenericSingleton<T> {
private static instances: Map<any, any> = new Map();
private constructor(private data: T) {}
public static getInstance<U>(key: any, data: U): GenericSingleton<U> {
if (!GenericSingleton.instances.has(key)) {
GenericSingleton.instances.set(key, new GenericSingleton(data));
}
return GenericSingleton.instances.get(key);
}
public getData(): T {
return this.data;
}
}
多键单例,泛型 T 确保 data 类型。
深入:结合接口。
typescript
interface ILogger {
log(message: string): void;
}
class LoggerSingleton implements ILogger {
// 单例实现
log(message: string): void {
console.log(message);
}
}
const logger = LoggerSingleton.getInstance();
logger.log("Message"); // 类型 ILogger
类型增强深入让单例安全,TS 接口定义合同。
工厂模式:抽象对象创建
工厂模式是创建型模式,提供接口创建对象,而不暴露逻辑。适合当创建复杂或有变体时。
工厂模式的基础实现
简单工厂:
typescript
class ProductA {
operation(): string {
return "ProductA operation";
}
}
class ProductB {
operation(): string {
return "ProductB operation";
}
}
class SimpleFactory {
static createProduct(type: string): ProductA | ProductB | null {
if (type === "A") return new ProductA();
if (type === "B") return new ProductB();
return null;
}
}
使用:
typescript
const prod = SimpleFactory.createProduct("A");
console.log(prod?.operation());
基础:工厂方法集中创建逻辑。
工厂模式的深入实现
抽象工厂:接口定义工厂,具体类实现。
typescript
interface IProduct {
operation(): string;
}
class ConcreteProduct1 implements IProduct {
operation(): string {
return "ConcreteProduct1";
}
}
class ConcreteProduct2 implements IProduct {
operation(): string {
return "ConcreteProduct2";
}
}
interface IFactory {
createProduct(): IProduct;
}
class Factory1 implements IFactory {
createProduct(): IProduct {
return new ConcreteProduct1();
}
}
class Factory2 implements IFactory {
createProduct(): IProduct {
return new ConcreteProduct2();
}
}
function clientCode(factory: IFactory) {
const product = factory.createProduct();
console.log(product.operation());
}
clientCode(new Factory1()); // "ConcreteProduct1"
深入:抽象工厂支持家族产品,易切换。
工厂深入让创建解耦,适合 DI。
利用 TypeScript 类型系统增强工厂模式
TS 类型增强工厂:泛型确保返回类型,接口定义合同。
类型增强的基本应用
泛型工厂:
typescript
class Factory {
static create<T>(ctor: new () => T): T {
return new ctor();
}
}
class MyClass {}
const instance = Factory.create(MyClass); // 类型 MyClass
基本:泛型 T 推断自 ctor。
类型增强的深入技巧
类型安全的抽象工厂:
typescript
interface IButton {
render(): void;
}
interface ICheckbox {
render(): void;
}
interface IGUIFactory {
createButton(): IButton;
createCheckbox(): ICheckbox;
}
class WindowsButton implements IButton {
render(): void {
console.log("Windows Button");
}
}
class WindowsCheckbox implements ICheckbox {
render(): void {
console.log("Windows Checkbox");
}
}
class WindowsFactory implements IGUIFactory {
createButton(): IButton {
return new WindowsButton();
}
createCheckbox(): ICheckbox {
return new WindowsCheckbox();
}
}
class MacButton implements IButton {
render(): void {
console.log("Mac Button");
}
}
class MacCheckbox implements ICheckbox {
render(): void {
console.log("Mac Checkbox");
}
}
class MacFactory implements IGUIFactory {
createButton(): IButton {
return new MacButton();
}
createCheckbox(): ICheckbox {
return new MacCheckbox();
}
}
function application(factory: IGUIFactory) {
const button = factory.createButton();
button.render();
const checkbox = factory.createCheckbox();
checkbox.render();
}
application(new WindowsFactory()); // Windows UI
application(new MacFactory()); // Mac UI
接口 IGUIFactory 确保工厂返回正确类型,类型系统检查实现。
深入技巧:泛型工厂参数化。
typescript
type Constructor<T> = new (...args: any[]) => T;
function createInstance<T>(ctor: Constructor<T>, ...args: any[]): T {
return new ctor(...args);
}
class User {
constructor(public name: string) {}
}
const user = createInstance(User, "Alice"); // 类型 User
类型增强深入让工厂安全,TS 泛型和接口防止错创建。
观察者模式:实现事件通知
观察者模式是行为型模式,定义一对多依赖,当主体变化,所有观察者通知。
观察者模式的基础实现
简单:
typescript
class Subject {
private observers: (() => void)[] = [];
attach(observer: () => void): void {
this.observers.push(observer);
}
detach(observer: () => void): void {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(): void {
this.observers.forEach(obs => obs());
}
}
class Observer {
update(): void {
console.log("Updated");
}
}
const subject = new Subject();
const obs1 = new Observer();
const obs2 = new Observer();
subject.attach(() => obs1.update());
subject.attach(() => obs2.update());
subject.notify(); // "Updated" twice
基础:Subject 管理观察者,notify 调用 update。
观察者模式的深入实现
发布订阅变体:
typescript
interface IObserver {
update(data: any): void;
}
class Publisher {
private subscribers: { [event: string]: IObserver[] } = {};
subscribe(event: string, observer: IObserver): void {
if (!this.subscribers[event]) this.subscribers[event] = [];
this.subscribers[event].push(observer);
}
unsubscribe(event: string, observer: IObserver): void {
this.subscribers[event] = this.subscribers[event].filter(obs => obs !== observer);
}
publish(event: string, data: any): void {
this.subscribers[event]?.forEach(obs => obs.update(data));
}
}
class Subscriber implements IObserver {
update(data: any): void {
console.log("Received:", data);
}
}
const publisher = new Publisher();
const sub1 = new Subscriber();
publisher.subscribe("event1", sub1);
publisher.publish("event1", "data"); // "Received: data"
深入:事件键分订阅,支持多事件。
观察者深入让模式适合 UI 事件或状态变化。
利用 TypeScript 类型系统增强观察者模式
TS 类型增强观察者:接口定义 update 签名,泛型指定 data 类型。
类型增强的基本应用
类型化 update:
typescript
interface IObserver<T> {
update(data: T): void;
}
class Subject<T> {
private observers: IObserver<T>[] = [];
attach(observer: IObserver<T>): void {
this.observers.push(observer);
}
notify(data: T): void {
this.observers.forEach(obs => obs.update(data));
}
}
class Observer implements IObserver<string> {
update(data: string): void {
console.log(data);
}
}
const subject = new Subject<string>();
const obs = new Observer();
subject.attach(obs);
subject.notify("message"); // OK
// subject.notify(123); // 错误
基本:泛型 T 确保 data 类型一致。
类型增强的深入技巧
事件类型映射:
typescript
type Events = {
click: MouseEvent;
keypress: KeyboardEvent;
};
class EventEmitter {
private listeners: { [K in keyof Events]?: ((event: Events[K]) => void)[] } = {};
on<K in keyof Events>(event: K, listener: (ev: Events[K]) => void): void {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event]!.push(listener);
}
emit<K in keyof Events>(event: K, data: Events[K]): void {
this.listeners[event]?.forEach(listener => listener(data));
}
}
const emitter = new EventEmitter();
emitter.on("click", ev => console.log(ev.clientX)); // ev MouseEvent
// emitter.on("click", ev => ev.key); // 错误
keyof + mapped 类型确保事件数据匹配。
深入技巧:TS 类型让观察者类型安全,防止错数据。
实际应用:设计模式在项目中的实践
单例在配置管理。
工厂在 DI 容器。
观察者在 Redux 状态订阅。
案例:游戏引擎,用单例资源管理,工厂实体创建,观察者事件系统。
实践提升架构。
高级设计模式:与 TS 结合的扩展
策略模式用类型守卫。
代理模式用装饰器。
高级扩展模式。
风险与最佳实践
风险:
- 模式滥用复杂。
- 无类型模式运行错。
- 继承过多 fragile。
实践:
- 简单优先模式。
- 类型化所有接口。
- 测试模式行为。
- 文档模式意图。
确保有效。
案例研究:真实项目
Angular 服务用工厂。
RxJS 观察者。
NestJS 单例模块。
改善 25%。
结语:设计模式,TS 架构的指南针
通过本篇文章的详尽探讨,您已掌握单例、工厂和观察者实现的细节,以及 TS 类型增强。这些模式将助您设计优雅代码。实践:实现单例。下一期 React 与 TS,敬请期待。若疑问,欢迎交流。我们继续。