Typescript - 开闭原则

Posted

技术标签:

【中文标题】Typescript - 开闭原则【英文标题】:Typescript - Open-closed principle 【发布时间】:2018-04-24 05:06:11 【问题描述】:

我想在我的“小”项目中使用 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)。

【问题讨论】:

对于 TypeScript 代码的质量,您能做的最糟糕的事情可能就是应用源自 Java 的模式。 之前的评论可能是正确的。这段代码应该是面向对象的吗? AnimationType 没有状态,它是一个类的目的是什么? 其实也可以是接口。只想实现OCP 然后你实现了,我猜。不确定你得到了什么。可能是无用的抽象。也可以键入函数。除非你打算用animation instanceof TransformAnimation 来区分类实例(这是你不想用 OCP 做的事情),否则类是没有用的。 【参考方案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 ;

【讨论】:

谢谢,但我可以在哪里使用 OCP?你知道在 TS 中使用它的好例子吗? 看看您的企业级打印机。您不依赖“超级打印机 586”,您只需使用抽象概念“打印机”。这使得添加“超级打印机和订书机 593”成为可能,它可以打印 订书钉。惊人的。你没有改变原来的打印机,它仍然可以用来打印东西。您没有更改原始打印机或打印机的抽象概念 - 但您已对其进行了扩展。我希望我没有过分夸大这个比喻。【参考方案2】:

我同意阿卢安·哈达德的观点。不要在这里以普通的 JAVA 模式思考。虽然 Typescript 看起来很像 C#,甚至有点像 JAVA,但归根结底还是 JavaScript。继承规则,正如您从 JAVA 中了解的那样,在这里并不深入。不要试图建立复杂的继承模式或相关的设计原则。使用 Typescript 你必须调整你的编码方式。

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

例如

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%)' ;
    


【讨论】:

嗯.. 所以实际上我不应该在打字稿中使用 OCP,是吗? 是的,这是我的建议。你的代码越复杂,你很快就会陷入困境。两年前我也走上了同样的路,也是来自 JAVA。您必须习惯这里的服务作为您的选择。【参考方案3】:

上面的代码实际上并不需要是面向对象的。 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,所以它是无状态的,它的用途变得不清楚。

【讨论】:

以上是关于Typescript - 开闭原则的主要内容,如果未能解决你的问题,请参考以下文章

手撸golang 架构设计原则 开闭原则

开闭原则——面向对象程序设计原则

面向对象原则之一 开放封闭原则(开闭原则)

开闭原则

设计模-设计原则-开闭原则

七大设计原则之开闭原则应用