Implementando el patrón fábrica en ExtJS

English

El patrón de fabrica consiste en definir una interfaz dedicada a la construcción de objetos la cual delega a sus subclases, la responsabilidad de instanciar dichos objetos (Wikipedia).  Es un patrón básico que puede ser implementado de manera sencilla en diferentes lenguajes de programación como Java, pero puede resultar complicado en otros lenguajes como JavaScript.

Este documento muestra como construir una fabrica GridPanel a traves de la implementación del patrón fabrica en ExtJS (framework de JavaScript). La fabrica GridPanel, podra instanciar diferentes tipos de componentes tipo Panel dependiendo de los parametros de entrada.

Las siguientes aplicaciones y librerías fueron utilizadas:

  • Ext JS 4
  • Windows 7
  • WAMP
  • Eclipse (con JavaScript Developer Tools)

1) Antes de crear un proyecto JavaScript, nos aseguraremos que el ambiente esta propiamente configurado. Primero, hay que copiar la carpeta de ExtJS dentro del directorio www de  WAMP (en mi caso, esta se localiza en (C:\wamp\www). Despues, tecleamos la siguiente URLen el navegador: http://localhost/ext401/  (cambiar la ruta para ExtJS)

image

2) Crear un proyecto en Eclipse, el cual se llamara  “FactoryPattern”. Asegúrense que el workspace apunte a la carpeta de examples de ExtJS(\wamp\www\ext401\examples\):

image

3) Ahora, iniciaremos creando los componentes que van a ser instanciados por nuestra fabrica Para este ejemplo, la fabrica GridPanel podrá crear 2 tipos de grids:

  1. Grid básico: grid simple que lista el nombre de una serie de compañías.
  2. Grid avanzado.-  similar al anterior, pero cuando se selecciona un registro los detalles se despliegan en otro grid.

4) El siguiente código define el grid básico:

Ext.namespace('Ext.ux.Customgrids');

/**
 * SimpleGrid Class
 *
 * @version 1.0
 * @class Ext.ux.Customgrids.SimpleGrid
 * @extends Ext.grid.Panel
 * @constructor
 * @param {Object} config Configuration options
 */

Ext.define('Ext.ux.Customgrids.SimpleGrid',
        {
            extend: 'Ext.grid.Panel',
            alias: 'widget.simplegrid',
            constructor: function(config)
            {
                Ext.apply(this,
                        {
                    columns: [{
                            text     : 'Company',
                            flex     : 1,
                            sortable : false,
                            dataIndex: 'company'
                        },
                        {
                            text     : 'Price',
                            width    : 75,
                            sortable : true,
                            renderer : 'usMoney',
                            dataIndex: 'price'
                        },
                        {
                            text     : 'Change',
                            width    : 75,
                            sortable : true,
                            dataIndex: 'change'
                        }
                    ]
                 }
                );

                Ext.ux.Customgrids.SimpleGrid.superclass.constructor.apply(this, arguments);
            }
        }
);

5) Antes de continuar con el componente de  ‘grid avanzado’, debemos definir en un archivo diferente la clase encargada de desplegar los detalles de la compañía:

Ext.namespace('Ext.ux.Customgrids');

/**
 * DetailsPanel Class
 *
 * @version 1.0
 * Panel to display the company details.
 * @class Ext.ux.Customgrids.DetailsPanel
 * @alias widget.detailspanel
 * @extends Ext.grid.Panel
 * @constructor
 * @param {Object} config Configuration options
*/
Ext.define('Ext.ux.Customgrids.DetailsPanel',
{
    extend: 'Ext.form.Panel',
    alias: 'widget.detailspanel',
    bodyPadding: 7,
    items      : [
        Ext.create('Ext.form.field.Text', {
        fieldLabel : 'Company',
        name       : 'company'
        }),
        Ext.create('Ext.form.field.Number', {
            fieldLabel : 'Price',
            name       : 'price',
            allowBlank: false,
            allowDecimals: true,
            minValue: 0,
            maxValue: 150000
            }),
        Ext.create('Ext.form.field.Number', {
            fieldLabel : 'Change',
            name       : 'change',
            allowBlank: false,
            allowDecimals: true,
            minValue: 0,
            maxValue: 150000
            })
    ],
    initComponent: function()
    {
        Ext.ux.Customgrids.DetailsPanel.superclass.initComponent.call(this);
    }
});

6) El componente ‘grid avanzado’ es un Ext.Panel que contiene 2 elementos:

  • DetailsPanel (declarado anteriormente)
  • SimpleGrid

El siguiente código corresponde a este componente (basado en el ejemplo – Data binding example de ExtJS):

Ext.namespace('Ext.ux.Customgrids');

/**
 * AdvancedGrid Class
 * @version 1.0
 * @class Ext.ux.Customgrids.AdvancedGrid
 * @alias widget.advancedgrid
 * @extends Ext.Panel
 * @constructor
 * @param {Object} config Configuration options
 */

Ext.define('Ext.ux.Customgrids.AdvancedGrid',
        {
            extend: 'Ext.Panel',
            alias: 'widget.advancedgrid',
            frame: true,
            width: 500,
            height: 400,
            layout: 'border',
            constructor: function(config)
            {
                Ext.apply(this,
                {
                    items: [
                    {
                        xtype: 'simplegrid',
                        itemId: 'mainPanel',
                        region: 'center',
                        store: config.store,
                        split: true
                    },
                    {
                        xtype: 'detailspanel',
                        itemId: 'detailPanel',
                        region: 'north',
                        height: 140
                    }
                      ]
                }
                );
                Ext.ux.Customgrids.AdvancedGrid.superclass.constructor.apply(this, arguments);
            },
            initEvents: function() {
                // call the superclass's initEvents implementation
                Ext.ux.Customgrids.AdvancedGrid.superclass.initEvents.call(this);

                // now add application specific events
                // notice we use the selectionmodel's rowselect event rather
                // than a click event from the grid to provide key navigation
                // as well as mouse navigation
                var GridSm = this.getComponent('mainPanel').getSelectionModel();
                ('selectionchange', function(sm, rs) {
                if (rs.length) {
                    var detailPanel = Ext.getCmp('detailPanel');
                    detailPanel.getForm().loadRecord(rs[0]);
                }
            })
                GridSm.on('selectionchange', this.onRowSelect, this);
            },
            // add a method called onRowSelect
            // This matches the method signature as defined by the 'rowselect'
            // event defined in Ext.selection.RowModel
            onRowSelect: function(sm, rs) {
                // getComponent will retrieve itemId's or id's. Note that itemId's
                // are scoped locally to this instance of a component to avoid
                // conflicts with the ComponentManager
                if (rs.length) {
                    var detailPanel = this.getComponent('detailPanel');
                    detailPanel.getForm().loadRecord(rs[0]);
                }
            }
});

7) Hasta el momento ya tenemos todos los componentes necesarios. Si quisiéramos instanciar alguno de estos elementos, se haría de la siguiente forma:

    var simple = new Ext.ux.Customgrids.SimpleGrid({
        height: 350,
        width: 400,
        title: 'Simple Grid',
        store: store,
        renderTo: 'grid_div'
    });

    var advanced = new Ext.ux.Customgrids.AdvancedGrid({
        height: 350,
        width: 400,
        title: 'Advanced Grid',
        store: store,
        renderTo: 'advancedgrid_div'
    });

8 ) Aqui es donde el patron fabrica resulta de utilidad. Como se dijo antes, este patron define una interfaz para crear objetos. Desafortunadamente, ExtJS (y JavaScript) no tiene una palabra reservada  ‘interface’ como en otros lenguajes de programacion (Java, C#, etc). Pero podemos lograr algo similar (una clase abstracta) a traves del siguiente codigo:

/* Abstract class */
Ext.namespace('Ext.ux.factory');

/**
 * AbstractFactory Class
 * @version 1.0
 * Abstract class to build factories.
 * @class Ext.ux.factory.AbstractFactory
 */

Ext.define('Ext.ux.factory.AbstractFactory', {
    create : function(model, config){
                    throw "Unimplemented method.";
               }
});

9) Esta clase abstracta sera heredada por nuestra fábrica:

Ext.namespace('Ext.ux.factory');

/**
 * GridFactory Class (Singleton)
 * @version 1.0
 * Grid factory.
 * @class Ext.ux.factory.GridFactory
 * @extends Ext.ux.factory.AbstractFactory
 * @constructor
 * @param {Object} config Configuration options
 */

Ext.define('Ext.ux.factory.GridFactory', {
    extend: 'Ext.ux.factory.AbstractFactory',
    alias: 'widget.gridfactory',
    singleton: true,
    create : function(model, config){
        var grid;
        switch(model)
        {
            case 'simple':
                grid = new Ext.ux.Customgrids.SimpleGrid(config);
                break;
            case 'advanced':
                grid = new Ext.ux.Customgrids.AdvancedGrid(config);
                break;
        }

        return grid;
    }
});

Esta clase se declara como ‘singleton’ porque solo necesitamos una fábrica que se encargue de construir todos los objetos (grids) que necesitemos. El codigo es muy claro, la fabrica puede construir 2 tipos de objetos: SimpleGrid y AdvancedGrid.

10) Ahora que tenemos la fábrica GridPanel, podemos instanciar nuestros objetos de la siguiente manera:

 var simple = Ext.ux.factory.GridFactory.create('simple', {
        height: 350,
        width: 400,
        title: 'Simple Grid',
        store: store,
        renderTo: 'grid_div'
    });

    var advanced = Ext.ux.factory.GridFactory.create('advanced', {
        height: 350,
        width: 400,
        title: 'Advanced Grid',
        store: store,
        renderTo: 'advancedgrid_div'
    });

11) El codigo se puede probar con el siguiente HTML:

example.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
 <link rel="stylesheet" type="text/css" href="../shared/example.css" />
 <link rel="stylesheet" type="text/css" href="../ux/css/CheckHeader.css" />
 <script type="text/javascript" src="../../bootstrap.js"></script>

<script type="text/javascript" src="SimpleGrid.js"></script>
<script type="text/javascript" src="DetailsPanel.js"></script>
<script type="text/javascript" src="AdvancedGrid.js"></script>
<script type="text/javascript" src="AbstractFactory.js"></script>
<script type="text/javascript" src="GridFactory.js"></script>
<script type="text/javascript" src="example.js"></script>

<title>Implementing Factory Pattern in ExtJS</title>
</head>
<body>
<div id="main">
        <div id="grid_div" style="float:left;  padding: 15px;"></div>
        <div id="advancedgrid_div"></div>
        </div>
</body>
</html>

example.js

Ext.Loader.setConfig({
    enabled: true
});

Ext.require([
    'Ext.grid.*'
]);

Ext.onReady(function(){

    //sample data
    var myData = [
                  ['3m Co',                               71.72, 0.02,  0.03],
                  ['Alcoa Inc',                           29.01, 0.42,  1.47],
                  ['Altria Group Inc',                    83.81, 0.28,  0.34],
                  ['American Express Company',            52.55, 0.01,  0.02],
                  ['American International Group, Inc.',  64.13, 0.31,  0.49],
                  ['AT&T Inc.',                           31.61, -0.48, -1.54],
                  ['Boeing Co.',                          75.43, 0.53,  0.71],
                  ['Caterpillar Inc.',                    67.27, 0.92,  1.39],
                  ['Citigroup, Inc.',                     49.37, 0.02,  0.04],
                  ['E.I. du Pont de Nemours and Company', 40.48, 0.51,  1.28],
                  ['Exxon Mobil Corp',                    68.1,  -0.43, -0.64],
                  ['General Electric Company',            34.14, -0.08, -0.23],
                  ['General Motors Corporation',          30.27, 1.09,  3.74],
                  ['Hewlett-Packard Co.',                 36.53, -0.03, -0.08],
                  ['Honeywell Intl Inc',                  38.77, 0.05,  0.13],
                  ['Intel Corporation',                   19.88, 0.31,  1.58],
                  ['International Business Machines',     81.41, 0.44,  0.54],
                  ['Johnson & Johnson',                   64.72, 0.06,  0.09],
                  ['JP Morgan & Chase & Co',              45.73, 0.07,  0.15],
                  ['McDonald\'s Corporation',             36.76, 0.86,  2.40],
                  ['Merck & Co., Inc.',                   40.96, 0.41,  1.01],
                  ['Microsoft Corporation',               25.84, 0.14,  0.54],
                  ['Pfizer Inc',                          27.96, 0.4,   1.45],
                  ['The Coca-Cola Company',               45.07, 0.26,  0.58],
                  ['The Home Depot, Inc.',                34.64, 0.35,  1.02],
                  ['The Procter & Gamble Company',        61.91, 0.01,  0.02],
                  ['United Technologies Corporation',     63.26, 0.55,  0.88],
                  ['Verizon Communications',              35.57, 0.39,  1.11],
                  ['Wal-Mart Stores, Inc.',               45.45, 0.73,  1.63]
              ];

    //data store
    var store = Ext.create('Ext.data.ArrayStore', {
        fields: [
           {name: 'company'},
           {name: 'price',      type: 'float'},
           {name: 'change',     type: 'float'}],
        data: myData
    });

    /* DECLARING OBJECTS WITHOUT FACTORY PATTERN
    var simple = new Ext.ux.Customgrids.SimpleGrid({
        height: 350,
        width: 400,
        title: 'Simple Grid',
        store: store,
        renderTo: 'grid_div'
    });

    var advanced = new Ext.ux.Customgrids.AdvancedGrid({
        height: 350,
        width: 400,
        title: 'Advanced Grid',
        store: store,
        renderTo: 'advancedgrid_div'
    });
    */

    //DECLARING OBJECTS WITH FACTORY PATTERN
    var simple = Ext.ux.factory.GridFactory.create('simple', {
        height: 350,
        width: 400,
        title: 'Simple Grid',
        store: store,
        renderTo: 'grid_div'
    });

    var advanced = Ext.ux.factory.GridFactory.create('advanced', {
        height: 350,
        width: 400,
        title: 'Advanced Grid',
        store: store,
        renderTo: 'advancedgrid_div'
    });

});

12) Al cargar la pagina HTML, se deben observar los siguientes componentes:

image

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: