【问题标题】:Javascript OOP - Best practices when validating objects via interface or prototypeJavascript OOP - 通过接口或原型验证对象时的最佳实践
【发布时间】:2011-12-10 11:21:18
【问题描述】:

我正在为来自 C# 背景的 javascript 学习更高级的 OO 策略,并且想知道如何实现基于原型的验证是否是个好主意。例如,当一个对象或函数需要它的一个参数来满足某个接口时,你可以像这样检查它的接口,

var Interface = function Interface(i) {
   var satisfied = function (t, i) {
      for (var key in i) {
         if (typeof t !== 'object') {
            return false;
         }
         if (!(key in t && typeof t[key] == i[key])) {
            return false;
         }
      }
      return true;
  }
  this.satisfiedBy = function (t) { return satisfied(t, i); }
}

// the interface
var interfacePoint2D = new Interface({
    x: 'number',
    y: 'number'
}); 

// see if it satisfies
var satisfied = interfacePoint2D.satisfiedBy(someObject); 

我想出了这个策略,只通过接口验证对象,忽略对象的内部实现。

或者说你正在使用基于原型的继承,你应该还是不应该根据它们的原型函数来验证参数?我了解您将使用原型来实现默认功能,而接口未指定任何默认功能。有时,您传递给函数的对象可能需要某些默认功能才能使该函数正常工作。仅针对接口进行验证会更好,还是应该针对原型进行验证,如果是,最好的方法是什么?

编辑 -- 我提供了更多关于我为什么要问这个的上下文,

以在线游戏设计为例(主要使用 javascript 编写的游戏)。在这种情况下,我对验证感兴趣有两个主要原因,

1) 提供强大的公共 API 以根据需要修改游戏

2) 防止(或至少极大地阻止)潜在的作弊者

这需要在可定制性和滥用之间取得平衡。具体来说,一种情况是设计物理引擎,让游戏中的对象对重力做出反应。在现实系统中,用户不应该能够向系统添加对重力没有反应的对象。该系统有一个函数可以表示重力在任何给定点的全局效应:

function getGravityAt(x, y) {
      // return acceleration due to gravity at this point
}

反应的对象有一个方法可以使用它来更新它们的加速度:

function update() {
      this.acceleration = getGravity(this.position); 
}

至少要做的事情可能是确保添加到系统中的任何对象都具有“更新”方法,但您仍然不能确保 update() 方法确实旨在对重力做出反应。如果只允许从原型 update() 方法继承的对象,那么您至少在某种程度上知道系统中的所有内容都会真实地做出反应。

【问题讨论】:

  • 就个人而言,我根本不会验证。诸如 JS 或 Python 之类的动态类型语言更遵循 duck typing 的概念……如果它听起来和看起来像鸭子,那就是鸭子。如果您的函数需要与某个接口兼容的参数并且没有得到它......那么它无论如何都会抛出错误。正确使用您的代码是其他程序员的责任。
  • 如果您开发一个 javascript 游戏或 API 并希望防止滥用框架怎么办?我认为明确定义的接口是个好主意,尤其是在 API 设计中。
  • 当然接口应该明确定义并有据可查。不要误会我的意思,这总是很重要的。但是imo你不应该在运行时检查它们。
  • 如果你有一个函数(x, y) 需要一个点并且有人传入一个本身已经包含属性'x'和'y'的对象,你通常会这样做:if(xx && xy) { } 检查这样的对象,以便函数可以单独接受点对象或每个坐标。那本质上不是接口检查吗?我的课程的重点只是以不那么冗长的方式启用该功能(对于更复杂的对象,您的 if 语句会变得很长)。

标签: javascript oop inheritance interface prototype-programming


【解决方案1】:

我会重申,类型检查不是惯用的 javascript。

如果您仍想进行类型检查,我推荐 Google 的 closure compiler 实现。类型检查是静态完成的 :) 它具有接口约定以及(原型)类型检查。

【讨论】:

    【解决方案2】:

    这是一个非常主观的问题。我将提出一个问题,即在 Javascript 中进行基于接口的验证是否是一个好主意(它可能有很好的用例,但它不是该语言的标准方法)。但我会说,根据原型验证对象可能不是一个好主意。

    如果您完全通过接口进行验证,那么您可能正在使用其他程序员创建的对象。有很多方法可以创建对象——有些依赖原型,有些不依赖原型,虽然它们都有自己的支持者,但它们都是有效且可能的方法。例如:

    var Point = function(x,y) {
        return {
            x: function() { return x },
            y: function() { return y }
        };
    };
    
    var p = new Point(1,1);
    

    对象p 符合类似于您上面的接口,除了xy 是函数。但是无法通过检查其构造函数(即Object())或Point.prototype 来验证p 是否满足此接口。您所能做的就是测试p 是否具有称为xy 的属性,并且它们的类型为"function" - 你在上面做什么。

    您可能会坚持认为p 在其原型链中有一个特定的祖先,例如AbstractPoint,其中包括 xy 函数 - 您可以使用 instanceof 进行检查。但你不能确定xy 没有在p 中重新定义:

    var AbstractPoint = function() {};
    AbstractPoint.prototype.x = function() {};
    AbstractPoint.prototype.y = function() {};
    
    var Point = function(x,y) {
        var p = new AbstractPoint(x,y);
        p.x = "foo";
        return p;
    }
    
    var p = new Point(1,1);
    p instanceof AbstractPoint; // true
    p.x; // "foo"
    

    也许更重要的是,这使得放入也满足接口但不从您的类继承的自定义对象变得更加困难。

    所以我认为你目前正在做的可能是你所希望的最好的。根据我的经验,Javascript 程序员更倾向于使用动态鸭子类型,而不是试图模仿静态类型语言的功能:

    function doSomethingWithUntrustedPoint(point) {
        if (!(point.x && point.y)) return false; // evasive action!
        // etc.
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-14
      • 2013-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-08
      • 2015-10-20
      相关资源
      最近更新 更多