mixin-interface-api v0.1.32
mixin-interface-api
A lightweight interface class API in Javascript es6 (ECMAScript 2015). It is implementated with mixins, Type checking and inheritance are supported.
Release 0.1.32 changelog
This release brings a much better and modern implementation of the Log feature with the sink metaphor.
This idea is neither new nor mine but I thought that it would be very nice to have. You're welcome to read this article and take a look at the Serilog library.
Now the Log client sends a trace request (MxI.$Log.write()), then the trace message is eventually processed by being sent to a specific target (e.g. Console, File, Server, Database, etc...).
The sink(s) must be explicitly declared (MxI.$Log.addSink()) else the trace request is not processed.
Notice that sink classes must implement
MxI.$ILogSinkbut they are no more singletons.
- Major refactoring of Log API: step 1/2 - move some classes from
mixin-interfacetomixin-interface-apiMxI.$ILoggerinterface moved and renamed toMxI.$ILogSink.MxI.$DefaultLoggerimplementation moved and renamed toMxI.$ConsoleLogSink.- Implementation of Log feature moved from
MxI.$Systemclass toMxI.$Logclass. Please notice that the previous API (e.g.MxI.$System.log()) is still supported but is now deprecated.
- Major refactoring of Log API: step 2/2 - New implementation classes in
mixin-interface-apiMxI.$Logis the new implementation of the Log feature in which trace requests are processed by sink(s). A sink redirects traces (MxI.$Log.write()calls) to specific target (e.g.$ConsoleLogSinkredirects to the console).MxI.$FileLogSinkis a sink which redirects traces (MxI.$Log.write()calls) to a file (e.g.log.txt).
Release 0.1.6 changelog
- Documentation upgrade 1/2: UML model diagram for the implementation sample
- Documentation upgrade 2/2: Paragraphs reordering ( Sample UML Model, "howtos" i.e How to Define an Interface class and Core API Reference are now before Installation and Usage and How to run the Unit Test)
Sample UML Model

Please find below the explanations with wich you may implement this model with the Core API privided by mixin-interface-api
How to Define an Interface class
Here is an example of an interface class (see ./src/test_classes/i_life_form.js. Here we define a single service: live()
- Inherit from
MxI.$IBaseInterface(or any other super_interface if applicable) by usingMxI.$Interface()just after the es6extendskeyword to define both that it is an interface class and that its super_interface isMxI.$IBaseInterface. Use
MxI.$raiseNotImplementedError()in order to guarantee that the service is provided by the implementation. This should be put in the Fallback implementation of each service defined by the interface.This will raise an Error if an implementation which declares it implements this interface misses one or more service implemention(s) (see paragraph on
MxI.$raiseNotImplementedErrorAPI service at the end of this document).Add the
MxI.$setAsInterface().$asChildOf()idiom after the class definition to define that this is an interface_class and what is its superclass.
Note: To remind that a class is an interface class, it is strongly advised to use the 'I prefix' naming convention as a reminder. This is a reminiscence of Hungarian notation , a fairly old identifier naming convention (e.g. see Microsoft COM)
const MxI = require('../mixin_interface_api.js').MxI;
//==================== 'ILifeForm' interface class ====================
class ILifeForm extends MxI.$Interface(MxI.$IBaseInterface) {
// Fallback implementation of 'live' service
live() {
MxI.$raiseNotImplementedError(ILifeForm, this);
} // ILifeForm.live()
} // 'ILifeForm' class
MxI.$setAsInterface(ILifeForm).$asChildOf(MxI.$IBaseInterface);
exports.ILifeForm = ILifeForm;Note: Each interface class must have a superclass (
MxI.$IBaseInterfaceif no other interface class applies). In the previous caseMxI.$setAsInterface()may be used without appending.$asChildOf(super_interface)idiom becauseMxI.$IBaseInterfacewill be the default superclass. However it is both cleaner, safer, more consistent and strongly advised to always use the full idiom (MxI.$setAsInterface().$asChildOf())
How to subclass an Interface class
Here is an example of a subclass of an interface class (see ./src/test_classes/i_animal.js). Here we want to define IAnimal as a subclass of the ILifeForm interface class.
- Use this syntax:
class IAnimal extends $Interface()to define thatIAnimalis a subclass ofILifeForm. Add the
MxI.$setAsInterface().$asChildOf()idiom just after the class definition.This is required so that
MxI.$isInstanceOf()works properly to identify an object both as an being an instance of an implementation class (and its superclasses) as well being an instance of an interface class (and its superclasses).We then define a new service:
run(). It will be a regular method of a javascript es6 class.Use
MxI.$raiseNotImplementedError()in order to guarantee that the service is provided by the implementation class. This should be put in the Fallback implementation of each service defined by the interface.This will raise an error if the implementation class does'nt provide (directly or via inheritance) one of the service(s) defined by the interface class(es) (see paragraph on
MxI.$raiseNotImplementedErrorAPI service at the end of this document).
const MxI = require('../mixin_interface_api.js').MxI;
const ILifeForm = require('./i_life_form.js').ILifeForm;
//==================== 'IAnimal' interface class ====================
class IAnimal extends MxI.$Interface(ILifeForm) {
// Fallback implementation of 'run' service
run() {
MxI.$raiseNotImplementedError(IAnimal, this);
} // IAnimal.run
} // 'IAnimal' class
MxI.$setAsInterface(IAnimal).$asChildOf(ILifeForm);
exports.IAnimal = IAnimal;How to code an Implementation class
Here is an example of an implementation class (see ./src/test_classes/animal.js). An implementation may implement one or more interface classes. To implement the services (i.e. defined by the interface class(es) that are declared as implemented by this class) we must:
Inherit from
MxI.$Object(or any of its subclasses) by using theMxI.$Implementation().$with()idiom just after the es6extendskeyword to define both a subclass and the interface class(es) that it implements (IAnimalhere).Inheriting from
MxI.$Objectalso provides the automatic instance naming feature (this feature is provided by thenameattribute on each instance ofMxI.$Objector any of its subclasses. Each instance name is generated from its class name and its instance count. Instances are named with SerpentCase pattern (e.g.flying_fish_0)Put
MxI.$setClass(Animal).$asImplementationOf(ILifeForm, IAnimal)idiom just after the class definition.This is syntactically redundant but nevertheless required in order that
MxI.$isInstanceOf()works correctly (see paragraph onMxI.$isInstanceOfAPI service at the end of this document).Provide implementation of all services (e.g.
live(),run(), ...) defined in each interface as well as their parent interfaces.If a service is not provided it may be inherited from the parent implementation class.
const MxI = require('../mixin_interface_api.js').MxI;
const IAnimal = require('./i_animal.js').IAnimal;
const ILifeForm = require('./i_life_form.js').ILifeForm;
//==================== 'Animal' implementation class ====================
class Animal extends MxI.$Implementation(MxI.$Object).$with(IAnimal) {
constructor() {
super();
} // 'Animal' constructor
run() {
MxI.$Log.write("--> Animal.run: '%d'", this);
} // IAnimal.run()
live() {
MxI.$Log.write("--> Animal.live: '%d'", this);
} // ILifeForm.live()
} // 'Animal' class
MxI.$setClass(Animal).$asImplementationOf(IAnimal, ILifeForm);How to subclass an Implementation class
Here is an example of how to subclass an implementation class (see ./src/test_classes/cat.js). Here we want to both to subclass Animal and implement the IMammal interface class, this is how to do it:
- Inherit from
Animalby using theMxI.Implementation().$with()idiom just afterextendsto define both a subclass and the interfaces that it implements. Provide implementation of the service defined by
IMammal(suckle()). If a service from the parent interfaces is not provided then it may be inherited from the parent implementation class.Notice this is the case in the following sample: for
run()anlive(), as they are disabled by the__prefix then it is the implementation from the parent class which is inherited instead.Add the
MxI.$setClass(Cat).$asImplementationOf(IMammal)idiom just after the class definition.This is required so that
MxI.$isInstanceOf()works properly to identify an object both as being an instance of an implementatio class (and its superclass(es)) as well being an instance of an interface class (and its superclass(es)).
const MxI = require('../mixin_interface_api.js').MxI;
const Animal = require('./animal.js').Animal;
const IMammal = require('./i_mammal.js').IMammal;
//==================== 'Cat' implementation class ====================
class Cat extends MxI.$Implementation(Animal).$with(IMammal) {
constructor() {
super();
} // 'Cat' constructor
suckle() {
MxI.$Log.write("--> Cat.suckle: '%d'", this);
} // IMammal.suckle()
__run() {
MxI.$Log.write("--> Cat.run: '%d'", this);
} // IAnimal.run()
__live() {
MxI.$Log.write("--> Cat.live: '%d'", this);
} // ILifeForm.live()
} // 'Cat' class
MxI.$setClass(Cat).$asImplementationOf(IMammal);Notice that
IAnimal.run()andILifeForm.live()services are not provided, so they are inherited from the parent implementation class (Animal).
API Reference - Foreword
Please note the following keywords and their meaning:
API service: function provided by 'mixin-interface' (e.g.
Mxi.$isInstanceOf())
MxI: namespace for all the mixin-interface API services
object: instance of an _implementation class
service: function defined by an interface class (e.g.IAnimal.run())
type: either an implementation class (e.g.Animal) or an interface class (e.g.IAnimal)
interface: interface class
super_interface: superclass of the interface class
implementation: implementation class
super_implementation: superclass of the implementation class
...interfaces: list of implemented interfaces. The list is provided as interface class(es) separated by a comma (e.g.ILifeFormandIAnimal, ILifeFormare valid ...interfaces arguments)
Core API reference
- MxI.$isInstanceOf(): replacement for javascript
instanceofoperator - MxI.$isInterface(): checks if a type is an interface class or not
- MxI.$implements(): checks if a type implements an interface class or not
MxI.$getSuperclass(): get the superclass of a a _type
MxI.$Interface(): defines an interface class and its super_interface
MxI.$setAsInterface().$asChildOf(): defines that a class is an interface class and its super_implementation
This is syntactically redundant but nevertheless required in order that
MxI.$isInstanceOf()works correctlyMxI.$Implementation().$with(): defines an implementation class and its superclass (
Mxi.$Objectif no other class applies)MxI.$setClass().$asImplementationOf(): defines the interface class(es) implemented by an implementation class
MxI.$raiseNotImplementedError(): error handling when a service (defined by of an interface class) is not implemented
MxI.$Object.init(): Delayed Initialization feature
MxI.$Object.isInitialized(): checks if an object has been initialized
MxI.$ISingleton: interface class for the Singleton (i.e. Unique instance) design pattern (see
design-patterns-api)- MxI.$Singleton: Default implementation for
MxI.$ISingletoninterface - MxI.$isSingleton(): Checks if an object is a Singleton
MxI.$setAsSingleton(): Required to define that an implementation is a Singleton
MxI.$INullObject: interface class for the Null Object design pattern (see
design-patterns-api)- MxI.$NullObject: Default implementation for
MxI.$INullObjectinterface - MxI.$Null: Singleton of
MxI.$NullObject MxI.$isNull(): Returns
truein 2 cases. The first is when the input value is an object which is both a Null Object an a Singleton (typically the 'default Null Object' which isMxI.$Null). The second case is when the input value isundefinedLog Feature
This feature was previously provided as an extension (
MxI.$System, provided bymixin-interface).MxI.$Systemstill supports the previous implementation but is now deprecated.- MxI.$ILogSink: interface class for a sink (implementation of the Log feature, previously called Logger).
- MxI.$Log.write(): new implementation of trace requests (previously
MxI.$System.log()). - MxI.$Log.banner(): outputs a message within a banner.
- MxI.$Log.addSink(): declares a sink object (which must implement
$ILogSink). - MxI.$Log.getSinkCount(): returns the number of sinks.
- MxI.$Log.clearSinks(): deletes all the sinks.
- MxI.$ConsoleLogSink: default sink implementation class (sends trace messages to the console).
- MxI.$FileLogSink: default sink implementation class (sends trace messages to a file - e.g.
./log.txt).
Check if an object is an instance of a Type
MxI.$isInstanceOf(type, object)This service provides type-checking for an object (see ./test.js for a unit test of this feature). The type argument is either an implementation class or an interface class. This API service allows to identify an object as being both an instance of an interface class (and its superclass(es)), as well as an instance of an implementation class (and its superclass(es)
This service is a replacement for javascript
instanceofoperator
var a_cat = new Cat();
MxI.$Log.write(a_cat.name + " is a 'IMammal': " + MxI.$isInstanceOf(IMammal, a_cat));Check if a type is an Interface class
MxI.$isInterface(type)This service checks if type is an interface class (see ./test.js for a unit test of this feature). The type argument is either an implementation class or an interface class.
MxI.$Log.write("'IAnimal' is an interface ? " + MxI.$isInterface(IAnimal));Check if an implementation implements an interface class
MxI.$implements(implementation, interface)Get the superclass of a type
MxI.$getSuperclass(type)Definition of an Interface class
MxI.$Interface(super_interface)
MxI.$setAsInterface(interface).$asChildOf(super_interface) These services allow to define an interface class:
- Use
MxI.$Interface()after theextendsclause of the es6 javascriptclassdefinition - After the class definition, use the
MxI.$setAsInterface().$asChildOf()idiom
Example (see ./src/test_classes/i_animal.js for a full sample):
class IAnimal extends MxI.$Interface(ILifeForm) {
...
} // 'IAnimal' class
MxI.$setAsInterface(IAnimal).$asChildOf(ILifeForm);This code means that IAnimal is an interface class which is a subclass of ILifeForm
Implementation of Interface class(es)
MxI.$Implementation(super_implementation).$with(...interfaces)
MxI.$setClass(implementation).$asImplementationOf(...interfaces)These services allow to define an implementation class:
- Use
MxI.$Implementation()after theextendsclause of the es6 javascriptclassdefinition - After the class definition, use the
MxI.$setClass().$asImplementationOf()idiom
Example (see ./src/test_classes/animal.js for a full sample):
class Animal extends MxI.$Implementation(MxI.$Object).$with(IAnimal) {
...
} // 'Animal' class
MxI.$setClass(Animal).$asImplementationOf(IAnimal, ILifeForm);This code means:
Animalis an implementation class which is a subclass ofMxI.$ObjectAnimalimplements bothIAnimalandILifeForminterface classes
Error Handling: 'service not implemented'
MxI.$raiseNotImplementedError(_interface_, this)This service provides Error Handling when a service of an interface class is not provided by an implementation class. It should be used in the Fallback implementation for each service defined by the interface class.
Here is an example of how to use this API service (see ./src/test_classes/i_life_form.js:
class ILifeForm extends MxI.$Interface(MxI.$IBaseInterface) {
// Fallback implementation of 'live' service
live() {
MxI.$raiseNotImplementedError(ILifeForm, this);
} // ILifeForm.live()
} // 'ILifeForm' class
MxI.$setAsInterface(ILifeForm).$asChildOf(MxI.$IBaseInterface);Let's see what happens if the Animal implementation doesn't provide an implementation for the run() service §defined by IAnimal interface class).
If you want to test this use case, just rename run() to __run() in ./src/test_classes/animal.js ), then restart the Unit Test with node test.js in the command shell. An exception should be raised an you would get the following output:
throw new Error(error_msg);
^
Error: ** mixin-interface-api **
Error code: SERVICE_NOT_IMPLEMENTED
Description: 'IAnimal.run' not found on 'animal_0'
at throwErrorMessage (D:\_Dev\NodeJS\github\mixin-interface-api\src\mixin_interface_api.js:31:11)
at Object.$raiseNotImplementedError (D:\_Dev\NodeJS\github\mixin-interface-api\src\mixin_interface_api.js:45:9)
at Animal.run (D:\_Dev\NodeJS\github\mixin-interface-api\src\test_classes\i_animal.js:16:9)
at Object.<anonymous> (D:\_Dev\NodeJS\github\mixin-interface-api\test.js:34:13)
...
...Delayed Object Initialization
MxI.$Object().init(...args_init)
MxI.$Object().isInitialized()These services provide the Delayed Initialization feature.
Once
init()service is called, ifargs_initis provided it is accessible to all instances of implementation class(es) viathis._$args_init.An object may be initialized only once:
this._$args_initcannot then be set or changed.Short explanation on Delayed Initialization: a typical example in GUI programming is when you need a widget (e.g. PushButton) but its container (e.g. CommandBar) is not yet created or known at instanciation time, so you may use later
init()service so that the PushButton can set its container (e.g. by calling setContainer() in the PushButton's implementation of init() service).
'Singleton' feature
MxI.$ISingleton
MxI.$Singleton
MxI.$isSingleton(object)
MxI.$setAsSingleton(implementation)Please find below a code sample from ./test_.js which uses MxI.$isSingleton():
MxI.$Log.write("isSingleton(%s): %s", MxI.$Null, MxI.$isSingleton(MxI.$Null));Please find below a code sample from ./src/mixin_interface_api.js which uses MxI.$setAsSingleton():
class $NullObject extends $Implementation($Singleton).$with($ISingleton, $INullObject) {
constructor(...args) {
super();
this._$name = MXI_NULL;
} // '$NullObject' constructor
} // '$NullObject' implementation class
$setClass($NullObject).$asImplementationOf($INullObject, $ISingleton);
$setAsSingleton($NullObject);'Null Object' feature
MxI.$INullObject
MxI.$NullObject
MxI.$Null
MxI.$isNull(object)Example: a default implementation of MxI.$INullObject interface
class $NullObject extends $Implementation($Singleton).$with($ISingleton, $INullObject) {
constructor(...args) {
super();
this._$name = MXI_NULL;
} // '$NullObject' constructor
} // '$NullObject' implementation class
$setClass($NullObject).$asImplementationOf($INullObject, $ISingleton);
$setAsSingleton($NullObject);Please find below a code sample which both logs MxI.$Null singleton and calls MxI.$isNull()
MxI.$Log.write("MxI.$isNull(%s): %s", MxI.$Null, MxI.$isNull(MxI.$Null));
MxI.$isNull()Returnstruein 2 cases. The first is when the input value is an object which is both a Null Object an a Singleton (typically the 'default Null Object' which isMxI.$Null). The second case is when the input value isundefined
Installation and Usage
npm install mixin-interface-api -SHow to run the Unit Test
Step 1: Install Prerequisite Tools
Step 2: Clone the 'mixin-interface-api' repository locally
Open a command shell then enter the following commands:
git clone git://github.com/Echopraxium/mixin-interface-api
cd mixin-interface-api
npm updateStep 3: Run the Unit Test
Now enter the following command:
node test.jsYou should get this kind of output (please find here the full output):
=============================================================
======== Unit Test for 'mixin-interface-api' package ========
=============================================================
1.Instance of 'Animal' created: animal_0
'animal_0' is a 'MxI.$Object' ? true
'animal_0' is a 'ILifeForm' ? true
'animal_0' is a 'IAnimal' ? true
'animal_0' is a 'Animal' ? true
'animal_0' is a 'IMammal' ? false
--> Animal.run
--> Animal.live
----------
2. Instance of 'Cat' created: cat_0
'cat_0' is a 'MxI.$Object' ? true
'cat_0' is a 'Animal' ? true
'cat_0' is a 'Cat' ? true
'cat_0' is a 'ILifeForm' ? true
'cat_0' is a 'IAnimal' ? true
'cat_0' is a 'IMammal' ? true
--> Animal.run
--> Cat.suckle
--> Animal.live
...Please notice in the previous output that an implementation class may inherit functions (i.e implementation of services from interface classes) from its parent class (e.g.
FlyingFishinheritsIAnimal.run()andIAnimal.live()implementations fromAnimal) but it is also possible to override these default implementations them as well.
References
- API Design: Avoid Logging in your APIs
http://tutorials.jenkov.com/api-design/avoid-logging.html - A fresh look at JavaScript Mixins
https://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/ - Functional Mixins in ECMAScript 2015
http://raganwald.com/2015/06/17/functional-mixins.html - JavaScript Mixins: Beyond Simple Object Extension https://lostechies.com/derickbailey/2012/10/07/javascript-mixins-beyond-simple-object-extension/
- "Real" Mixins with JavaScript Classes http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
- Classes versus Prototypes in Object-Oriented Languages ftp://ftp.cs.washington.edu/pub/constraints/papers/fjcc-86.pdf
- The Theory of Classification - Part 15: Mixins and the Superclass Interface
http://www.jot.fm/issues/issue_2004_11/column1/ - CSE 505 Lecture Notes Archive - Prototype-based Programming https://en.wikipedia.org/wiki/Prototype-based_programming
- 19. Classes, Metaclasses, and Prototype-Based Languages https://courses.cs.washington.edu/courses/cse505/00au/lectures/19-metaclasses.txt
- Safe Metaclass Composition Using Mixin-Based Inheritance - ESUG http://www.esug.org/data/ESUG2003/mixinsforsafemetaclasscomposition.nourybouraqadi.bled25aug2003.pdf
- CSE 341: Smalltalk classes and metaclasses http://courses.cs.washington.edu/courses/cse341/04wi/lectures/17-smalltalk-classes.html
- Topiarist: A JavaScript OO library featuring mixins, interfaces & multiple inheritance
http://bladerunnerjs.org/blog/topiarist/
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago