diff --git a/lib/class.js b/lib/class.js index ae370b4..4996bf5 100644 --- a/lib/class.js +++ b/lib/class.js @@ -217,16 +217,10 @@ var extend = ( function( extending ) members = member_builder.initMembers( prototype, prototype, prototype ), - abstract_methods = ( base.abstractMethods || [] ).slice(); - // it's much faster to lookup a hash than it is to iterate through an - // entire array each time we need to find an existing abstract method - var abstract_map = {}; - for ( var i = 0, len = abstract_methods.length; i < len; i++ ) - { - var method = abstract_methods[ i ]; - abstract_map[ method ] = i; - } + abstract_methods = util.clone( base.abstractMethods ) + || { __length: 0 } + ; util.propParse( props, { each: function( name, value, keywords ) @@ -269,15 +263,17 @@ var extend = ( function( extending ) if ( is_abstract ) { - abstract_methods.push( name ); + abstract_methods[ name ] = true; + abstract_methods.__length++; } - else if ( ( abstract_map[ name ] !== undefined ) + else if ( ( abstract_methods[ name ] ) && ( is_abstract === false ) ) { // if this was a concrete method, then it should no longer // be marked as abstract - delete abstract_methods[ abstract_map[ name ] ]; + delete abstract_methods[ name ]; + abstract_methods.__length--; } }, @@ -289,8 +285,6 @@ var extend = ( function( extending ) }, } ); - abstract_methods = util.arrayShrink( abstract_methods ); - // reference to the parent prototype (for more experienced users) prototype.parent = base.prototype; @@ -332,7 +326,7 @@ var extend = ( function( extending ) function createCtor( abstract_methods ) { // concrete class - if ( abstract_methods.length === 0 ) + if ( abstract_methods.__length === 0 ) { var args = null; @@ -507,7 +501,7 @@ function attachPropInit( prototype, properties ) */ function attachAbstract( func, methods ) { - var is_abstract = ( methods.length > 0 ) ? true: false; + var is_abstract = ( methods.__length > 0 ) ? true: false; /** * Returns whether the class contains abstract methods (and is therefore diff --git a/test/test-class-abstract.js b/test/test-class-abstract.js index cbf3a1d..ab5883e 100644 --- a/test/test-class-abstract.js +++ b/test/test-class-abstract.js @@ -72,11 +72,6 @@ assert.ok( "All classes should have an isAbstract() method" ); -assert.ok( - ( Foo.abstractMethods instanceof Array ), - "All classes should provide a list of their abstract methods as an array" -); - assert.equal( Foo.isAbstract(), false, @@ -96,28 +91,6 @@ assert.equal( "concrete implementation for all abstract methods" ); -assert.ok( - ( ( AbstractFoo.abstractMethods[ 0 ] == 'method' ) - && ( AbstractFoo.abstractMethods[ 1 ] == 'second' ) - ), - "Abstract classes should provide a list of their abstract methods' names" -); - -assert.ok( - ( ( SubAbstractFoo.abstractMethods[ 0 ] == 'method' ) - && ( SubAbstractFoo.abstractMethods[ 1 ] == undefined ) - ), - "Abstract subclasses should not have their concrete methods within the " + - "the abstract method list if the concrete method was abstract in " + - "its supertype" -); - -assert.equal( - ConcreteFoo.abstractMethods.length, - 0, - "Concrete classes should not have any abstract methods listed" -); - assert.equal( ConcreteFoo.isAbstract(), false, diff --git a/test/test-class-implement.js b/test/test-class-implement.js index 3458794..cfb973a 100644 --- a/test/test-class-implement.js +++ b/test/test-class-implement.js @@ -112,6 +112,11 @@ var Type = Interface.extend( { } )(); +/** + * Since interfaces can contain only abstract methods, it stands to reason that + * any class implementing an interface without providing any concrete methods + * should be abstract by default. + */ ( function testClassesImplementingInterfacesAreConsideredAbstractByDefault() { assert.equal( @@ -123,40 +128,6 @@ var Type = Interface.extend( { } ) (); -( function testAbstractMethodListUpdatedWhenInterfaceImplemented() -{ - // no base - assert.equal( - Foo.abstractMethods.length, - 2, - "Abstract methods list is updated when interface is implemented " + - "(empty base)" - ); - - // PlainFoo base - assert.equal( - PlainFoo2.abstractMethods.length, - 2, - "Abstract methods list is updated when interface is implemented " + - "(concrete base)" - ); -} )(); - - -( function testProperAbstractMethodsAreCopiedFromInterface() -{ - assert.ok( - ( ( Foo.abstractMethods[ 0 ] == 'foo' ) - && ( Foo.abstractMethods[ 1 ] == 'foo2' ) - ) - && ( ( PlainFoo2.abstractMethods[ 0 ] == 'foo' ) - && ( PlainFoo2.abstractMethods[ 1 ] == 'foo2' ) - ), - "Abstract methods list contains names of implemented methods" - ); -} )(); - - ( function testInstancesOfClassesAreInstancesOfTheirImplementedInterfaces() { // concrete implementation so that we can instantiate it @@ -175,5 +146,11 @@ var Type = Interface.extend( { "Instances of classes implementing interfaces are considered to be " + "instances of the implemented interfaces" ); + + assert.equal( + ConcreteFoo.isAbstract(), + false, + "Concrete implementations are not considered to be abstract" + ); } )();