Javascript OOP - лучшие практики при проверке объектов через интерфейс или прототип - PullRequest
4 голосов
/ 19 октября 2011

Я изучаю более продвинутую ОО-тактику для javascript, основанную на C #, и мне интересно, как это сделать, или даже хорошая идея реализовать проверку на основе прототипов. Например, когда объект или функция требует одного из своих параметров для удовлетворения определенного интерфейса, вы можете проверить его интерфейс следующим образом:

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 () действительно предназначен для реакции на гравитацию. Если разрешены только объекты, которые наследуются от прототипного метода update (), то вы знаете, по крайней мере, до некоторой степени, что все в системе реагирует реалистично.

Ответы [ 2 ]

2 голосов
/ 19 октября 2011

Это довольно субъективный вопрос. Я перейду к вопросу о том, стоит ли вообще делать проверку на основе интерфейса в Javascript (вполне могут быть хорошие сценарии использования, но это не стандартный подход в языке). Но я скажу, что, вероятно, не хорошая идея для проверки объектов на основе их прототипов.

Если вы вообще проверяете интерфейсом, вы, вероятно, работаете с объектами, созданными другими программистами. Существует множество способов создания объектов - некоторые полагаются на прототипы, некоторые - нет, и, хотя у каждого из них есть свои сторонники, все они являются действительными и вероятными подходами. Например:

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

var p = new Point(1,1);

Объект p соответствует интерфейсу, подобному вашему выше, за исключением того, что x и y являются функциями. Но нет способа проверить, что p удовлетворяет этому интерфейсу, проверив его конструктор (то есть Object()) или Point.prototype. Все, что вы можете сделать, это проверить, что p имеет атрибуты с именами x и y и что они имеют тип "function" - что вы делаете выше.

Вы могли бы потенциально настаивать на том, что у p есть определенный предок в цепочке прототипов, например AbstractPoint, который будет включать функции x и y - вы можете использовать instanceof, чтобы проверить это. Но вы не можете быть уверены, что x и y не были переопределены в 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.
}
0 голосов
/ 19 октября 2011

Повторюсь, проверка типов не является идиоматическим javascript.

Если вы все еще хотите проверять тип, я рекомендую закрывающий компилятор Google *1004*. Проверка типов выполняется статически :) У нее есть соглашения для интерфейсов, а также (прото) проверка типов.

...