From 0d1ba74415527a8c14691a58e4fadf5fea4b85cd Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 14 Nov 2010 20:48:39 -0500 Subject: [PATCH] Ensured abstract classes cannot be instantiated and permitted their instantiation during extending so that it may be used in the subclass's prototype --- lib/class.js | 51 +++++++++++++++++++++++++++++-------- test/test-class-abstract.js | 12 +++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/class.js b/lib/class.js index 098dfd5..f5d17f0 100644 --- a/lib/class.js +++ b/lib/class.js @@ -211,6 +211,14 @@ function prop_copy( props, dest, result_data ) } +/** + * Set to TRUE when class is being extended to allow the instantiation of + * abstract classes (for use in prototypes) + * + * @var {boolean} + */ +var extending = false; + /** * Mimics class inheritance * @@ -225,18 +233,13 @@ function prop_copy( props, dest, result_data ) */ function extend() { - var args = Array.prototype.slice.call( arguments ), - props = args.pop() || {}, - base = args.pop() || Class, + // ensure we'll be permitted to instantiate abstract classes for the base + extending = true; - prototype = new base(), - new_class = function() - { - if ( this.__construct instanceof Function ) - { - this.__construct.apply( this, arguments ); - } - }; + var args = Array.prototype.slice.call( arguments ), + props = args.pop() || {}, + base = args.pop() || Class, + prototype = new base(); // copy the given properties into the new prototype var result_data = { @@ -247,11 +250,37 @@ function extend() // reference to the parent prototype (for more experienced users) prototype.parent = base.prototype; + var new_class = ( result_data.abstractMethods.length === 0 ) + ? ( + // concrete class + function() + { + if ( this.__construct instanceof Function ) + { + // call the constructor + this.__construct.apply( this, arguments ); + } + } + ) + : ( + // do not allow abstract classes to be instantiated + function () + { + if ( !extending ) + { + throw new Error( "Abstract classes cannot be instantiated" ); + } + } + ); + // set up the new class setup_props( new_class, result_data ); new_class.prototype = prototype; new_class.constructor = new_class; + // we're done with the extension process + extending = false; + return new_class; } diff --git a/test/test-class-abstract.js b/test/test-class-abstract.js index 1bdd58f..102c4ea 100644 --- a/test/test-class-abstract.js +++ b/test/test-class-abstract.js @@ -133,3 +133,15 @@ assert.equal( "Subtypes of abstract types are not abstract if they provide concrete " + "implementations of all abstract methods" ); + +assert.throws( function() +{ + new AbstractFoo(); + new SubAbstractFoo(); +}, Error, "Abstract classes cannot be instantiated" ); + +assert.ok( + new ConcreteFoo(), + "Concrete subclasses can be instantiated" +); +