viernes, 6 de junio de 2014

Javascript OOP, Visual Method

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)

No hay comentarios: