Let's do this inheritance example with Javascript:
All objects must calculate area and perimeter through a common interface and must be generated through a factory.
/* IShape -- abstract class */
function IShape(area, perimeter) {
return { "area":area, "perimeter":perimeter };
};
/* Circle constructor */
function circle() {
var area = function(radius) {
return Math.PI * radius * radius;
};
var perimeter = function(radius) {
return 2 * Math.PI * radius;
};
return IShape(area, perimeter);
};
/* Rectangle constructor */
function rectangle() {
var area = function(length, width) {
return length * width;
};
var perimeter = function(length, width) {
return (2 * length) + (2 * width);
};
return IShape(area, perimeter);
};
/* Triangle constructor */
function triangle() {
var area = function(base, height) {
return base * height;
};
var perimeter = function() {
return undefined;
};
return IShape(area, perimeter);
};
/* Square -- child class -- constructor */
function square() {
var extendedObject = rectangle();
var area = function(side) {
return extendedObject.area(side, side);
};
var perimeter = function(side) {
return extendedObject.perimeter(side, side);
};
return IShape(area, perimeter);
};
/* Shapes factory */
function shapesFactory(shapeId) {
switch (shapeId.toLowerCase()) {
case "circle":
return circle();
case "rectangle":
return rectangle();
case "triangle":
return triangle();
case "square":
return square();
default:
return undefined;
}
};
// TESTS
var shape1 = shapesFactory("circle");
console.log("Circle(radius=1)."+
" Area: " + shape1.area(1) +
" Perimeter: " + shape1.perimeter(1));
// output:
// Circle(radius=1). Area: 3.141592653589793 Perimeter: 6.283185307179586
var shape2 = shapesFactory("rectangle");
console.log("Rectangle(length=2, width=3)."+
" Area: " + shape2.area(2,3) +
" Perimeter: " + shape2.perimeter(2,3));
// output:
// Rectangle(length=2, width=3). Area: 6 Perimeter: 10
var shape3 = shapesFactory("triangle");
console.log("Triangle(base=2, height=3)."+
" Area: " + shape3.area(2,3) +
" Perimeter: " + shape3.perimeter(2,3));
// output:
// Triangle(base=2, height=3). Area: 6 Perimeter: undefined
var shape4 = shapesFactory("square");
console.log("Square(radius=1)."+
" Area: " + shape4.area(1) +
" Perimeter: " + shape4.perimeter(1));
// output:
// Square(radius=1). Area: 1 Perimeter: 4
Using JQuery the factory could be an object on another file:
/* Shapes factory */
jQuery.shapesFactory = function(shapeId) {
/* IShape -- abstract class */
function IShape(area, perimeter) {
return { "area":area, "perimeter":perimeter };
};
/* Circle constructor */
function circle() {
var area = function(radius) {
return Math.PI * radius * radius;
};
var perimeter = function(radius) {
return 2 * Math.PI * radius;
};
return IShape(area, perimeter);
};
/* Rectangle constructor */
function rectangle() {
var area = function(length, width) {
return length * width;
};
var perimeter = function(length, width) {
return (2 * length) + (2 * width);
};
return IShape(area, perimeter);
};
/* Triangle constructor */
function triangle() {
var area = function(base, height) {
return base * height;
};
var perimeter = function() {
return undefined;
};
return IShape(area, perimeter);
};
/* Square -- child class -- constructor */
function square() {
var extendedObject = rectangle();
var area = function(side) {
return extendedObject.area(side, side);
};
var perimeter = function(side) {
return extendedObject.perimeter(side, side);
};
return IShape(area, perimeter);
};
/* Shapes factory */
switch (shapeId.toLowerCase()) {
case "circle":
return circle();
case "rectangle":
return rectangle();
case "triangle":
return triangle();
case "square":
return square();
default:
return undefined;
}
};
// TESTS - Another file -
var shape1 = $.shapesFactory("circle");
console.log("Circle(radius=1)."+
" Area: " + shape1.area(1) +
" Perimeter: " + shape1.perimeter(1));
// output:
// Circle(radius=1). Area: 3.141592653589793 Perimeter: 6.283185307179586
var shape2 = $.shapesFactory("rectangle");
console.log("Rectangle(length=2, width=3)."+
" Area: " + shape2.area(2,3) +
" Perimeter: " + shape2.perimeter(2,3));
// output:
// Rectangle(length=2, width=3). Area: 6 Perimeter: 10
var shape3 = $.shapesFactory("triangle");
console.log("Triangle(base=2, height=3)."+
" Area: " + shape3.area(2,3) +
" Perimeter: " + shape3.perimeter(2,3));
// output:
// Triangle(base=2, height=3). Area: 6 Perimeter: undefined
var shape4 = $.shapesFactory("square");
console.log("Square(radius=1)."+
" Area: " + shape4.area(1) +
" Perimeter: " + shape4.perimeter(1));
// output:
// Square(radius=1). Area: 1 Perimeter: 4
Part II: Managing to preserve Liskov's Substitution Principle (LSP)