Transitioning from Java Classes to JavaScript Prototypes

JavaScript is a objected-oriented language with prototype-based inheritance. The lack of classes and language-level support for easily creating an inheritance chain causes a lot of fuss. Most programmers invest a great deal of time in first-year university trying to “get” object-oriented programming (OOP) in a class-based language like C++ or Java (I’ll use Java as my class-based language example.) When they arrive to JavaScript their concepts of how an object-oriented language works no longer apply they learn that their big OOP investment was not complete. No one has ever agreed on what OOP is exactly so it should really be no surprise their education was incomplete. They need to invest more precious energy to “get” the JavaScript way of doing OOP. I think they feel ripped off that Java OOP doesn’t “just work” in JavaScript and it seems anger is the most common reaction.

I too struggle(d) with learning how to do this business of prototype-based inheritance. There are very few books showing how to use prototype-based inheritance well. Prototype-based inheritance does appear in the famous Gang of Four Design Patterns book but it is not one of the more widely employed patterns in programs using languages with class-based inheritance.

First tactic: Avoidance

My very first post on this blog was about Class-Based Inheritance in JavaScript and I have made many posts since exploring various JavaScript techniques to achieve the kind of code reuse that is achieved by class-based inheritance in Java. In JavaScript, trying to manipulate the prototype chain of a constructor function is tricky. JavaScript just wants to have prototypes and objects based on those prototypes and it will hammer on you if you try to make deep inheritance chains.

Simulating single, class-based inheritance is possible but the very next thing most programmers want to simulate is super (which ironically is a reserved word in JavaScript.) Simulating super is a struggle that really can’t be solved perfectly. Another disappointment.

When I started learning JavaScript, more experienced JavaScript programmers on Usenet’s comp.lang.javascript repeatedly advised me to stop trying to simulate classes in JavaScript. “JavaScript doesn’t have classes.” “Stop thinking in terms of classes.” “Don’t do that.” “You don’t need classes.” I didn’t see how the last one was even possible. How could you not need classes? Every book on OOP talks about classes.

I struggled and struggled but simulating class-based inheritance in JavaScript really doesn’t work and makes the code…well…very un-JavaScript-ish.

Second tactic: Acceptance (aka Submission)

I have given up trying to simulate classes and inheritance chains in JavaScript and my life as a JavaScript program has improved dramatically. I’ll go as far to say that because JavaScript doesn’t support Java’s extends-style inheritance and the resulting deep inheritance chains, JavaScript encourages a better style of of OOP. After all Extends is Evil. A quotation from the Extends is Evil article…

I once attended a Java user group meeting where James Gosling (Java’s inventor) was the featured speaker. During the memorable Q&A; session, someone asked him: “If you could do Java over again, what would you change?” “I’d leave out classes,” he replied. After the laughter died down, he explained that the real problem wasn’t classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible.

The goal of Java’s extends, and inheritance in general, is code reuse (function reuse mainly.) We don’t want to retype code we already typed once and we really don’t want to maintain multiple copies of the same code. So code reuse is our main goal. It turns out that JavaScript is better equipped to reuse functions then most languages because JavaScript functions are first-class objects. You can grab a function-valued property from one object and attach it as a property of another object. If you grab all the properties of one object and attach them to another object you have, in essence, mixin-style multiple inheritance.

The remainder of this article shows an example using the observer pattern coded several ways in both Java and JavaScript to compare and contrast the various code-reuse strategies.

Feel free to cheat and scroll to the last three example of this article. The stuff from here to there is motivation for those final examples.

Example: Java inline implementation

As a base example, this code shows the observer pattern code directly inline. The Java inline implementation is clean. In this example, the code for the observer pattern cannot be shared with another model (eg. StockPriceModel.)

There is a little bulk for the type declarations and interfaces but that does enable the advantage of compile-time checks which is very important to some people especially those on large projects.

import java.util.ArrayList;

interface Subject {
    public void addObserver(Observer observer);
    public void notifyObservers();
}

interface Observer {
    public void update();
}

// ---------------------------

class WeatherModel implements Subject {
    private ArrayList<Observer> observers;
    private double temperature;
    
    public WeatherModel() {
        observers = new ArrayList<Observer>();
    }
    
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
    
    public void setTemperature(double temp) {
        temperature = temp;
        notifyObservers();
    }
    
    public double getTemperature() {
        return temperature;
    }
}

// ---------------------------

class CurrentConditionsView implements Observer {
    private WeatherModel model;
    
    public CurrentConditionsView(WeatherModel m) {
        model = m;
        model.addObserver(this);
    }
    
    public void update() {
        System.out.println(model.getTemperature());
    }
}

// ---------------------------

public class ObservableOne {
    
    public static void main(String[] args) {
        WeatherModel victoriaWeather = new WeatherModel();
        CurrentConditionsView victoriaNews = new CurrentConditionsView(victoriaWeather);
        
        victoriaWeather.setTemperature(15.3);
        victoriaWeather.setTemperature(17.0);
        victoriaWeather.setTemperature(14.7);
    }
    
}

Example: JavaScript inline implementation

The JavaScript inline implementation is nice and clean also but the observer pattern code is not very easily shared with another model (eg. StockPriceModel.)

function WeatherModel() {
    this.observers = [];
}

WeatherModel.prototype.addObserver = function(observer) {
    this.observers.push(observer);
};

WeatherModel.prototype.notifyObservers = function() {
    for (var i=0; i<this.observers.length; i++) {
        this.observers[i].update();
    }
};

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Example: Java implementation with extends inheritance

In this example, we create a base class Observable from which many model classes can inherit. This starts the classic extends vs. implementsdebate: if the WeatherModel extends Observer then WeatherModel can’t extend something else due to the limitation of single inheritance.

There is standard, feature-rich java.util.Observer class that can be used as a base class like the Observer is in this example. Even using the standard java.util.Observer, the extends vs. implements debate remains a problem.

import java.util.ArrayList;

interface Subject {
    public void addObserver(Observer observer);
    public void notifyObservers();
}

interface Observer {
    public void update();
}

// ---------------------------

class Observable implements Subject {
    private ArrayList<Observer> observers;

    public Observable() {
        observers = new ArrayList<Observer>();
    }

    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// ---------------------------

class WeatherModel extends Observable {
    private double temperature;
    
    public WeatherModel() {}
        
    public void setTemperature(double temp) {
        temperature = temp;
        notifyObservers();
    }
    
    public double getTemperature() {
        return temperature;
    }
}

// ---------------------------

class CurrentConditionsView implements Observer {
    private WeatherModel model;
    
    public CurrentConditionsView(WeatherModel m) {
        model = m;
        model.addObserver(this);
    }
    
    public void update() {
        System.out.println(model.getTemperature());
    }
}

// ---------------------------

public class ObservableTwo {
    
    public static void main(String[] args) {
        WeatherModel victoriaWeather = new WeatherModel();
        CurrentConditionsView victoriaNews = new CurrentConditionsView(victoriaWeather);
        
        victoriaWeather.setTemperature(15.3);
        victoriaWeather.setTemperature(17.0);
        victoriaWeather.setTemperature(14.7);
    }
    
}

Example: JavaScript class-based implementation with inheritance

If we simulate class-based inheritance in JavaScript, we can create a base class Observable from which many model classes can inherit. The extends vs. implements problem remains.

function extends(sub, sup) {
   function F() {}
   F.prototype = sup.prototype;
   sub.prototype = new F();
   sub.prototype.constructor = sub;
   sub.superConstructor = sup;
   sub.superPrototype = sup.prototype;
};

// ---------------------------

function Observable() {
    this.observers = [];
}
Observable.prototype.addObserver = function(observer) {
    this.observers.push(observer);
};
Observable.prototype.notifyObservers = function() {
    for (var i=0; i<this.observers.length; i++) {
        this.observers[i].update();
    }
};

// ---------------------------

function WeatherModel() {
    WeatherModel.superConstructor.call(this);
}

extends(WeatherModel, Observable);

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

alternate version without super

The above version shows just the beginning of what is ugly about trying to simulate super in JavaScript. Here is a slightly different version that still uses simulated classes but doesn’t require super.

function extends(sub, sup) {
   function F() {}
   F.prototype = sup.prototype;
   sub.prototype = new F();
   sub.prototype.constructor = sub;
   sub.superConstructor = sup;
   sub.superPrototype = sup.prototype;
};

// ---------------------------

function Observable() {}

Observable.prototype.addObserver = function(observer) {
    if (!this.observables) {
        this.observers = [];
    }
    this.observers.push(observer);
};
Observable.prototype.notifyObservers = function() {
    for (var i=0; i<this.observers.length; i++) {
        this.observers[i].update();
    }
};

// ---------------------------

function WeatherModel() {}

extends(WeatherModel, Observable);

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Example: Java “has a” implementation

To switch to an implements version of the Java code we can use a “has a” relationship where the WeatherModel instances have an internal Observableinstance. The WeatherModel instances then requires a couple very thin wrapper instance methods to implement the Subject interface and provide access to the internal Observable instance.

If more “has a” relationships are implemented as Subject is in this example, the idea of “multiple inheritance” or “mixins” in Java becomes clear.

This solution is quite nice and it is conceptually clear. The only real problem is needing to write those thin wrapper instance methods. Suppose several interfaces are mixed in like this to many model classes. That could mean typing out many thin wrappers and we are trying to avoid code repetition.

import java.util.ArrayList;

interface Subject {
    public void addObserver(Observer observer);
    public void notifyObservers();
}

interface Observer {
    public void update();
}

// ---------------------------

class Observable implements Subject {
    private ArrayList<Observer> observers;

    public Observable() {
        observers = new ArrayList<Observer>();
    }

    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// ---------------------------

class WeatherModel implements Subject {
    private double temperature;
    private Observable observable;
    
    public WeatherModel() {
        // "has a" rather than "is a"
        observable = new Observable();
    }
    
    // two thin wrappers for the Subject interface
    public void addObserver(Observer observer) {
        observable.addObserver(observer);
    }
    public void notifyObservers() {
        observable.notifyObservers();
    }
    
    public void setTemperature(double temp) {
        temperature = temp;
        notifyObservers();
    }
    
    public double getTemperature() {
        return temperature;
    }
}

// ---------------------------

class CurrentConditionsView implements Observer {
    private WeatherModel model;
    
    public CurrentConditionsView(WeatherModel m) {
        model = m;
        model.addObserver(this);
    }
    
    public void update() {
        System.out.println(model.getTemperature());
    }
}

// ---------------------------

public class ObservableThree {
    
    public static void main(String[] args) {
        WeatherModel victoriaWeather = new WeatherModel();
        CurrentConditionsView victoriaNews = new CurrentConditionsView(victoriaWeather);
        
        victoriaWeather.setTemperature(15.3);
        victoriaWeather.setTemperature(17.0);
        victoriaWeather.setTemperature(14.7);
    }
    
}

Example: JavaScript “has a” implementation

This example follows the Java “has a” implementation above. It is very similar but, in my eyes, this JavaScript code looks absolutely horrible and exemplifies “not getting it” with regard to first-class functions and programming in JavaScript in general.

function Observable() {
    this.observers = [];
}
Observable.prototype.addObserver = function(observer) {
    this.observers.push(observer);
};

Observable.prototype.notifyObservers = function() {
    for (var i=0; i<this.observers.length; i++) {
        this.observers[i].update();
    }
};

// ---------------------------

function WeatherModel() {
    this.observable = new Observable();
}

// two thin wrapper functions
WeatherModel.prototype.addObserver = function(observer) {
    this.observable.addObserver(observer);
};
WeatherModel.prototype.notifyObservers = function() {
    this.observable.notifyObservers();
};

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Example: JavaScript explicit borrowing implementation

We want to avoid those thin wrapper functions of the previous example. In this example we simply borrow some functions from an observableobject. Although this example doesn’t avoid the repetitive borrowing code, this is the key example of this article and indicates the idea of all following examples which make implementation easier.

In the Java “has a” example, the Observable instance was wrapped inside the WeatherModel and there was still a clear boundary around that wrapped object. This example shows what is more of a merging of some functions in one object into another object.

var observable = { 

  addObserver: function(observer) {
      if (!this.observers) {
          this.observers = [];
      }
      this.observers.push(observer);
  },
  
  notifyObservers: function() {
      for (var i=0; i<this.observers.length; i++) {
          this.observers[i].update();
      }
  }
  
};

// ---------------------------

function WeatherModel() {}

// borrow functions from the observable object
WeatherModel.prototype.addObserver = observable.addObserver;
WeatherModel.prototype.notifyObservers = observable.notifyObservers;

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Example: JavaScript automated borrowing implementation

There are several ways to automate the borrowing in the previous example to avoid the repetitive code. This example is a passible way of implementing JavaScript mixins but following examples are even better.

var observable = { 

  addObserver: function(observer) {
      if (!this.observers) {
          this.observers = [];
      }
      this.observers.push(observer);
  },
  
  notifyObservers: function() {
      for (var i=0; i<this.observers.length; i++) {
          this.observers[i].update();
      }
  }
  
};

// ---------------------------

function WeatherModel() {}

// borrow all properties of the observable object
for (var prop in observable) {
    WeatherModel.prototype[prop] = observable[prop];
}

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Note that the above code uses a for-in loop to borrow all the properties from observable. This process adds a prop variable to the global scope. We can avoid this by using an automatically-evaluated, anonymous function around the borrowing.

(function() {
    for (var prop in observable) {
        WeatherModel.prototype[prop] = observable[prop];
    }
})();

Example: JavaScript with a mixinfunction

In the last example we are finally getting somewhere. We can borrow any number of function properties of one object and add those function to another object and we can do this with a couple lines of code. Now let’s add a helper function for the borrowing.

function mixin(target, source) {
    for (var p in source) {
        target[p] = source[p];
    }
}

// --------------------------

var observable = { 

  addObserver: function(observer) {
      if (!this.observers) {
          this.observers = [];
      }
      this.observers.push(observer);
  },

  notifyObservers: function() {
      for (var i=0; i<this.observers.length; i++) {
          this.observers[i].update();
      }
  }

};

// ---------------------------

function WeatherModel() {}

mixin(WeatherModel.prototype, observable);

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

The form of the mixin function can become quite elaborate. For example, here is a mixin function where the call specifies which functions are borrowed.

function mixin(target, source, props) {
    if (props) {
        if (props instanceof Array) {
            for (var i=0; i<props.length; i++) {
                target[props[i]] = source[props[i]];
            }
        }
        else if (source[props]) {
            target[props] = source[props];
        }
    }
    else {
        for (var prop in source) {
            target[prop] = source[prop];
        }
    }
}

Example: JavaScript where module has the mixin function

The previous example, with the global mixin function offers great control and is a very good solution. For some reason the code in this example appeals more to me. In this case the module decides what it “exports”.

var observable = { 

  addObserver: function(observer) {
      if (!this.observers) {
          this.observers = [];
      }
      this.observers.push(observer);
  },
  
  notifyObservers: function() {
      for (var i=0; i<this.observers.length; i++) {
          this.observers[i].update();
      }
  },

  mixin: function(subject) {
      for (var p in observable) {
          if (p=='mixin') {
              continue;
          }
          subject[p] = observable[p];
      }
  }

};

// ---------------------------

function WeatherModel() {}

observable.mixin(WeatherModel.prototype);

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Example: My Favorite

The previous two examples are very good and useful solutions. Aesthetically this example is my favorite and thankfully the last one.

var observablize;

(function() {

    var observable = { 

      addObserver: function(observer) {
          if (!this.observers) {
              this.observers = [];
          }
          this.observers.push(observer);
      },

      notifyObservers: function() {
          for (var i=0; i<this.observers.length; i++) {
              this.observers[i].update();
          }
      }
      
    };
    
    observablize = function (subject) {
        for (var p in observable) {
            subject[p] = observable[p];
        }
    }
    
})();

// ---------------------------

function WeatherModel() {}

observablize(WeatherModel.prototype);

WeatherModel.prototype.setTemperature = function(temp) {
    this.temp = temp;
    this.notifyObservers();
};

WeatherModel.prototype.getTemperature = function() {
    return this.temp;
};

// ---------------------------

function CurrentConditionsView(model) {
    this.model = model;
    model.addObserver(this);
}

CurrentConditionsView.prototype.update = function() {
    alert(this.model.getTemperature());
};

// ---------------------------

var victoriaWeather = new WeatherModel();
var victoriaNews = new CurrentConditionsView(victoriaWeather);

victoriaWeather.setTemperature(15.3);
victoriaWeather.setTemperature(17.0);
victoriaWeather.setTemperature(14.7);

Summary

If you made it all the way down here I’m pleasantly surprised. I hope this article helps at least a few people see how code reuse is actually easier in JavaScript than in class-based languages like Java.

Credits The idea for the WeatherModel and CurrentConditionsView examples came from Head First Design Patterns by Freeman, Freeman, Sierra and Bates.

posted in: 

tags: 

code reuse, Inheritance, inheritance, Java, javascript, JavaScript, multiple inheritance, patterns, Patterns 

accepting comments

Leave a Reply

Your email address will not be published. Required fields are marked *