打字原稿 - 开放原则

时间:2018-04-24 05:06:12

标签: typescript abstract solid-principles open-closed-principle

我想在我的“小”项目中使用OCP规则。实际上我有一个实现这个问题。让我告诉你我写了什么。

abstract class AnimationType {
    public abstract createAnimation(timing: number): { in: string, out: string };
}

class FadingAnimation extends AnimationType {
  public createAnimation(timing: number): { in: string, out: string } {
    return { in: 'opacity: 0', out: 'opacity: 1' }
  }
}

class TransformAnimation extends AnimationType {
  public createAnimation(timing: number): { in: string, out: string } {
    return { in: 'transform: translateX(-100%)', out: 'transform: translateX(100%)' }
  }
}

class AnimationCreator {
  private timing: number;

  constructor(time: number) {
    this.timing = time;
  }

  getAnimation(animation: AnimationType): { in: string, out: string } {
    return animation.createAnimation(this.timing);
  }
}

我写得好吗?如果我这样做,那么实际上我获得了什么?如果我想添加这个动画,我需要发送FadingAnimation类的对象。如果我想要变换动画我需要发送TransformAnimation类的对象 - 所以某处我必须使用switch(this.animationType)。

3 个答案:

答案 0 :(得分:2)

我同意Aluan Haddad的观点。不要在这里用简单的JAVA-Patterns思考。虽然Typescript看起来很像C#,因此甚至有点像JAVA,但它最终仍然是JavaScript。正如您从JAVA所了解的那样,继承规则在这里不起作用。不要试图建立复杂的继承模式或相关的设计原则。使用Typescript,您必须调整编码方式。

解决问题的一个好方法是使用带有2个返回所需对象的方法的普通服务。

e.g。

import { Injectable } from '@angular/core';

@Injectable()
export class AnimationTypeService {

    constructor() {}

    getFadingAnimation(timing: number): { in: string, out: string } {
        return { in: 'opacity: 0', out: 'opacity: 1' };
    }

    getTransformAnimation(timing: number): { in: string, out: string } {
        return { in: 'transform: translateX(-100%)', out: 'transform: translateX(100%)' };
    }

}

答案 1 :(得分:2)

上面的代码并不一定需要面向对象。 AnimationType类没有状态,不能从多个实例中受益,因此不需要。除非使用animation instanceof TransformAnimation来区分类实例(并且这不是应该用开放原理来完成的),否则不会使用这些类。

AnimationType类可以用功能方法替换:

interface IAnimation {
  type?: string;
  in: string;
  out: string;
}

type TAnimationFactory = (timing: number) => IAnimation;

const createFadeAnimation: TAnimationFactory = (timing: number) => {
   return { in: 'opacity: 0', out: 'opacity: 1' };
}

class AnimationCreator {
  private timing: number;

  constructor(time: number) {
    this.timing = time;
  }

  getAnimation(animationFactory: TAnimationFactory): IAnimation {
    return animationFactory(this.timing);
  }
}

取决于AnimationCreator的作用,它也可以替换为函数,或者取消直接使用动画工厂函数。

如果AnimationType有状态,则可能会发生变化,例如它接受transform%作为构造函数参数。但在这种情况下,不清楚为什么transform属性应该考虑与timing不同的类型 - 两者都可以从一个动画到另一个动画不同,因此在构造时定义。在这种情况下,AnimationCreator不需要timing,因此它是无国籍的,其目的不明确。

答案 2 :(得分:1)

原则和模式绝对可在语言之间转换。当你跨越范式的范围时,它们可能不太适用。 SOLID原则和OOP模式适用于任何面向对象,而不仅仅是Java。

请记住,TypeScript / JavaScript不仅仅是面向对象的。

以问题

开头

问题不在于您是否试图从其他语言的原则中汲取经验。这里的问题是你试图从这个原则开始。

良好的代码设计,无论您使用何种范例,都要从您想要解决的问题开始。在寻找模式或应用原则之前编写更多代码,因此设计可以从应用程序增长时发现的实际问题中找到。

从一个原则开始,你太早决定问题最终会是什么;并且您的设计可能会让您远离清洁代码的目标。

模式和原则是高度可转移的,但要等待你遇到问题的信号,而不是过早地应用某种解决方案。

简单就是更好

在您的情况下,您可以用最简单的方式解决问题。如果您有测试指导您,您可以稍后轻松更新实施细节。

也许在你的情况下,一个简单的字典/对象就足够了。

const animationType = {
    fading: {
        in: 'opacity: 0',
        out: 'opacity: 1'
    },
    transforming: {
        in: 'transform: translateX(-100%)',
        out: 'transform: translateX(100%)'
    }
};

如果您不想强制执行fadingtransforming,则可以输入此选项以允许任何密钥 - 但您还不需要添加此灵活性:

interface AnimationTypes {
    [key: string]: { in: string, out: string };
}