-
Notifications
You must be signed in to change notification settings - Fork 14
Interface Development
Below are some tips for working with interfaces in JavaScript.
The open source Zynga Core JS framework provides you with the ability to define Interfaces in JavaScript. Using this, you can create very flexible designs using interface-based design patterns.
The JavaScript language does not have built-in interfaces. Instead, you can create an object with an expected set of public properties. That object can be used to compare against other objects to see if they contain the same properties and functions (including number of arguments). Core allows you to define these kind of objects and assert against them.
You use core.Interface to define an interface object. The interface object is defined much like the way that you define a core.Class object.
The interface object doesn't have a specific implementation: it only has the property default values or function signatures that make up the public API. Since all the properties of the interface are public, they are all members of the prototype object.
Sample interface object
/**
* Interface for the public API of Bar.
*/
core.Interface('foo.Bar', {
/**
* Create a new Bar object with optional @data {Object?}.
*/
construct: function (data) {},
members: {
/** {String} */
id: 'defaultBarID',
/**
* Perform update logic using @deltaTime {Integer}.
*/
update: function (deltaTime) {},
/**
* Reset the data and properties.
*/
reset: function () {}
}
});Sample implementor object
A class who wants to implement this interface would look like this:
/**
* Default implementation for a Bar.
*/
core.Class('foo.DefaultBar', {
implement: [foo.Bar],
construct: function (data) {
if (data) {
this.id = data.id;
}
},
members: {
id: 'defaultBarID',
update: function (deltaTime) {
// do update logic here.
},
reset: function () {
// do reset logic here.
delete this.id;
}
}
});Core JS framework also provides you with debug and assertion tools. If you want to make sure that an object you are operating on adheres to a specific interface, you can use Core like this:
/** Do some stuff using a @barObj {#foo.Bar}. */
function doStuff (barObj) {
if (core.Env.isSet('debug')) {
/*
* Throws an assertion error if the bar object doesn't implement
* foo.Bar interface API.
*/
foo.Bar.assert(barObj);
}
}NOTE: We wrap the assertion in a 'debug' check so that this extra logic is only executed in debug mode. Core framework paired with the Jasy tool provides permutation support, and debug is one of the default permutations. When not in debug mode, the entire debug if-block is removed. This allows you to assert your code when developing, but turn it off for performance reasons in production.
Below are some general interface design best practices.
In your code, you should provide a default implementation of each interface, where the default implementation is an object that can be instantiated on its own. The default implementation should only implement the single interface and not any additional interfaces.
Since mixins are possible in JavaScript, you can combine any number of objects together to create other useful objects. With a specific default implementation of each interface, you can create a specific object that fulfills any number of interfaces by mixing in the default implementations of each interface.
This technique is extremely flexible and powerful, enabling you to mix and match any number of objects to achieve your goals.
Interface-based development becomes very important when you are working with component-based systems. By using an Interface to represent each type of component, you are free to operate on the public API while replacing the component implementation behind the scenes.
If there is an Interface available for the functionality you are using, only code against the public interface API. Assume that any additional properties or functions in a specific implementation are transient and may disappear at any time.