Typescript - 开闭原则
Typescript - Open-closed principle
我想在我的 "small" 项目中使用 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 class 的对象。如果我想要变换动画,我需要发送 TransformAnimation 对象 class - 所以我必须在某个地方使用 switch(this.animationType).
我同意 Aluan Haddad 的观点。不要在这里想简单的 JAVA-Patterns。尽管 Typescript 看起来很像 C#,因此甚至有点像 JAVA,但它最终仍然是 JavaScript。正如您从 JAVA 了解到的那样,继承规则在这里并不适用。不要试图建立复杂的继承模式或相关的设计原则。使用 Typescript 你必须调整你的编码方式。
解决您的问题的一个好方法是使用具有 2 个方法的普通服务,return 需要的对象。
例如
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%)' };
}
}
上面的代码实际上并不需要面向对象。 AnimationType
class 没有状态,无法从多个实例中受益,因此不需要。除非 class 个实例用 animation instanceof TransformAnimation
区分(这不是开闭原则应该做的事情),否则这些 classes.
是没有用的
AnimationType
class可以用函数式的方式代替:
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
,因此它是无状态的,其目的变得不明确。
原则和模式绝对可以在语言之间转移。当您跨越范式的界限时,它们可能不太适用。 SOLID 原则和 OOP 模式适用于任何面向对象,而不仅仅是 Java.
请记住 TypeScript/JavaScript 不仅仅是面向对象的。
从问题开始
问题不在于您试图借鉴来自其他语言的原则。这里的问题是你试图从原理入手。
好的代码设计,无论您使用什么范式,都始于您想要解决的问题。在寻找模式或应用原则之前编写更多代码,这样设计就可以从您在应用程序增长时发现的实际问题中浮现出来。
从一个原则开始,你就过早地决定问题最终可能是什么;并且您的设计可能会使您离简洁代码的目标更远。
模式和原则是高度可移植的,但等待出现问题的信号,而不是过早地应用一些解决方案。
越简单越好
你的情况,用最简单的方法就可以解决问题。如果你有一个测试来指导你,你可以在以后轻松更新实现细节。
也许在您的情况下,一个简单的 dictionary/object 就足够了。
const animationType = {
fading: {
in: 'opacity: 0',
out: 'opacity: 1'
},
transforming: {
in: 'transform: translateX(-100%)',
out: 'transform: translateX(100%)'
}
};
如果您不想强制执行 fading
和 transforming
,您可以键入此内容以允许任何密钥 - 但您还不需要添加此灵活性:
interface AnimationTypes {
[key: string]: { in: string, out: string };
}
我想在我的 "small" 项目中使用 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 class 的对象。如果我想要变换动画,我需要发送 TransformAnimation 对象 class - 所以我必须在某个地方使用 switch(this.animationType).
我同意 Aluan Haddad 的观点。不要在这里想简单的 JAVA-Patterns。尽管 Typescript 看起来很像 C#,因此甚至有点像 JAVA,但它最终仍然是 JavaScript。正如您从 JAVA 了解到的那样,继承规则在这里并不适用。不要试图建立复杂的继承模式或相关的设计原则。使用 Typescript 你必须调整你的编码方式。
解决您的问题的一个好方法是使用具有 2 个方法的普通服务,return 需要的对象。
例如
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%)' };
}
}
上面的代码实际上并不需要面向对象。 AnimationType
class 没有状态,无法从多个实例中受益,因此不需要。除非 class 个实例用 animation instanceof TransformAnimation
区分(这不是开闭原则应该做的事情),否则这些 classes.
AnimationType
class可以用函数式的方式代替:
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
,因此它是无状态的,其目的变得不明确。
原则和模式绝对可以在语言之间转移。当您跨越范式的界限时,它们可能不太适用。 SOLID 原则和 OOP 模式适用于任何面向对象,而不仅仅是 Java.
请记住 TypeScript/JavaScript 不仅仅是面向对象的。
从问题开始
问题不在于您试图借鉴来自其他语言的原则。这里的问题是你试图从原理入手。
好的代码设计,无论您使用什么范式,都始于您想要解决的问题。在寻找模式或应用原则之前编写更多代码,这样设计就可以从您在应用程序增长时发现的实际问题中浮现出来。
从一个原则开始,你就过早地决定问题最终可能是什么;并且您的设计可能会使您离简洁代码的目标更远。
模式和原则是高度可移植的,但等待出现问题的信号,而不是过早地应用一些解决方案。
越简单越好
你的情况,用最简单的方法就可以解决问题。如果你有一个测试来指导你,你可以在以后轻松更新实现细节。
也许在您的情况下,一个简单的 dictionary/object 就足够了。
const animationType = {
fading: {
in: 'opacity: 0',
out: 'opacity: 1'
},
transforming: {
in: 'transform: translateX(-100%)',
out: 'transform: translateX(100%)'
}
};
如果您不想强制执行 fading
和 transforming
,您可以键入此内容以允许任何密钥 - 但您还不需要添加此灵活性:
interface AnimationTypes {
[key: string]: { in: string, out: string };
}