8. Possible Variations and Extensions

8.1. Adding an object-level validation function

When object-level validation (across two or more properties) is required for a model class, we can add a custom validation function validate to it, such that object-level validation can be performed before save by invoking validate on the object concerned. For instance, for expressing the constraint defined in the class model shown in Figure 4.1, we define the following validation function:

Author.prototype.validate = function () {
  if (this.dateOfDeath && this.dateOfDeath < this.dateOfBirth) {
    throw new ConstraintViolation(
          "The dateOfDeath must be after the dateOfBirth!");
  }
};

When a validate function has been defined for a model class, it can be invoked in the create and update methods. For instance,

Author.add = function (slots) {
  var author = null;
  try {
    author = new Author( slots);
    author.validate();
  } catch (e) {
    console.log( e.constructor.name +": "+ e.message);
  }
};

8.2. Using implicit JS setters

Since ES5, JavaScript has its own form of setters, which are implicit and allow having the same semantics as explicit setter methods, but with the simple syntax of direct access. In addition to having the advantage of a simpler syntax, implicit JS setters are also safer than explicit setters because they decrease the likelihood of a programmer circumventing a setter by using a direct property assignment when instead a setter should be used. In other OOP languages, like Java, this is prevented by declaring properties to be 'private'. But JavaScript does not have this option.

The following code defines implicit setter and getter methods for the property title:

Object.defineProperty( Book.prototype, "title", {
  set: function(t) {
    var validationResult = Book.checkTitle( t);
    if (validationResult instanceof NoConstraintViolation) {
      this._title = t;
    } else {
      throw validationResult;
    }
  },
  get: function() {
    return this._title;
  }
});

Notice that, also in the constructor definition, the internal property _title, used for storing the property value, is not used for setting/getting it, but rather the virtual property title:

Book = function (slots) {
  this.learnUnitNo = 0;
  this.title = "";
  if (arguments.length > 0) {
    this.learnUnitNo = slots.learnUnitNo;
    this.title = slots.title;
    // optional property
    if (slots.subjectArea) this.subjectArea = slots.subjectArea;
  }
});

We will start using implicit setter and getter methods, along with ES2015 class definitions, in Chapter 7.