TypeScript中如何枚举实现IShape接口的类并支持扩展
嗨,这个需求刚好是开闭原则的典型应用场景!咱们可以用「注册表模式」来实现,让所有实现IShape的类自动注册到统一容器里,后续新增形状完全不用动原有代码。下面一步步拆解给你看:
实现方案:基于注册表的自动扩展模式
1. 先定义核心的IShape接口
把基础的接口规范先定好,确保所有形状类都遵循统一的方法约定:
interface IShape { // 返回形状的友好名称 GetName(): string; // 执行绘制逻辑 Draw(): void; }
2. 创建形状注册表
我们需要一个全局容器来存放所有IShape的构造函数,同时提供注册方法让形状类主动“报到”:
// 定义形状构造函数的类型,确保传入的类符合IShape接口规范 type ShapeConstructor = new () => IShape; // 注册表容器,存放所有已注册的形状类 const shapeRegistry: ShapeConstructor[] = []; // 注册函数,供每个形状类调用完成注册 function registerShape(ctor: ShapeConstructor) { shapeRegistry.push(ctor); }
3. 实现基础形状类并自动注册
接下来实现Rectangle、Circle这些类,每个类定义完成后,直接调用registerShape把自己加入注册表——完全不用手动维护硬编码的列表:
class Rectangle implements IShape { GetName(): string { return "矩形"; } Draw(): void { console.log("绘制矩形:🟩"); } } // 自动注册到注册表 registerShape(Rectangle); class Circle implements IShape { GetName(): string { return "圆形"; } Draw(): void { console.log("绘制圆形:🔵"); } } registerShape(Circle); class Triangle implements IShape { GetName(): string { return "三角形"; } Draw(): void { console.log("绘制三角形:🔺"); } } registerShape(Triangle);
4. 生成下拉选项并处理用户选择
现在可以直接从注册表中取出所有形状类,动态生成下拉选项;用户选中后,实例化对应类并调用Draw方法:
// 动态渲染形状下拉菜单的示例函数 function renderShapeDropdown() { const select = document.createElement("select"); // 遍历注册表,逐个添加下拉选项 shapeRegistry.forEach((ShapeCtor) => { const shapeInstance = new ShapeCtor(); const option = document.createElement("option"); option.value = ShapeCtor.name; // 用类名作为选项标识(可根据需求替换为自定义ID) option.textContent = shapeInstance.GetName(); select.appendChild(option); }); // 绑定选择事件,触发绘制逻辑 select.addEventListener("change", (e) => { const selectedCtorName = (e.target as HTMLSelectElement).value; // 从注册表中找到对应的形状构造函数 const selectedShapeCtor = shapeRegistry.find(ctor => ctor.name === selectedCtorName); if (selectedShapeCtor) { const shape = new selectedShapeCtor(); shape.Draw(); } }); // 将下拉菜单添加到页面中 document.body.appendChild(select); } // 调用渲染函数生成下拉菜单 renderShapeDropdown();
5. 新增形状的扩展方式
现在要加Square、Oval这类新形状,只需要实现IShape接口,然后调用registerShape就行——完全不用修改之前的注册表、下拉渲染代码:
class Square implements IShape { GetName(): string { return "正方形"; } Draw(): void { console.log("绘制正方形:🟦"); } } registerShape(Square); class Oval implements IShape { GetName(): string { return "椭圆形"; } Draw(): void { console.log("绘制椭圆形:🥚"); } } registerShape(Oval);
额外优化小建议
- 如果担心代码压缩后类名被混淆,可以给每个形状类加一个静态的
shapeId属性,用这个作为唯一标识(代替ctor.name):class Rectangle implements IShape { static shapeId = "rectangle"; // ... 其他代码 } // 注册和查找时改用shapeId - 可以把注册表和注册函数封装成单例类,避免全局变量污染,更符合面向对象的设计风格。
内容的提问来源于stack exchange,提问作者Hugo Bernier
相关产品推荐
相关产品推荐

