Real abstract method in javascript - javascript

I would like to create an abstract class in javascript, which implements certain methods, calls abstract ones and let the 'inherited' classes to implement these abstract ones. I've read a lot about js prototyping. Every single suggested implementation of abstract classes and methods in javascript seems to be a simple inheritance, but not real abstraction.
Here is a really simple example to show what i want to achieve:
var abstractClass = {
abstractMethod: function() { /* i don't know yet what i'm going to do */ },
concreteMethod: function() {
abstractClass.abstractMethod();
}
}
specializedClass = Object.create(abstractClass);
specializedClass.abstractMethod = function() {
alert('Now i know what to do');
}
specializedClass.concreteMethod();
My question: is there a non-hacky, non-workaround way to make abstract classes in javascript?

inside the methods, use this instead of the named class/variable, which is duplicated/broken by your Object.create() call.
var abstractClass = {
abstractMethod: function() { /* i don't know yet what i'm going to do */ },
concreteMethod: function() {
this.abstractMethod(); // < made generic with this
}
}
specializedClass = Object.create(abstractClass);
specializedClass.abstractMethod = function() {
alert('Now i know what to do');
}
specializedClass.concreteMethod();

Related

Javascript - Call parent function and extend method

This is probably a stupid question, but is there way in Javascript (ES5 preferred) to "extend" a class function similar to how i can i extend a parent' function in PHP ?
Basicly, i have this class hierarchy from System -> Weapon -> Dual and i would like Dual to use the code from System.setState() and then do some more stuff.
Note i use pre ES6 syntax for my hierarchy.
function System(system){
this.setState = function(){
//do stuff
}
}
function Weapon(system){
System.call(this, system);
}
Weapon.prototype = Object.create(System.prototype);
function Dual(system){
Weapon.call(this, system);
this.setState = function(){ // this is the problem
System.prototype.setState(); // error - not defined
//Weapon.protoype.setState() doesnt work either
//do more stuff
}
}
Dual.prototype = Object.create(Weapon.prototype);
Because setState is an instance property of System it does not exist on System.proptotype so you can't call it using System.prototype.setState.call. If you want to call it in this case, just create an object from System like so
function Dual(system){
Weapon.call(this, system);
var parent = new System(system);
this.setState = function() {
parent.setState(); // done
}
}
Instance properties are duplicated on each individual object ( they don't share). Whereas, prototype properties will be shared among children( they are not duplicated on child classes). To make all System 's subclasses share setState function, add it to System 's prototype
function System (arg) { ... }
System.prototype.setState = function () {...}
Now in your child classes, you can do
function Dual(system){
Weapon.call(this, system);
this.setState = function() {
System.prototype.setState.call(this); // done
}
}
First, you should set your instance methods on the prototype:
System.prototype.setState = function() {
// your stuff
}
This will improve performance and allow you to inherit the method without constructing a System instance.
Then, you just need to call System's version of setState on the right object (the instance of Dual) instead of calling it on System.prototype:
Dual.prototype = Object.create(Weapon.prototype, {
'setState': { value: function(){
System.prototype.setState.call(this) // fixed
// other stuff
}}
})

How to register Polymer behavior with ES6?

If I do
Polymer({
is: 'pp-app',
behaviors: [PlayPlan.HelperBehavior],
scrollPageToTop() {
document.getElementById('mainContainer').scrollTop = 0;
},
onDataRouteClick() {
var drawerPanel = document.querySelector('#paperDrawerPanel');
if (drawerPanel.narrow) {
drawerPanel.closeDrawer();
}
}
});
The behavior here will works fine, but in es6
class PlayPlanApp {
beforeRegister() {
this.is = 'pp-app';
this.properties = {};
this.behaviors = [PlayPlan.HelperBehavior];
}
scrollPageToTop() {
document.getElementById('mainContainer').scrollTop = 0;
}
onDataRouteClick() {
var drawerPanel = document.querySelector('#paperDrawerPanel');
if (drawerPanel.narrow) {
drawerPanel.closeDrawer();
}
}
}
Polymer(PlayPlanApp);
The behavior will not works, how to specify behaviors when using es6 ?
This is caused by
registerCallback: function() {
// TODO(sjmiles): perhaps this method should be called from polymer-bootstrap?
this._desugarBehaviors(); // abstract
this._doBehavior('beforeRegister'); // abstract
this._registerFeatures(); // abstract
this._doBehavior('registered'); // abstract
}
_doBehavior('beforeRegister') is after _desugarBehaviors, here is an work around:
get behaviors() {
return [PlayPlan.HelperBehavior];
}
BTW, this will not work, because the constructor never get called(commented in Polymer.Base)
constructor() {
return [PlayPlan.HelperBehavior];
}
Note that the answer given by wener will not work if you use extended behaviors. Extended behaviors in Polymer are arrays of behaviors themselves. I don't know what's going on exactly but apparently Polymer is parsing the protoype of your class and flattening the behavior array, writing the flattened version back to your prototype. The fixed getter function will ignore the flattened version and always return the deep array. That will not do. So you have two options.
You can flatten the array yourself:
get behaviors() {
return [
MyFlatBehavior, MyExtendedBehavior
].reduce(function(a, b) {return a.concat(b);});
}
However, since Polymer may call get behaviors a lot (it does in my case), this will introduce a minor performance penalty. The alternative is letting it set the behaviors:
let SkeletonPolymerBehavior = [MyFlatBehavior, MyExtendedBehavior];
class SkeletonPolymer {
get behaviors( ) {return SkeletonPolymerBehavior;}
set behaviors(b) {SkeletonPolymerBehavior = b;}
...
}
Finally you can flatten the array right away:
let SkeletonPolymerBehavior = [
MyFlatBehavior, MyExtendedBehavior
].reduce(function(a, b) {return a.concat(b);});
class SkeletonPolymer {
get behaviors( ) {return SkeletonPolymerBehavior;}
...
}
Note that my flatten function is not safe! It worked in my case, but it will fail when extending extended behaviors and possibly in other cases. You definitely need something more sophisticated there. If you don't want to bother (and I see no compelling reason to) you should definitely go with the second version, letting Polymer set the behaviors.
Final note: I tried abstracting that away into an es2015 Behaviors mixin. However, Polymer's prototype magic does not catch stuff inherited from base classes and the getters and setters defined there are never called.
However, it is possible to mess with the prototype the es5 way. This utility function will ease the addition of extended behaviors:
function addBehaviors(clas, ...behaviors) {
let bhv = behaviors;
Object.defineProperty(clas.prototype, "behaviors", {
get:function( ) {return bhv;},
set:function(b) {return bhv = b;}
});
}
Usage:
class SkeletonPolymer {...}
addBehaviors(SkeletonPolymer, MyFlatBehavior, MyExtendedBehavior);

Javascript Inheritance subclass calling by default

I have a scenario in which i need to override sample.A in sample.B
and when i click i will call a function from which i need to call sample.A.Test() which need to internally call the overridden function(Test) in sample.B and execute only this function. is it possible ?
My peers told that java script will call the subclass function by default and get executed, is it true?
The above scenario
sample.A = (function () {
return {
Test: function(){
console.debug('Test in Super class');
},
}
sample.B = (function () {
return {
Test: function(){
console.debug('Test in Sub class');
},
}
can this behavior be achieved through java script or jquery?
To enable 'inheritance' in JavaScript, you at the very least need to add some utility methods.
For instance:
function inheritMethods(fromA, intoB) {
for (x in fromB) {
if (typeof x === 'function') {
intoB[x] = fromA[x];
}
}
}
or something the like ...
But since 'inheritance' in the classical OO sense is not native to JavaScript there are various ways about emulating this. There is a nice discussion in 'JavaScript Patterns' under 'Code Reuse' on this, if you can get hold of the book.

Which pattern to use

I have a class - call it ThankYou that delivers these. Based on the different implementations, generic or Facebook, I need to deliver customized layout. Right now, I am building the HTML in JS and delivering the layout.
1) Sign up for email newsletters (for generic and Facebook implementation)
2) Teaser content (for generic implementation)
3) Facebook like(for Facebook ThankYou implementation)
Which design pattern do you think this is better implemented using - Factory or Mediator? I am just starting to utilize some design patterns in my code and would like to start off on the right foot.
Some notes:
a) Although the functionality might be the same, the layout might be different for generic and Facebook
If I don't use a design pattern, I could easily do this using an 'if' statement, but I am just looking for a more elegant solution.
I think that Factory more suitable in this case. You have base class (interface) called IThankYou that implements common methods and two classes extending base functionality. Factory stores mapping between type and class.
Small example code:
function IThankYou () {}
IThankYou.prototype = {
templates: { // this is common field for all instances extending this class
like: '<div class="b-like">Like</div>',
},
like: function () { throw "Unimplemented" }, // this method is not implemented in base class
commonMethod: function () { } // this is common method
};
function GenericThankYou (someParam) { this.someParam = someParam; };
GenericThankYou.prototype = new IThankYou;
GenericThankYou.prototype.like = function () {
// there you could use base class fields and methods
this.commonMethod();
};
function FacebookThankYou (someParam) { this.someParam = someParam; };
FacebookThankYou.prototype = new IThankYou;
FacebookThankYou.prototype.like = function () {
// there you could use base class templates map
};
var ThankYouFactory = {
typeMap: {
'facebook' : FacebookThankYou,
'generic' : GenericThankYou
},
getConstructor: function (type) {
return this.typeMap[type];
}
};
ThankYouFactory.getConstructor('facebook')(ctorParam);
ThankYouFactory.getConstructor('generic')(ctorParam);

Can the Javascript Module Pattern be used for singletons and also for objects that are instantiated mutliple times?

I have one page with two types of forms. I have a single form of type A at the top, and then I have 1 or more forms of type B below it.
I use the Module pattern + jQuery to wire up all the events on my forms, handle validation, ajax calls, etc.
Is this the preferred/valid way to define a singleton, as in Form A, and a reusable object class, as in Form B? They are very similar and I'm not sure if I need to be using object the prototype property, new, or a different pattern. Everything seems to work for me but I'm afraid I'm missing some key error.
Form A javascript looks like this:
var MyProject.FormA = (function() {
var $_userEntry;
var $_domElementId;
var validate = function() {
if($_userEntry == 0) {
alert('Cannot enter 0!');
}
}
var processUserInput = function() {
$_userEntry = jQuery('inputfield', $_domElementId).val();
validate();
}
return {
initialize: function(domElementId) {
$_domElementId = domElementId;
jQuery($_domElementId).click(function() {
processUserInput();
}
}
}
})();
jQuery(document).ready(function() {
MyProject.FormA.initialize('#form-a');
});
Form B, which is initialized one or many times, is defined like so:
var MyProject.FormB = function() {
var $_userEntry;
var $_domElement;
var validate = function() {
if($_userEntry == 0) {
alert('Cannot enter 0!');
}
}
var processUserInput = function() {
$_userEntry = jQuery('inputfield', $_domElement).val();
validate();
}
return {
initialize: function(domElement) {
$_domElement = domElement;
jQuery($_domElement).click(function() {
processUserInput();
}
}
}
};
jQuery(document).ready(function() {
jQuery(".form-b").each(function() {
MyProject.FormB().initialize(this);
});
});
Both of your modules explicitly return objects which precludes the use of new.
Prototype inheritance isn't really compatible with the method hiding your achieving with this pattern. Sure you could re-write this with a prototype form object with your validate method defined on it, but then this method would be visible and you'd loose the encapsulation.
It's up to you whether you want the low memory footprint and speedy object initialization of prototypes (Shared methods exist only once, Instantiation runs in constant time) or the encapsulation of the module pattern which comes with a slight performance penalty (Multiple defined identical methods, Object instantiation slowed as every method has to be created every time)
In this case I would suggest that the performance difference is insignificant, so pick whatever you like. Personally I would say there is too much duplication between them and I would be inclined to unify them. Do you really need A to be a singleton? What are the dangers of it being accidentally instantiated twice? Seems like this is maybe over-engineering for this problem. If you really must have a singleton I'd wrap the non-singleton (B) class like this:
var getSingleton = function() {
var form = MyProject.FormB();
form.initialize("#form-a");
console.log("This is the original function");
getSingleton = function() {
console.log("this is the replacement function");
return form;
}
return form;
}
I think you just need to write a kind of jQ plugin:
(function($) {
$.fn.formValidator = function() {
return $(this).each(function() {
var $_domElement = $(this);
$_domElement.click(function() {
if($('inputfield', $_domElement).val() == 0) {
alert('Cannot enter 0!');
}
});
});
};
})(jQuery);
In this case you'll extend jQ element methods module and will be able to use it for any amount of elements at the page (for single or multiple elements collection). Also it will be chainable.
Usage:
$('#form-a').formValidator();
$('.form-b').formValidator();
Or
$('#form-a, .form-b').formValidator();
Ofcourse you can use a module to store this function:
ProjectModule.formValidator = function(selector) {
return $(selector).each(function() {
var $_domElement = $(this);
$_domElement.click(function() {
if ($('inputfield', $_domElement).val() == 0) {
alert('Cannot enter 0!');
}
});
});
};
Usage:
ProjectModule.formValidator('#form-a, .form-b');

Categories