views:

772

answers:

5

I have been trying to learn how to add testing to existing code -- currently reading reading Working Effectively With Legacy Code. I have been trying to apply some of the principles in JavaScript, and now I'm trying to extract an interface.

In searching for creating interfaces in JavaScript, I can't find a lot -- and what I find about inheritance seems like their are several different ways. (Some people create their own base classes to provide helpful methods to make it easier to do inheritance, some use functions, some use prototypes).

What's the right way? Got a simple example for extracting an interface in JavaScript?

+13  A: 

There's no definitive right way, because so many people are doing so many different things.. There are many useful patterns.

Crockford suggests that you "go with the grain", or write javascript in a way that corresponds to javascript's prototypal nature.

Of course, he goes on to show that the original model that Netscape suggested is actually broken. He labels it "pseudoclassical", and points out a lot of the misdirection and unnecessary complexity that is involved in following that model.

He wrote the "object" function as a remedy (now known as Object.create() ). It allows for some very powerful prototypal patterns.

It's not always easy to do develop a clean interface when you have to work with legacy javascript, especially not when you're dealing with large systems, usually including multiple libraries, and each implementing a unique style and different inheritance pattern. In general, I'd say that the "right way" to do inheritance is the one which allows you to write a clean interface which behaves well in the context of your legacy code, but also allows you to refactor and eliminate old dependencies over time.

Considering the differences between the major library patterns, I've found that the most successful route to take in my own work is to keep my interfaces independent of the library interfaces entirely. I'll use a library or module if it's helpful, but won't be bound to it. This has allowed me to refactor a lot of code, phase out some libraries, and use libraries as scaffolding which can be optimized later.

Along these lines, I've written interfaces that were inspired by Crockford's parasitic inheritance pattern. It's really a win for simplicity.

On the other side of the coin, I'm sure you could argue for picking a library, enforcing it across your team, and conforming to both its inheritance patterns and its interface conventions.

keparo
How about some links?
pc1oad1etter
+3  A: 

Also check out Dean Edwards' Base.js. You can have a look at it here, the blog post is self-explanatory.

mrm
+5  A: 

You are looking at two different things.

First you have interfaces. The most accepted way of implementing this is though Duck Typing ("if it looks like a duck and quacks like a duck then it is a duck"). This means that if an object implements a set of methods of the interface then it is that interface. You implement this by having an array of method names that define an interface. Then to check if an object implements that interfece you see if it implements those methods. Here is a code example I whipped up:

function Implements(obj, inter)
{
 var len = inter.length, i = 0;
 for (; i < len; ++i)
 {
  if (!obj[inter[i]])
   return false;
 }
 return true;
}

var IUser = ["LoadUser", "SaveUser"];

var user = {
  LoadUser : function()
  {
   alert("Load");
  },

  SaveUser : function()
  {
   alert("Save");
  }
 };

var notUser = {
  LoadUser : function()
  {
   alert("Load");
  }
 };

alert(Implements(user, IUser));
alert(Implements(notUser, IUser));

Now you have inheritance. JS has no inheritance built in; so you have to implement it manually. This is just a matter of "copying" the properties of one object to another. Here is another code sample (not perfect but it demonstrates the point):

function InheritObject(base, obj)
{
 for (name in base)
 {
  if (!obj[name])
   obj[name] = base[name];
 }
}

var Base = {
  BaseFunc : function() { alert("BaseFunc from base"); },
  InheritFunc : function() { alert("InheritFunc from base"); }
 }

var Inherit = {
  InheritFunc : function() { alert("InheritFunc from inherit"); },
  AnotherFunc : function() { alert("AnotherFunc from inherit"); }
 }

InheritObject(Base, Inherit);

Inherit.InheritFunc();
Inherit.BaseFunc();
Inherit.AnotherFunc();

Base.BaseFunc();
Base.InheritFunc();

You probably want to look at http://www.mootools.net. It has my favorite implementation of Classes. You also definitely want to check out "Pro Javascript Design Patterns"

http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X

This book goes into good detail about how to emulate OOP in javascript.

nlaq
+1 Simple and clear explanation!
Marco Demajo
+5  A: 

There are no class in javascript, only objects.
But if you insist on emulating the class based object-oriented model you can use this:

function ChildClass() {
    ParentClass.call(this);
    // Write the rest of your constructor function after this point.
};
ChildClass.prototype = jQuery.extend({}, ParentClass.prototype, ChildClass.prototype);

jQuery.extend is a 'shallow copy' function from the jQuery library. You can replace it with any other object copying/cloning function.

the first sentence can't be emphasized more
Claudiu
A: 

Prototype offers its own take on inheritance, from http://www.prototypejs.org/api/class/create:

var Animal = Class.create({
  initialize: function(name, sound) {
    this.name  = name;
    this.sound = sound;
  },

  speak: function() {
    alert(this.name + " says: " + this.sound + "!");
  }
});

// subclassing Animal
var Snake = Class.create(Animal, {
  initialize: function($super, name) {
    $super(name, 'hissssssssss');
  }
});
Chase Seibert