在小型项目中应用开闭原则(OCP)遇实现问题,求技术指导
如何在动画系统中正确应用开闭原则(OCP)
嘿,我看了你这段尝试实现开闭原则的动画代码,先帮你梳理下当前可能存在的问题,再给你一套符合OCP的调整方案!
首先明确下核心:*开闭原则(OCP)*的本质是对扩展开放,对修改关闭——简单说就是,新增功能时靠写新代码实现,别去改动已经稳定运行的旧代码。
先看你现有代码的潜在问题
你已经定义了抽象基类AnimationType,也实现了两个具体动画类,这是个好开头,但如果后续在业务代码里直接用if/else判断创建实例,比如这样:
function getAnimation(type: string, timing: number) { let animation: AnimationType; if (type === 'fade') { animation = new FadingAnimation(); } else if (type === 'transform') { animation = new TransformAnimation(); } // 每次加新动画,都得改这个函数,直接违反OCP! return animation.createAnimation(timing); }
这就踩了OCP的坑:每次新增动画类型,都要修改判断逻辑,相当于对修改敞开了大门。
符合OCP的改进方案
我们可以通过抽象基类+工厂模式的组合解决这个问题,让扩展轻松实现,同时不碰原有代码:
1. 保留稳定的抽象基类
这个基类是所有动画的规范,尽量不要修改它:
abstract class AnimationType { // 定义统一的动画创建接口 public abstract createAnimation(timing: number): { in: string, out: string }; }
2. 实现具体动画类(扩展开放)
所有新动画都通过继承基类实现,完全不影响原有代码:
class FadingAnimation extends AnimationType { public createAnimation(timing: number): { in: string, out: string } { return { in: `opacity: 0; transition: opacity ${timing}s ease`, out: `opacity: 1; transition: opacity ${timing}s ease` }; } } class TransformAnimation extends AnimationType { public createAnimation(timing: number): { in: string, out: string } { return { in: `transform: translateX(-100%); transition: transform ${timing}s ease`, out: `transform: translateX(0); transition: transform ${timing}s ease` }; } } // 新增动画?直接加类就行,不用改任何旧代码 class ScaleAnimation extends AnimationType { public createAnimation(timing: number): { in: string, out: string } { return { in: `transform: scale(0.8); transition: transform ${timing}s ease`, out: `transform: scale(1); transition: transform ${timing}s ease` }; } }
3. 用工厂类封装创建逻辑(修改关闭)
把创建动画的逻辑放到工厂里,通过注册机制支持新动画,工厂的核心创建逻辑永远不用改:
class AnimationFactory { // 注册表:存储动画类型和对应的类 private static animationRegistry: Map<string, typeof AnimationType> = new Map([ ['fade', FadingAnimation], ['transform', TransformAnimation] ]); // 开放注册接口,新增动画只需要调用这个方法 public static registerAnimation(type: string, animationClass: typeof AnimationType) { this.animationRegistry.set(type, animationClass); } // 核心创建方法,永远不用修改 public static createAnimation(type: string, timing: number): { in: string, out: string } { const AnimationClass = this.animationRegistry.get(type); if (!AnimationClass) { throw new Error(`Unknown animation type: ${type}`); } return new AnimationClass().createAnimation(timing); } }
4. 业务代码里的使用方式
现在用起来就非常清爽,新增动画也完全不用动原有业务逻辑:
// 使用已有动画 console.log(AnimationFactory.createAnimation('fade', 0.5)); console.log(AnimationFactory.createAnimation('transform', 0.3)); // 新增动画?注册一下就能用,旧代码一行不用改 AnimationFactory.registerAnimation('scale', ScaleAnimation); console.log(AnimationFactory.createAnimation('scale', 0.4));
最后总结
这样调整后完全符合开闭原则:
- 要加新动画?只需要新增一个继承
AnimationType的类,再注册到工厂里就行 - 原有所有代码(基类、工厂核心逻辑、旧业务代码)都不需要修改
内容的提问来源于stack exchange,提问作者user6372887
相关产品推荐
相关产品推荐

