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 7.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);
}
};
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 ES6 class definitions, in Chapter 11.