Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

July 2010 - Posts

Introducción a IoC y DI: otro ejemplo web

Sigamos explorando Inversion of Control y Dependency Injection, dentro del Proyecto Hogwarts. Anteriores posts del tema:

Introducción a IoC y DI: Un ejemplo web
Introducción a IoC y DI: Hello, world flexible

Simplemente para ver otro ejemplo sencillo de programación con interfaces, veamos de agregar un objeto presenter en un ejemplo web. La forma de llenar la página con datos (dar datos a la vista) y la forma de atender comandos del usuario (un botón de aceptar en un formulario para grabar lo ingresado), lo separamos del "code behind" y le asignamos esa responsabilidad a un objeto Presenter:

Notemos que la vista que va a consumir no es una clase concreta, sino una interfaz. Eso nos va a permitir cambiar la vista, o probar a nuestro objeto presenter sin necesidad de darle una página web para operar, sino algún otro objeto más adecuado para nuestras pruebas.
El código de este ejemplo se puede bajar de PresenterExample.zip.

Esta vez, la solución tiene dos proyectos de librería de clases, y una aplicación web:

 

El primer proyecto se basa en nuestro anterior ejemplo: contiene el servicio, ayudado por repositorios.

El segundo proyecto define la interfaz que tiene que cumplir todas las vistas de lista de Customers, ICustomerListView:

namespace Customers.Presenters {  public interface ICustomerListView
   {
      IEnumerable<Customer> Customers { set; }
   }
}

El Presenter consume tanto a la vista como al servicio:

 public class CustomerPresenter  {
    private ICustomerService service;
    private ICustomerListView view;
    public CustomerPresenter(ICustomerService service, ICustomerListView view)
    {
       this.service = service;
       this.view = view;
    }
    public void Initialize()
    {
      this.view.Customers = this.service.GetCustomers();
    }
 }

Vean que el servicio también lo ve como una interfaz. Finalmente, el código de la página arma todos los objetos y se "conecta" con el Presenter para que éste la maneje. En nuestro ejemplo, sólo para llenar sus datos:

  public partial class _Default : System.Web.UI.Page, ICustomerListView
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                CustomerPresenter presenter = new CustomerPresenter(new CustomerService(new InMemoryCustomerRepository()), this);
                presenter.Initialize();
            }
        }
        public IEnumerable<Customer> Customers
        {
            set
            {
                this.grdCustomers.DataSource = value;
                this.grdCustomers.DataBind();
            }
        }
    }

Este ejemplo es un paso más hacia el tema de esta serie de post: Inversion of Control y Dependency Injection, términos que tenemos que definir. Pero, como en el post anterior, hay algo para mejorar: en el código de la página decidimos qué objetos concretos conectamos en un grafo de objetos colaboradores. Lo que quisiéramos mejorar es la definición de esos objetos concretos: tratar de no tenerlo de alguna forma fijo en el código, sino tener alguna otra manera de instanciarlos y de cambiar cuáles son las clases concretas a usar.

Este material será incluido en el curso online del Southworks Professional Improvement Program en http://pip.southworks.net/ (pueden ir ahí para ir explorando el curso de TDD ya publicado).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, Jul 28 2010 11:23 by lopez | 2 comment(s)

Proyecto Hogwarts: Sitio en línea, Southworks Professional Improvement Program

Después de haber publicado varios posts de avance sobre el Proyecto Hogwarts, finalmente pudimos hacer pública la versión beta (yo imagino que en beta constante) del material del curso de introducción a TDD, como parte del como parte del Southworks Professional Improvement Program. Pueden registrarse gratuitamente en:

http://pip.southworks.net/

Tienen lecciones en línea, con código, videos, ejercicios, explicaciones de conceptos, bibliografía, enlaces. Como comentaba al principio, para mí está en beta constante. Si hay algo que no se entiende o no queda claro, tienen un foro para plantear dudas. Si tienen una sugerencia, también puede dejarla ahí.

Está soportado en la plataforma open source de Moodle, con PHP y MySQL.

Tenemos otro curso en desarrollo: Inversion of Control y Dependency Injection (van a ver ahí que está dado de alta, incompleto). Próximos cursos a incluir: Mocks (como una continuación de TDD), principios S.O.L.I.D.

Ayer jueves 22, los buenos de @MartinSalias y @sebarenzi dictaron un taller presencial de TDD con máquinas acá en Buenos Aires, basado en el material del curso. La idea es que cada curso también tiene contrapartida de cursos, talleres presenciales. Esperamos redondear la idea, para organizar cómo darlos en otros lugares. Por ejemplo, ahora estamos probando que un instructor de una empresa, dicte el curso de introducción a TDD en su equipo.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Fri, Jul 23 2010 10:42 by lopez | with no comments

Introducción a IoC y DI: Un ejemplo web

Sigo posteando material de lo que se está produciendo en el Proyecto Hogwarts. Esta vez, sobre el tema de Inversion of Control y Dependency Injection. Dentro de poco, estará disponible un sitio en línea con un curso de TDD, y otros en construcción. En el anterior post:

Introducción a IoC, DI: Hello, world flexible

había planteado un ejemplo simple, veamos alguno apenas más complejo. Y veamos de plantearla con interfaces.

Lo que vimos en ese ejemplo es que:

- Un objeto A puede necesitar la ayuda de otro objeto B, pero conociendo la interface, sin ligarse a una clase en concreto

- Al objeto A alguien le provee el objeto B

Lo que vamos explorar ahora, es un ejemplo donde al objeto A le damos alguna vez un objeto B1, y otras veces el objeto B2. Para que no sea sólo una aplicación de consola, veamos de levantar el alcance, y hagamos un ejemplo web.

Sea una simple aplicación web, donde tenemos una página para presionar dos enlaces para leer y mostrar los clientes:

La idea es tener DOS implementaciones de una interface que se haga responsable de implementar un repositorio de clientes (digamos, una lista de clientes). Si presionamos en el primer enlace, se usará un repositorio en memoria:

 

Si presionamos en el segundo enlace, usaremos el repositorio de base de datos:

Pueden bajarse el ejemplo desde mi Skydrive CustomersExample.zip. Contiene una solución .NET y los scripts para crear la base de datos en SQL Server.

En este ejemplo, vamos a usar un servicio que se ayudará de un repositorio para conseguir los objetos que necesita la presentación. Veamos cómo se implementó.

Tenemos entonces, un servicio que necesita consumir un repositorio. Como en el ejemplo de Hello, world, el objeto consumidor no referencia a una clase concreta, sino a una interfaz:

Esa interfaz está definida como:

namespace Customers
{
 public interface ICustomerRepository
 {
 Customer GetById(int id);
 void Insert(Customer customer);
 void Update(Customer customer);
 void Delete(Customer customer);
 IQueryable<Customer> GetAll();
 }
}

Luego tenemos dos implementaciones de esta interfaz:

Una es la implementación del repositorio en memoria, parte del código:

 public class InMemoryCustomerRepository : ICustomerRepository
 {
 private IList<Customer> customers = new List<Customer>();
 public InMemoryCustomerRepository()
 {
 for (int k = 1; k <= 10; k++)
 {
 customers.Add(new Customer()
 {
 Id = k,
 Name = string.Format("Customer {0}", k),
 Address = string.Format("Address {0}", k),
 Notes = string.Format("Notes {0}", k)
 });
 }
 }
//....
 }

La otra implementación de la interfaz accede a una base de datos, código parcial:

 public class DatabaseCustomerRepository : ICustomerRepository
 {
 private string connectionstring;
 public DatabaseCustomerRepository(string connectionstring)
 {
 this.connectionstring = connectionstring;
 }
 public Customer GetById(int id)
 {
 SqlConnection conn = new SqlConnection(this.connectionstring);
 SqlCommand cmd = new SqlCommand("select Id, Name, Address, Notes from Customer where Id = @Id", conn);
 cmd.Parameters.Add(new SqlParameter("@Id", id));
 conn.Open();
 SqlDataReader reader = cmd.ExecuteReader();
 Customer customer = null;
 if (reader.Read())
 customer = this.GetCustomer(reader);
 reader.Close();
 conn.Close();
 return customer;
 }
///...
 }

Lo que se usó en este ejemplo es que al crear el objeto servicio le pasamos en los argumentos del constructor cuál implementación de repositorio queremos usar. Así, en la acción de ver por memoria, el código es:

protected void lnkMemory_Click(object sender, EventArgs e)
 {
 CustomerService service = new CustomerService(new InMemoryCustomerRepository());
 this.grdCustomers.DataSource = service.GetCustomers();
 this.grdCustomers.DataBind();
 }

La otra opción es por base de datos:

 protected void lnkData_Click(object sender, EventArgs e)
 {
 CustomerService service = new CustomerService(new DatabaseCustomerRepository("server=.\SQLEXPRESS;database=Customers;integrated security=true"));
 this.grdCustomers.DataSource = service.GetCustomers();
 this.grdCustomers.DataBind();
 }

Notemos que al servicio le "inyectamos" su ayudante, esta vez usando un parámetro de su constructor (en el ejemplo Hello World, habiamos inyectado usando propiedades).

Esto nos muestra:

- La capacidad de abstraer lo que necesitamos (la interfaz de repositorio) de cómo lo implementamos
- Nos permite cambiar la implementación y que todo siga funcionando igual

Acá usamos las DOS implementaciones al mismo tiempo. Pero es más común usar solo una.

Podríamos haber codificado el acceso a los clientes en el propio servicio. Pero lo separamos para:

- Poder mejorar el servicio (p.ej., en temas de seguridad) separando esas otras funcionalidades de la más simple de recuperación de clientes.

- Poder cambiar el ayudante, sin necesidad de cambiar el servicio y sus consumidores.

Hasta podemos ir armando una aplicación de demostración, usando durante semanas la implementación del repositorio en memoria. Mientras, en un desarrollo ágil, vamos iterando, mejorando nuestro dominio, SIN tener una base de datos por debajo. El cliente final va viendo nuestro avance, y solamente cuando tenemos algo más definido, vamos definiendo la base de datos subyacente y operando sobre ella.

También, este "approach" nos permite cambiar la implementación de la base de datos. Ahora, la implementación va contra SQL Server, pero si mañana necesitamos ir contra Oracle, cambiamos la implementación, y ni servicio y otros se enteran de ese cambio.

Y por último, veremos, más adelante en otro curso (Mocks con TDD), que podemos usar implementaciones de repositorios que no vayan contra la base, para alivianar nuestros tests del servicio.

Y todo, por haber programado contra la interfaz, y segregado responsabilidades.

Tenemos que avanzar en un sentido: no queremos tener que cambiar el código de creación del servicio, para que una vez use una implementación del repositorio (en memoria) y otra vez use una distinta (en base de datos). Quisiéramos que el cambiar esa relación NO IMPLIQUE tener que cambiar el código. Esa es la idea que subyace en lo que tenemos que definir en los próximos post de este tema: qué es Inversion of Control, qué es Dependency Injection, y contenedores de IoC.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, Jul 21 2010 11:20 by lopez | 3 comment(s)

Resultado de ALT.NET Hispano VAN sobre NoSQL

Gracias a la gente de la comunidad ALT.NET Hispano, pude participar de una VAN (des-conferencia) sobre NoSQL, hace ya unas semanas, el 22 de mayo pasado. Había escrito adelantando la presentación en:

ALT.NET Hispano VAN sobre NoSQL

Eso fue un sábado, y ya al lunes siguiente, el video de la VAN estuvo publicado, pueden verlo, junto con unas notas, en:

VAN NoSQL

En estas semanas que pasaron, estuve ocupado con mi mudanza, mi consolidación de libros, y sigo ordenando cajas y volúmenes. Y luego alguna gripe. Pero ya pude hacerme algo de tiempo para pasar en limpio el material que utilicé para la VAN.

La presentación la pueden bajar desde mi Skydrive NoSqlVan2010.pptx

Enlaces sobre NoSQL:

Un panorama de NoSQL en el artículo de la Wikipedia.
A brief history of NoSQL interesante que nombrara a Pick, como hice en la presentación.
Java development 2.0: NoSQL una intro de IBM
NoSQL Architecture
MyNoSQL Blog activo sobre NoSQL
NoSQL Databases – Part 1 – Landscape
NoSQL: scaling to size and scaling to complexity
http://nosql-database.org/
What is NoSQL?
The NoSQL alternative
NoSQL Summer, list of papers
BASE: an Acid alternative
Errors in database systems, eventual consistency and the CAP theorem
Scalable Datastores: comparison nosql, and some scalable RDBMS
NoSQL el movimiento en contra de las bases de datos (yo no lo veo en contra, es complementario)
Diff SQL NoSQL
Choosing a NoSQL data store according to your data set
John P. Wood NoSQL posts
Databases: relational vs object vs graph vs document
NoSQL: no necesitas ACID
NoSQL and SQL anti-patterns
Learning NoSQL from Twitters experience

Eventual Consistency:

http://www.allthingsdistributed.com/2008/12/eventually_consistent.html
http://queue.acm.org/detail.cfm?id=1466448
http://devblog.streamy.com/tag/eventual-consistency

Teorema CAP

http://www.julianbrowne.com/article/viewer/brewers-cap-theorem
http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf
There is no free lunch with distributed data

Enlaces sobre Google BigTable:

http://labs.google.com/papers/bigtable.html

Enlaces sobre Hypertable:

http://hypertable.org/
Hypertable NoSQL (pdf)

Enlaces sobre Amazon Dynamo:

Amazon Dynamo (original paper)

Enlaces sobre Cassandra:

http://cassandra.apache.org/
http://wiki.apache.org/cassandra/ArticlesAndPresentations
Cassandra by example (Twitter-like one)
http://github.com/ericflo/twissandra
WTF is a supercolumn Cassandra model
Cassandra NoSQL Database
Apache Cassandra
NoSQL Live Dynamo derivatives: Cassandra
http://github.com/suguru/cassandra-webconsole
Tutorial: Getting started with Cassandra
NoSQL in Twitter
Twitter, Facebook and Cassandra, and Open Source
Cassandra and Twitter: interview with Ryan King
(Twitter está reviendo si usa NoSQL o no)

Enlaces sobre Voldemort:

http://project-voldemort.com/
Voldemort design
The NoSQL that must not be named
Product project Voldemort Distributed Database

Enlaces sobre CouchDB:

http://couchdb.apache.org
CouchDB Introduction
CouchDB Overview

Enlaces sobre SimpleDB

http://awsdocs.s3.amazonaws.com/SDB/latest/sdb-gsg.pdf
http://awsdocs.s3.amazonaws.com/SDB/latest/sdb-dg.pdf

Enlaces sobre Redis

http://code.google.com/p/redis/
Twitter alike example
Add NoSQL Data Storage to your PHP development with Redis

Nombré en algún momento a Memcached:

http://memcached.org/

Sobre NoSQL tipo graph

InfoQ: Graph NoSQL Neo4j
Graph databases: A special case of document databases

Otros temas relacionados:

Scalability of the Hadoop distributed file system
MapReduce Hadoop algorithms in academic papers

Enlaces sobre MongoDB:

NoSQL with MongoDB, NoRM, and ASP.NET, part 1
NoSQL with MongoDB, NoRM, and ASP.NET, part 2
Master Slave in MongoDB
Choosing a non relational database: Why we migrated from MySQL to MongoDb
MongoHQ - The cloud-based hosted database solution for MongoDB.
http://github.com/azamsharp/SKOOL Ejemplo MongoDB, VS2010, ASP.NET MVC, TDD, BDD
On distributed consistency

El ejemplo que mostré de MongoDB (instalar, levantar el servidor, consumirlo desde .NET con un driver) siguió la línea explicada en el primer artículo de Ted Neward:

Going NoSQL with MongoDB
Going NoSQL with MongoDB (Part 2)
Going NoSQL with MongoDB (Part 3)

Código de ejemplo, ASP.NET MVC con MongoDB (apenas unas páginas) (ver de referenciar al driver que deje compilado en Libraries)

MongoDbMvcApplication1.zip

Para esos ejemplos tuve que descargar:

http://github.com/samus/mongodb-csharp
http://www.mongodb.org/display/DOCS/Downloads

Mis enlaces sobre los temas tratados:

http://delicious.com/ajlopez/mysql
http://delicious.com/ajlopez/mongodb
http://delicious.com/ajlopez/cassandra
http://delicious.com/ajlopez/couchdb

En especial, buscar presentaciones, tutoriales, ejemplos y “reviews”, como en:

http://delicious.com/ajlopez/mysql+tutorial
http://delicious.com/ajlopez/mysql+presentation
http://delicious.com/ajlopez/mysql+example
http://delicious.com/ajlopez/mysql+review

Nos leemos!

Angel "Java" Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Sat, Jul 17 2010 17:20 by lopez | 1 comment(s)

Armando una aplicación usando AjGenesis (Parte 4)

En este post, generaremos, desde el mismo modelo, archivos de texto para C#, Java, y VB.NET. Anteriores post:

Building An Application Using AjGenesis (Part 1)
Armando una Aplicación usando AjGenesis (Parte 1)
Building An Application Using AjGenesis (Part 2)
Armando una Aplicación usando AjGenesis (Parte 2)
Building an Application Using AjGenesis (Part 3)
Armando una Aplicación usando AjGenesis (Parte 3)

El código de este post puede bajarse desde AppExampleStep04.zip.

Necesitamos los binarios últimos de AjGenesis. Podemos bajarlo desde AjGenesisTrunkBinaries.zip. (el código fuente completo está en el repositorio de AjGenesis en Codeplex). Tienen que agregar el directorio bin al path, para ejecutar los ejemplos de este post.

El ejemplo ahora tiene más estructura:

Projects\AjApp es el folder conteniendo el modelo del ejemplo, en Project.xml:

<Project>
	<Name>AjApp</Name>
	<Description>Building an Application using AjGenesis</Description>
	<Model>
		<Entities>
			<Entity Source="Entities/Customer.xml"/>
			<Entity Source="Entities/Supplier.xml"/>
		</Entities>
	</Model>
</Project>

Podría, entonces, agregar más carpetas, con otros proyectos, si lo necesitara.

En el directorio inicial, tenemos tres comandos:

GenerateCSharp.cmd
GenerateJava.cmd
GenerateVbNet.cmd

Los tres comandos tienen un contenido similar. Por ejemplo, veamos el de GenerateCSharp.cmd:

AjGenesis.Console Projects\AjApp\Project.xml Projects\AjApp\Technologies\CSharp.xml Tasks\Complete.ajg Tasks\Generate.ajg

Notemos que hay dos modelos que se cargan en modelo: Project.xml es como en los anteriores posts, tiene el modelo independiente de la tecnología. El nuevo, CSharp.xml, describe la tecnología a usar:

<Technology>
	<Name>CSharp</Name>
</Technology>

Por ahora, contiene solo el nombre del lenguaje a usar. Podría extender este modelo, en próximos posts, para que contenga la base de datos a usar, el webserver a usar, etc…  Entonces: Project.xml es el modelo abstracto. Technologies\CSharp.xml, Technologies\VbNet.xml, Technologies\Java.xml son los modelos que describen la tecnología a usar. Cada uno de los comandos Generate*.cmd carga el modelo abstracto, Y uno de los tecnológicos.

La tarea Complete.ajg:

' Set Build Directory
if not Project.BuildDirectory then
	Project.BuildDirectory = "Build/${Project.Name}/${Technology.Name}"
end if
FileManager.CreateDirectory(Project.BuildDirectory)
IncludeCode "Tasks/Complete${Technology.Name}.ajg"

Noten el uso de un nuevo truco: incluir el código a ejecutar desde otro archivo, usando un string dinámico. Si Technology.Name == “CSharp”, la tarea de arriba termina ejecutando CompleteCSharp.ajg:

' Some functions
' Name to use for variables
function CSharpVariableName(name)
	firstletter = name.Substring(0,1)
	
	return firstletter.ToLower() & name.Substring(1)
end function
' Name to use for Classes, Properties..
function CSharpName(name)
	firstletter = name.Substring(0,1)
	
	return firstletter.ToUpper() & name.Substring(1)
end function
function CSharpType(type)
	type = type.ToLower()
	
	if type="text" then
		return "string"
	end if
	
	if type="integer" then
		return "int"
	end if
	
	return type
end function
' Set namespace to use in CSharp code
if not Project.CSharp.Namespace then
	Project.CSharp.Namespace = CSharpName(Project.Name)
end if
' Complete Entities
for each Entity in Project.Model.Entities
	' Set the variable name to use for an entity
	if not Entity.CSharp.VariableName then
		Entity.CSharp.VariableName = CSharpVariableName(Entity.Name)
	end if
	
	for each Property in Entity.Properties
		' Set the CSharp to use in each property
		if not Property.CSharp.Type then
			Property.CSharp.Type = CSharpType(Property.Type)
		end if
	end for
end for

Hay tareas escritas similares a la de arriba, para otras tecnologías: CompleteVbNet.ajg, CompleteJava.ajg. Estas tareas completan el modelo en memoria (asignando namespaces, packages, nombres para usar en las variables y propiedaes, el directorio a usar para dejar lo generado, etc…)

La segunda tarea es Generate.ajg:

IncludeCode "Tasks/Generate${Technology.Name}.ajg"

De nuevo, el viejo truco de un include de código dinámico. Esta es la subtarea GenerateCSharp.ajg:

for each Entity in Project.Model.Entities
	TransformerManager.Transform("Templates/CSharp/EntityClass.tpl", "${Project.BuildDirectory}/${Entity.Name}.cs", Environment)
end for

Compare, con CompleteJava.ajg:

' Some functions
' Name to use for variables
function JavaVariableName(name)
	firstletter = name.Substring(0,1)
	
	return firstletter.ToLower() & name.Substring(1)
end function
' Name to use for Classes, Properties..
function JavaName(name)
	firstletter = name.Substring(0,1)
	
	return firstletter.ToUpper() & name.Substring(1)
end function
function JavaType(type)
	type = type.ToLower()
	
	if type="text" then
		return "String"
	end if
	
	if type="integer" then
		return "int"
	end if
	
	return type
end function
' Set package to use in Java code
if not Project.Java.Package then
	Project.Java.Package = JavaName(Project.Name).ToLower()
end if
' Complete Entities
for each Entity in Project.Model.Entities
	' Set the variable name to use for an entity
	if not Entity.Java.VariableName then
		Entity.Java.VariableName = JavaVariableName(Entity.Name)
	end if
	
	for each Property in Entity.Properties
		if not Property.Java.VariableName then
			Property.Java.VariableName = JavaVariableName(Property.Name)
		end if
		
		' Set the Java to use in each property
		if not Property.Java.Type then
			Property.Java.Type = JavaType(Property.Type)
		end if
	end for
end for

y el GenerateJava.ajg:

for each Entity in Project.Model.Entities
	TransformerManager.Transform("Templates/Java/EntityClass.tpl", "${Project.BuildDirectory}/${Entity.Name}.java", Environment)
end for

Hay templates para cada tecnología. Ejemplo, este es el EntityClass.tpl para CSharp:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
namespace ${Project.CSharp.Namespace} {
	public class ${Entity.Name}
	{
<#
	for each Property in Entity.Properties
#>
		public ${Property.CSharp.Type} ${Property.Name} { get; set; }
<#
	end for
#>
	}
}

y éste es el de Java EntityClass.tpl:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
package ${Project.Java.Package};
public class ${Entity.Name}
{
<#
	for each Property in Entity.Properties
#>
		private ${Property.Java.Type} ${Property.Java.VariableName};
<#
	end for
	for each Property in Entity.Properties
#>
		public ${Property.Java.Type} get${Property.Name}()
		{
			return this.${Property.Java.VariableName};
		}
		public void set${Property.Name}(${Property.Java.Type} value)
		{
			this.${Property.Java.VariableName} = value;
		}
<#
	end for
#>
}

Ejecutando los tres comandos Generate*.cmds, se crea el directorio Build, con Build\AjApp, Build\AjApp\CSharp, Build\AjApp\Java, Build\AjApp\VbNet, un directorio por el proyecto, con subdirectorios por tecnología.

Veamos el Customer.cs generado:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
namespace AjApp {
	public class Customer
	{
		public string Name { get; set; }
		public string Address { get; set; }
	}
}

El Customer.vb generado:

' Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
Namespace AjApp
	Public Class Customer
		Private mName as String
		Private mAddress as String
		Public Property Name as String
			Get
				return Me.mName
			End Get
			Set(value as String)
				Me.mName = value
			End Value
		End Property		
		Public Property Address as String
			Get
				return Me.mAddress
			End Get
			Set(value as String)
				Me.mAddress = value
			End Value
		End Property		
	End Class
End Namespace

Y el Customer.java generado:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
package ajapp;
public class Customer
{
		private String name;
		private String address;
		public String getName()
		{
			return this.name;
		}
		public void setName(String value)
		{
			this.name = value;
		}
		public String getAddress()
		{
			return this.address;
		}
		public void setAddress(String value)
		{
			this.address = value;
		}
}

Próximos pasos: generar proyectos C# o VB.NET, listos para cargar en Visual Studio, o un proyecto Eclipse para Java.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Fri, Jul 16 2010 10:34 by lopez | with no comments

ALT.NET Hispano VANs sobre Implementation Patterns, y OData

La gente de la comunidad ALT.NET Hispano tiene programadas dos VAN (des-conferencias virtuales), una para mañana viernes, a cargo del bueno de Martín Salías (@MartinSalias), sobre el tema Implementacion Patterns, a las 18 GMT, (15hs acá en Buenos Aires). Esta es la descripción que dejó Martín (tomado de un mensaje enviado por del bueno de @JorgeGamba):

Los programadores queremos ser independientes, pero nos la pasamos buscando patrones... :)

A mediados de los '90, la publicación del libro seminal "Design Patterns" del Gang of Four instituyó la idea de tabular ciertos elementos conocidos y repetitivos en las implementaciones de software orientado a objetos, generando un alto nivel de impacto en un catálogo de patrones que hoy día han sido incorporados en gran parte a las plataformas y lenguajes más modernos, pero siguen siendo útiles como herramienta de comunicación.

Por la misma época, Uncle Bob Martin publicaba en el C++ Report (que luego se llamaría Journal of OOP) una serie de artículos sobre principios generales de orientación a objetos, que recopilaría en el 2002 en su clásico "Agile Software Development: Principles, Patterns and Practices": los principios SOLID que hoy conocemos, y que en gran parte podemos ver como los fundamentos de los patrones del GoF.

Centrándose en la premisa de mejorar la expresividad de nuestro código, Kent Beck, el principal ideólogo de XP, publicó recientemente su libro "Implementation Patterns", en el que baja a un nivel de detalle aún más bajo en cuanto a la manera en que escribimos nuestro código OOP: cómo creamos clases, cómo mantenemos estado, modelamos el comportamiento, escribimos y componemos métodos y manejamos colecciones.

Beck escribe el libro pensando y escribiendo código Java, pero al leerlo tenemos varias revelaciones. Por ejemplo, al recorrer estructuras muy marcadas de ese lenguaje, podemos reflexionar sobre otros. En principio, creo que las 160 páginas del libro podrían comprimirse a unas pocas docenas en Ruby, pero también nos permite reflexionar sobre C#, un lenguaje más cercano al corazón de Alt.Net (aunque podemos repasar el mismo tema en Ruby en alguna otra ocasión).

El mecanismo de esta VAN será entonces recorrer (en forma más abreviada) estos mismos elementos de código desde la perspectiva de C# (y en algunos casos, la BCL).

Algunos temas que podemos llegar a tocar son:

- Clases, super clases simples y calificadas, abstracciones e interfaces
- Estado, acceso directo e indirecto, estado común, variable y extrínseco
- Variables, campos, parámetros y constantes
- Control de flujo, mensajes, excepciones
- Métodos, visibilidad, sobreescritura y sobrecarga, helpers, conversiones, constructores, factories, queries, etc
- Arrays, Iteradores, Colecciones, Listas, etc

Obviamente son muchos temas por cubrir, así que mi objetivo será dejar planteados los temas para que podamos seguir discutiéndolos posteriormente en la lista o en los grupos de estudio.
Saludos a todos,

La otra VAN es el sábado, de nuevo 18GMT (15hs Buenos Aires), a cargo de Pablo Castro, encargado de OData, el tema de la VAN. Descripción enviada por Pablo (que desde hace años partió de Buenos Aires a Redmond):

"... asumo que vamos a hacer algo flexible y si la gente quiere cambiar las cosas en el momento no hay problema.

OData (Open Data Protocol) es un protocolo para compartir datos diseñado para funcionar sobre la Web. En esta VAN vamos a discutir como OData es consistente con la forma en la que funciona la Web, utilizando URLs para identificar cada pieza de información en un servicio, HTTP para transportar requerimientos y respuestas, y AtomPub y JSON para manejar colecciones y para representaciones de datos. También vamos a ver varias aplicaciones que pueden producir y consumir OData, desde aplicaciones para usuarios finales hasta entornos de desarrollo, y discutiremos como crear servidores y clientes usando la librerías para OData incluidas en .NET, conocidas como WCF Data Services (o ADO.NET Data Services en la versión anterior, y “Astoria” antes de eso).

Durante la presentación vamos a usar varios ejemplos prácticos. A medida que cubrimos el tema podemos ajustar la dirección y contenido en base a lo que les interese más a los que estén presentes. Para aquellos que ya tengan experiencia con OData, siéntanse libres de traer todas las preguntas y sugerencias que tengan.

Para más información sobre OData pueden visitar http://odata.org."

Recuerden cualquier puede entrar, via la URL:http://snipr.com/virtualaltnet

Como siempre, recuerdo al bueno de @jorgegamba:

Hay que aclarar que no se requiere ningún tipo de registro, simplemente acudir el día y la hora indicados a la dirección Web http://snipr.com/virtualaltnet, eso sí, deberán tener instalado el programa cliente de Live Meeting; hay más instrucciones sobre cómo hacer esto y otras indicaciones en la página wiki Descripción de Reuniones.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Thu, Jul 15 2010 19:23 by lopez | with no comments

Introducción a IoC y DI: Hello, world, flexible

Dentro del Proyecto Hogwarts, estamos produciendo material para un curso de Inversion of Control y Dependency Injection, que quedará en línea (además de preparar alguna charla o taller presencial). No voy a explicar todavía los términos, sino que quisiera seguir otro camino: tomando la aplicación más simple, ir viendo de flexibilizarla, hasta llegar en algún momento a usar IoC y DI (siglas de los términos en inglés para Inversión de Control e Inyección de Dependencias).

¿Cuál es la aplicación más simple? Tomemos un clásico “Hello, world” en C# (espero publicar más adelante una serie de versiones en Java, que culmine con el uso de Spring Framework, como hago en mis cursos sobre el tema):

 class Program
 {
 static void Main(string[] args)
 {
 Console.WriteLine("Hello, world");
 }
 }

¿Qué podemos querer cambiar de esa aplicación? Se me ocurren dos cosas:

- El mensaje

- La forma de procesarlo

Son dos responsabilidades, que podemos asignarlas a dos clases nuevas, digamos, MessageProvider:

 public class MessageProvider
 {
 public string Message { get; set; }
 }

y a MessageProcessor:

 public class MessageProcessor
 {
 public MessageProvider Provider { get; set; }
 public void Process()
 {
 Console.WriteLine(this.Provider.Message);
 }
 }

Nuestro Hello World puede quedar ahora de esta forma:

 static void Main(string[] args)
 {
 MessageProvider provider = new MessageProvider() { Message = "Hello, World" };
 MessageProcessor processor = new MessageProcessor() { Provider = provider };
 processor.Process();
 }

Vean que creamos los dos objetos de dos clases concretas, y los “conectamos”: como el MessageProcessor necesita de un MessageProvider, se lo proveemos mediante una propiedad (podríamos haberlo hecho mediante un parámetro en el constructor). Lo que estamos haciendo es construir un grafo de objetos, y dada la forma en que colaboran entre ellos, el grafo lo armamos nosotros. La alternativa sería: DENTRO de MessageProcessor crear un MessageProvider y usarlo. Pero eso dejaría acoplada la responsabilidad de cuál mensaje procesar al propio MessageProcessor. De la forma adoptada arriba, si queremos cambiar el mensaje, NO ALTERAMOS el código de MessageProcessor. A este objeto “le llueve del cielo” su colaborador. Vemos la aparición de un camino a seguir: los “new” de los objetos colaboradores, no están en el código de los consumidores de esos colaboradores. Más adelante, pasaremos a ejemplos más de la vida real. Pero por ahora, exploremos estos temas con ejemplo sencillo. Luego, el problema (armar el grafo de objetos) y la solución (lo armamos “por fuera” de los objetos) será la misma, con variantes, en ejemplos medios y complejos.

Próximo paso: ahora que tenemos objetos que se hacen cargo de las responsabilidades que descubrimos, podemos refinar el ejemplo: ahora, MessageProcessor está acoplado a una clase concreta MessageProvider. Pero ¿qué necesita realmente MessageProcessor? En lugar de consumir un objeto de una clase concreta, ahora que tenemos un mini caso de uso codificado, podemos extraer, “descubrir” la interface que necesita consumir MessageProcessor. Y ya que estamos, también extraer, descubrir la interface, la conducta expuesta de cualquier MessageProcessor que se nos ocurra mañana. Pueden hacerlo a mano, o apelar a las capacidades de Refactoring, Extract Interface de Visual Studio. Tenemos IMessageProvider:

 public interface IMessageProvider
 {
 string Message { get; set; }
 }

IMessageProcessor:

 public interface IMessageProcessor
 {
 void Process();
 IMessageProvider Provider { get; set; }
 }

Y nuestras clases ahora implementan y esperan esas interfaces:

 public class MessageProvider : HelloWorldInterfacesExample.IMessageProvider
 {
 public string Message { get; set; }
 }
 public class MessageProcessor : HelloWorldInterfacesExample.IMessageProcessor
 {
 public IMessageProvider Provider { get; set; }
 public void Process()
 {
 Console.WriteLine(this.Provider.Message);
 }
 }

Vemos que MessageProcessor es UNA implementación de IMessageProcessor, y que no espera un MessageProvider, sino que se las arregla con cualquier implementación, actual o futura, de IMessageProvider. Como antes, él no se preocupa de crear esa instancia ayudante, sino que alguien proveerá.

Nuestra invocación queda:

 static void Main(string[] args)
 {
 IMessageProvider provider = new MessageProvider() { Message = "Hello, World" };
 IMessageProcessor processor = new MessageProcessor() { Provider = provider };
 processor.Process();
 }

Por ahora, no parece que ganemos mucho (para este ejemplo simple). Si tenemos que cambiar el proveedor del mensaje, el mensaje, o la forma de procesarlo (por ejemplo, que genere, en lugar de un mensaje en pantalla, otra cosa, como un archivo PDF o una página web), tenemos que tocar el código de nuestra rutina de inicio. Exploraremos cómo, ayudados por algún framework, conseguir que ese armado y esos datos se deleguen a configuración.

Código de los ejemplos en HelloWorldObjectsExample.zip y HelloWorldInterfacesExample.zip

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, Jul 13 2010 23:29 by lopez | 5 comment(s)

Material de Introducción a Ajax y Javascript

El viernes 25 de Junio tuve el gusto de dar una charla en la Facultad de Ciencias Exactas, de la Universidad de Buenos Aires, invitado por el bueno de @jgabardini (blog) y su gente. Había anunciado el evento en:

Mi próxima charla: Ajax y Javascript

Llegó la hora de publicar el material:

Presentación de la charla: AjaxJavascript201001.pptx

Ejemplos de Javascript simple, Ajax simple, sitio con PHP y Ajax simple: Ajax2010.zip

Ejemplo de ASP.NET MVC con JQuery simple: AspNetMvc1Jquery.zip.

Vimos hacia el final la librería JQuery:

http://jquery.com

Vimos ejemplos de JQuery UI:

http://jqueryui.com/

La gente de que organizó la charla, publicará el video, así que van a tener más contexto para seguir los ejemplos desde la presentación y los enlaces mencionados. Tendrá que aparecer en el blog: http://www.exactas.uba.ar/uti/

La mayor parte de los otros temas, fueron mencionados en otras charlas anteriores. Ver mis posts anteriores sobre Ajax. El post más informativo es: Aprendiendo Ajax. Rescato de ahí enlaces como:

Sitios sobre AJAX

http://ajaxian.com/
http://www.ajaxlines.com
http://www.ajaxtutorial.net/
http://www.ajaxprojects.com/
http://www.ajaxresources.com/
http://blog.joshuaeichorn.com/ajax-resources/

Tecnologías asociadas a AJAX

JSON
http://www.json.org/
Standard ECMA JavaScript
http://www.ecma-international.org/publications/files/ecma-st/ECMA-262.pdf
The XMLHttpRequest Object
http://www.w3.org/TR/XMLHttpRequest/
http://www.fiftyfoureleven.com/resources/programming/xmlhttprequest/
http://www.fiftyfoureleven.com/resources/programming/xmlhttprequest/examples

Mis enlaces actualizados sobre los temas tratados:

http://delicious.com/ajlopez/ajax
http://delicious.com/ajlopez/javascript
http://delicious.com/ajlopez/jquery

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Mon, Jul 12 2010 20:15 by lopez | with no comments

Filed under: , , ,

Armando una aplicación usando AjGenesis (Parte 3)

Tercera parte de esta serie de posts tutoriales, sobre cómo armar una aplicación usando AjGenesis, mi proyecto open source de generación de código. Anteriores posts:

Building An Application Using AjGenesis (Part 1)
Armando una Aplicación usando AjGenesis (Parte 1)
Building An Application Using AjGenesis (Part 2)
Armando una Aplicación usando AjGenesis (Parte 2)

El código de este post puede bajarse de from AppExampleStep03.zip.

Se necesitan los últimos binarios de AjGenesis. Los pueden bajar desde AjGenesisTrunkBinaries.zip. (el código fuente estáa en el repositorio de AjGenesis en Codeplex). Hay que agregar el directorio bin al path, para ejecutar los ejemplos de este post.

Esta vez, mejoré el código del proceso de generación. El comando GenerateClasses.cmd:

AjGenesis.Console Project.xml CompleteModelCSharp.ajg GenerateClasses.ajg

tienen un nuevo parámetro: una tarea intermedia a ejecutar, CompleteModelCSharp.ajg:

' Some functions
' Name to use for variables
function CSharpVariableName(name)
  firstletter = name.Substring(0,1)
  
  return firstletter.ToLower() & name.Substring(1)
end function
' Name to use for Classes, Properties..
function CSharpName(name)
  firstletter = name.Substring(0,1)
  
  return firstletter.ToUpper() & name.Substring(1)
end function
function CSharpType(type)
  type = type.ToLower()
  
  if type="text" then
    return "string"
  end if
  
  if type="integer" then
    return "int"
  end if
  
  return type
end function
' Set namespace to use in CSharp code
if not Project.CSharp.Namespace then
  Project.CSharp.Namespace = CSharpName(Project.Name)
end if
' Complete Entities
for each Entity in Project.Model.Entities
  ' Set the variable name to use for an entity
  if not Entity.CSharp.VariableName then
    Entity.CSharp.VariableName = CSharpVariableName(Entity.Name)
  end if
  
  for each Property in Entity.Properties
    ' Set the CSharp to use in each property
    if not Property.CSharp.Type then
      Property.CSharp.Type = CSharpType(Property.Type)
    end if
  end for
end for

Lo nuevo: definición de funciones, el uso de métodos .NET como .ToUpper(), y completar el modelo, como en:

if not Project.CSharp.Namespace then
  Project.CSharp.Namespace = CSharpName(Project.Name)
end if

Estoy agregando propiedades al modelo en memoria. Si no hemos especificado el namespace a usar, se crea uno. Noten que si Project.CSharp.Namespace no est’a definido, y Project.CSharp no existe, está en nulo, no aparece exception: un indefinido O.A.B. se evalúa a null, nothing, y eso se toma como falso en AjBasic (he tomado esa estrategia de PHP).

Entonces, el código de arriba enriquece el modelo a usar, tomando decisiones relacionadas, en este caso, con C#. En próximos post, escribiré código similar para otras tecnologías. Pero vale la pena mencionar que de esta manera podemos escribir un modelo independiente de la tecnología, y luego, cuando la tecnología se elija (en este caso, el lenguaje de programación, pero podría ser también la base de datos, o la tecnología de presentación), podemos enriquecer el modelo con la información más técnica.

Este es el fragmento que en el código de arriba agregar un tipo C# a una propiedad, basado en su tipo abstracto:

  for each Property in Entity.Properties
    ' Set the CSharp to use in each property
    if not Property.CSharp.Type then
      Property.CSharp.Type = CSharpType(Property.Type)
    end if
  end for

La segunda tarea a ejecutar en el comando mencionado, es la misma que en el anterior post, GenerateClasses.ajg:

for each Entity in Project.Model.Entities
  TransformerManager.Transform("EntityClass.tpl", "${Entity.Name}.cs", Environment)
end for

El template EntityClass.tpl:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
namespace ${Project.CSharp.Namespace} {
  public class ${Entity.Name}
  {
<#
  for each Property in Entity.Properties
#>
    public ${Property.CSharp.Type} ${Property.Name} { get; set; }
<#
  end for
#>
  }
}

ahora usa las nuevas propiedades del modelo.

Este es el Customer.cs generado:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
namespace AjApp {
public class Customer
  {
    public string Name { get; set; }
    public string Address { get; set; }
  }
}

Próximos pasos: escribir más tareas y templates para generar, desde el mismo modelo, código Java y VB.NET.

Como en los anteriores posts de la series, les recuerdo que ésta es una serie tutorial. En el desarrollo real, prefiero adoptar otra manera de armar la generación de código, que describí en los posts:

AjGenetizing An Application
AjGenetizando una Aplicación

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Mon, Jul 12 2010 11:50 by lopez | 1 comment(s)

Armando una aplicación usando AjGenesis (Parte 2)

En este segundo post de la serie explicando cómo armar una aplicación usando AjGenesis, mi proyecto open source de generación de código. El anterior post de la serie:

Building An Application Using AjGenesis (Part 1)
Armando una aplicación usando AjGenesis (Parte 1)

En esta segunda parte, generaré dos archivos sencillos, a partir del modelo definido en el anterior post.

Los archivos para este segundo paso pueden ser bajados desde AppExampleStep02.zip.

Necesitaran bajarse los binarios de AjGenesis (la versión del trunk) desde AjGenesisTrunkBinaries.zip, expandirlos, Y agregar el directorio bin a su path.

Hay un comando PrintEntities.cmd:

AjGenesis.Console Project.xml PrintEntities.ajg

El programa AjGenesis.Console (parte de los binarios de AjGenesis) es un programa de consola que lanza el proceso del generador. Puede recibir varios argumentos. Si el argumento es un archivo .xml su contenido se toma como modelo en memoria (más abajo veremos cómo se usa entonces=. Si el argumento es un archivo .ajg, su contenido se toma como una tarea a ejecutar, escrita en un lenguaje interpretado, llamado afectuosamente AjBasic..

Este es el contenido de Project.xml (lo había definido en el anterior post, más detalles ahí):

<Project>
  <Name>AjApp</Name>
  <Description>Building an Application using AjGenesis</Description>
  <Model>
    <Entities>
      <Entity Source="Customer.xml"/>
      <Entity Source="Supplier.xml"/>
    </Entities>
  </Model>
</Project>

Una vez que el modelo de este archivo es cargado en memoria, tenemos a nuestra disposición una variable pública Project, con propiedades dinámicas, una por cada atributo o elemento inmediato. Desde AjBasic podemos acceder, entonces, a Project, Project.Model, Project.Name, Project.Model.Entities...

El contenido de PrintEntities.ajg:

for each Entity in Project.Model.Entities
  Message "Entity " & Entity.Name
  
  for each Property in Entity.Properties
    Message "   Property ${Property.Name}"
  end for
end for

Noten el uso de for each, que es un comando AjBasic. Message es otro comando, que imprime un mensaje en el standard output. El código de arriba también muestra una de las características de AjBasic: la “expansión de String” (inspirada en PHP y otros). Esto es, el string:

“ Property ${Property.Name}”

se expande en ejecución al contenido de la expresión entre ${ y }. Si ejecutamos

PrintEntities.cmd

la salida es:

Entity Customer
   Property Name
   Property Address
Entity Supplier
   Property Name
   Property Address

Ahora, generamos un archivo con el texto de una clase en C#, por cada una de las entidades en el modelo. Usamos el comando GenerateClasses.cmd que consiste de:

AjGenesis.Console Project.xml GenerateClasses.ajg

Este comando carga en memoria el contenido de Project.xml como modelo listo para ser accedido desde la variable Project en AjBasic, y ejecuta la tarea:

for each Entity in Project.Model.Entities
  TransformerManager.Transform("EntityClass.tpl", "${Entity.Name}.cs", Environment)
end for

Hay algunos pocos objetos ayudantes, predefinidos en el ambiente que AjGenesis le provee al intérprete de AjBasic. Uno de los más usados es el  TransformerManager, cuyo método Transform toma tres argumentos (ver el código fuente del proyecto AjGenesis para más detalle de TransformerManager y otros objetos auxiliares):

- El nombre del archivo template
- El nombre del archivo a generar
- El Environment actual (nombre/valor de las variables globales y funciones, usualmente apuntados por la variable global Environment)

Este es el primer template en esta serie de tutoriales, EntityClass.tpl:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
public class ${Entity.Name}
{
<#
  for each Property in Entity.Properties
#>
  public string ${Property.Name} { get; set; }
<#
  end for
#>
}

Un template (plantilla) es un archivo de texto con el contenido a generar, y con fragmentos de AjBasic, contenidos entre <# y #>. En el ejemplo de arriba, el comando for each recorre cada una de las propiedades de la entidad bajo proceso. En el texto mismo de un template también hay expansión de string, así que las expresiones entre ${ y } son evaluadas en el momento y su resultado se inserta en lugar de la expresión.

Queda entonces esta salida generado, ejemplo Customer.cs usando el EntityClass.tpl de arriba:

// Entity Class, generated with AjGenesis (http://ajgenesis.codeplex.com)
public class Customer
{
  public string Name { get; set; }
  public string Address { get; set; }
}

He dejado algunas simplificaciones: no hay namespace, no hay id para las entidades, todas las propiedades se supone strings, etc… Extenderé el ejemplo en próxumos pasos para resolver esos temas. También hay que encarar: generar, desde el mismo modelo, clases VB.NET, C# o Java, a pedido. Y en algún momento, generar un proyecto completo. Más adelante, agregar capa de presentación, de persistencia, creación de la base, etc, en distintas tecnologías. Pero vayamos paso a paso

(Ya comenté que el otro camino es AjGenetizando una aplicación: partir de una aplícación ya armada, y comenzar a abstraer desde ahí, y hasta iterar: generar manual, abstraer, poner en generación automática, mantener lo manual, etc…. Pero creo que esta serie de tutorial es más fácil de entender, para el que reciíen llega a este proyecto).

Nos leemos!

Angel “Java” Lopez ie
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Sat, Jul 10 2010 16:58 by lopez | 3 comment(s)

Armando una aplicación usando AjGenesis (Parte 1)

Este es la primera parte de un tutorial acerca de cómo usar AjGenesis, mi generador de código, open source, para generar una aplicación. La solución que quiero generar será una aplicación .NET en C#, pero en el camino agregaré ejemplos en VB.NET, PHP y Java, para que sirvan de “prueba de concepto” del poder de partir de un modelo abstracto, independiente lo más posible de la tecnología a usar.

Este es el primer paso que encaro hoy: escribir un modelo en archivos XML (podría escribirlos también en texto plano: la versión del trunk de AjGenesis soporta ambos tipos de serialización de los modelos).

Recuerden: el modelo a usar es libre, no sigue un esquema. La idea es que el modelo vaya emergiendo de las necesidades de cada uno, no de lo que yo escriba en este tutorial. Iremos refinando el modelo, paso a paso, enriqueciéndolo, haciéndolo más expresivo, tratando de separar la definición de la implementación. La serie de estos posts mostrará las decisiones que voy tomando. Quiero terminar modelando una aplicación empresarial: algo que contenga clientes, proveedores, facturas, pagos, etc. Comencemos por Customer (Cliente) y Supplier (Proveedor).

(Pueden bajar el código de este post desde AppExampleStep01.zip, son solamente 3 archivos XML)

Este es el contenido del archivo Customer.xml:

<Entity>
	<Name>Customer</Name>
	
	<Properties>
		<Property>
			<Name>Name</Name>
			<Type>Text</Type>
		</Property>
		<Property>
			<Name>Address</Name>
			<Type>Text</Type>
		</Property>
	</Properties>
</Entity>

Hay un archivo con contenido parecido en Supplier.xml. Noten que no hay un esquema a seguir: podemos agregar cualquier tag o atributo mientras que el resultado sea un documento XML bien formado. Iremos aprendiendo algunas restricciones mínimas que pide AjGenesis. Por ejemplo, reconoce que <Properties> es una lista de <Property> así que no aceptará fácilmente que pongamos algo distinto dentro de esa lista. Ahora, cada <Property> puede tener los atributos y elementos hijos que querramos. Podemos tener un elemento simple como <Name> o un elemento compuesto como <Property>. También podría haber escrito Name como atributo, tipo <Entity Name=”Customer”…. Vamos a ver que para AjGenesis, esto es lo mismo: al cargarlo en memory, el nombre, en el modelo, quedará referenciado como Project.Name, independientemente de cómo estaba escrito en el archivo original.

Uno de los principios que guiaron mis decisiones de diseño: el modelo serializado (XML, archivo de text, otros…) no debería hacerme doler los ojos (vieron esos archivos XML que son inentendibles de tanto namespaces y schemas?) Así, que tomé una convención para permitir que no todo el modelo tenga que estar en un solo archivo. Vean el Project.xml:

<Project>
	<Name>AjApp</Name>
	<Description>Building an Application using AjGenesis</Description>
	<Model>
		<Entities>
			<Entity Source="Customer.xml"/>
			<Entity Source="Supplier.xml"/>
		</Entities>
	</Model>
</Project>

 

El atributo Source es uno distinguido, tratado especialmente por AjGenesis: el contenido de ese atributo apunta a un archivo, relativo al archivo padre, que se carga en el modelo como si hubiera estado directamente ahí, en la rama donde aparece el atributo Source. El modelo en memoria, veremos, será un solo grafo de objetos, aunque serializado estuviera ocupando más de un archivo.

Próximos pasos, en siguientes post: cómo usar este modelo de AjGenesis para generar algunos archivos C#. Luego, generaremos Java, VB.NET, y algún proyecto más completo.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Thu, Jul 8 2010 13:10 by lopez | 4 comment(s)

Tales from the Scrum: Comunicación en el equipo y más allá

Hace ya un tiempo que no escribo sobre Scrum: los últimos meses me los pasé ocupado en mi proyecto personal de mudanza y consolidación de libros. Tengo varios posts “in pectore” sobre el tema de Scrum y desarrollo ágil, pero hoy quisiera volver sobre un tema, que me parece importante.

Como saben, en Scrum hay un equipo, y se hacen reuniones diarias (además de las reuniones de iteraciones y de retrospectivas). Pero no sólo hay equipo en Scrum. También hay otros actores: "stakeholders”, interesados en el proyecto, gente del cliente final, gente de apoyo técnico de nuestro entorno, etc. Recordemos también al Product Owner, que es nuestro principal punto de contacto con el cliente, y que es el encargado de priorizar el Product Backlog, por valor de negocio (y no por otro criterio).

Pero una cosa que no está escrito en los rituales de Scrum, es el uso de herramientas: uno no está obligado a usar tal o tal herramienta. Podemos llevar todo Scrum con lápiz, papel y reuniones físicas. Sin embargo, en los últimos cuatro años de experiencia en proyectos ágiles, he visto una herramienta que ya a esta altura, me parece indispensable: la lista de correo.

Algo ya escribí cómo suelo usar la lista de correo del proyecto en:

Tales from the Scrum: Informando el estado

Pero he aprendido que la lista de correo permite también:

- Mantener el sincronismo con el proyecto, aunque uno como miembro, se ausente un día o dos.

- Mantener informado a otros interesados de lo que se está haciendo, de los bloqueos, de los avances, de los posibles caminos que se están explorando para resolver un tema.

- Permite que interesados que no están en el equipo, puedan mantenerse al tanto del estado. Puede que algunos estén físicamente lejos.

- Si bien todo esto se puede hacer con otras herramientas (agregando documentos a un repositorio, escribiendo en páginas de un wiki), la lista de correo tiene una forma de llevar información, digamos, “intravenosa”: me llegan los emails, sin necesidad de ir a otra herramienta.

Casos que he visto donde participar en la lista de correo fue provechosa:

- Una vez, estábamos desarrollando una aplicación web, y teníamos que implementar seguridad sobre un web service. Hicimos las reuniones de standup, teníamos las tareas, habíamos consultado algunos temas técnicos. Pero gracias a que publicamos lo que íbamos a hacer en la lista de correo, uno de los suscriptos, especialista técnico en el tema, supo darnos otra orientación que se nos había pasado por alto. Esa persona, como especialista, estaba muy ocupada usualmente, y no podía estar al tanto de todas las reuniones de varios equipos. Pero gracias a las lista de correo, podía ponerse al tanto del estado de los proyectos.

- Lo mismo pasó en un caso no de programación. Ante una dificultad en el “deployment” de una aplicación, que no sabíamos cómo solucionar, alguien interesado del cliente, en el área de infraestructura, nos supo dar, al leer de nuestro problema en la lista de correo, una recomendación que sirvió para seguir adelante.

- El propio Product Owner se mantiene al tanto de lo que va pasando por la iteración, sin necesidad de estar asistiendo a reuniones en el medio, y sin tener que esperar al fin de la iteración para tener idea del estado.

- La lista de correo permite que gente de dirección, por ejemplo, de la consultora que está llevando a cabo el proyecto, sepa si hay un bloqueo o algún problema: recordar el tema: “bad news, travel fast”. La lista de correo se transforma entonces en una especie de “instrumento de medición” de la salud del equipo.

- Una vez estuve enfermo unos días (sí, ya estoy viejito… :-) y la lista de correo permitió de alguna forma que me mantuviera al tanto, de lo que iba pasando.

- No en todas las reuniones (de diseño, relevamiento, pruebas, demostraciones, …) asiste todo el equipo. Entonces, algún asistente a la reunión, prepara una minuta y la envía a la lista de correo. Esto permite que los lectores de la lista, se informen de eventos a los que no asistieron.

También he notado que el informar los detalles por escrito, permite que quede más claro que simplemente informar rápidamente en una conversación, donde puede que no estén todos presentes, o no quede claro lo que uno dijo, o simplemente se olvide lo que se comentó. De alguna forma, lo escrito sobrevive más a lo simplemente conversado.

Nos leemos!

Angel "Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, Jul 7 2010 14:51 by lopez | with no comments

TDD: Primeros pasos en Visual Studio

He estado escribiendo algunos posts sobre TDD (Test-Driven Development) relacionados con el curso en preparación del Proyecto Hogwarts. La idea es publicar el material del curso presencial que se está armando en ese proyecto, para que quede en línea en un sitio público. Mientras tanto, sigo adelantando algunos temas por acá.

Recordemos el primer ejemplo, en video:

Un ejemplo de TDD

Quisiera repasar unos simples puntos, en este post. Primero, en ese ejemplo trabajamos con Visual Studio 2008, y con las facilidades de test que tiene ese entorno en una versión Architect. Deberíamos explorar en algún momento de escribir test con otras librerías, y en otros entornos, lenguajes (como Java/Eclipse). En el ejemplo presentado (ver el post, el video, y ahí el código para bajar), se armaron dos proyectos:

 

Uno es un proyecto de librería de clases, y el segundo es un proyecto de test:

 

(Recordemos: no todas las versiones del Visual Studio soportan este tipo de proyecto. Está disponible a partir de la versión Architect. Tendremos que estudiar alternativas de código abierto, como NUnit, que varían en los detalles pero con conceptos similares).

El código que van a ver que se terminó generando en el ejemplo, es:

using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Calculator.Tests
{
 [TestClass]
 public class CalculatorTests
 {
 [TestMethod]
 public void AddTwoNumbers()
 {
 Calculator calculator = new Calculator();
 int result = calculator.Add(2, 3);
 Assert.AreEqual(5, result);
 }
 [TestMethod]
 public void AddManyNumbers()
 {
 Calculator calculator = new Calculator();
 for (int k = 1; k <= 10; k++)
 {
 int j = k + 1;
 int result = calculator.Add(k, j);
 Assert.AreEqual(k + j, result);
 }
 }
 }
}

¿Qué hay para destacar de este código? Primero, el uso de atributos como [TestClass] y [TestMethod]. Estos son los atributos iniciales que estamos utilizando. Tendremos que estudiar otros, y cómo hay atributos similares en otras librerías, como NUnit. Pero ¿qué son estos atributos? Son la forma que tiene este entorno de implementar dos soluciones de frameworks de TDD, a dos problemas: ¿cómo identificar un test? ¿cómo agruparlos?

Lo que usa Visual Studio (y NUnit y otros), es hacer que cada test esté implementado como un método. No es un programa, es un método. Y para ubicar el método y distinguirlo de otros, usamos el atribute [TestMethod].

También este entorno tiene la facilidad de ejecutar un método, o todos los métodos de la solución. Pero también puede ejecutar los tests que están en una clase. Los métodos de tests no están “flotando” en cualquier clase, sino en las que están marcadas con el atributo [TestClass].

En nuestro tercer video:

Ejemplo de TDD: Implementando una Pila (Parte 2)

aparecieron nuevos atributos, como [TestInitialize]. Tendremos que explicarlos y repasarlos, en un próximo post, y escribir tests usando otros atributos. Comenzaremos a entender, entonces, el funcionamiento de un framework de tests (como cuándo crea una instancia de objeto de la clase tests, cómo invoca los métodos de tests, el ciclo de vida de las variables del objeto de test, el uso de archivos externos, etc..)

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, Jul 6 2010 15:42 by lopez | with no comments

Filed under: , ,

Proyecto Hogwarts: Taller de TDD en Buenos Aires

Como parte del:

Proyecto Hogwarts

se están preparando varios cursos (TDD, Inversion of Control, Mocks, S.O.L.I.D…), como parte del Southworks Professional Improvement Program (el nombre definitivo de los entregables visibles de proyecto interno Hogwarts), con material para instructores, y material para asistentes. La primera instancia pública de un taller sobre uno de estos temas, acaba de ser anunciada:

Media Jornada – Taller de Test-Driven Development (TDD)

en el MUG (Microsoft User Group) de Argentina. El curso estará a cargo de @MartinSalias y @sebarenzi. Leo en la página:

22 de Julio 2010
Lugar: Auditorio del MUG: Rivadavia 1479 1er Piso - Ciudad de Buenos Aires.

En este taller se realizará una introducción a Test-Driven Development, desde su motivación y principios, sus conceptos principales, y la mecánica de trabajo, poniendo énfasis en la práctica. Los ejercicios se realizarán en C# utilizando Visual Studio 2010.

Fecha: Jueves 22 de julio de 2010, en el horario de 14:00 a 18:00hs.

Prácticas en laboratorio.

Contenidos:
Introducción a TDD
Ejemplos y prácticas:

Pruebas Unitarias
Secuencia Rojo, Verde, Refactor
Listas de tests
Refactorización
Cobertura de Código
Diferencia entre pruebas unitarias y de integración
Desacoplamiento e Inyección de dependencias
Dependencias falsas y objetos mock

Vean que es con práctica en máquina. Lean ahí en la página del anuncio el costo del curso. Se anunciará ahí un sitio público en línea, para quienes asistan a uno de estos cursos presenciales, o para quienes no puedan acudir, pero quieran ir aprendiendo, con su tiempo y máquina, sobre temas de TDD (texto explicativo, videos, ejemplos, ejercicios, preguntas, bibliografía, material para si quieren dar un curso presencial en su zona). En el sitio estará el material a presentar en el curso. Seguramente, aparecerá un post por acá anunciando la URL.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, Jul 6 2010 14:26 by lopez | 1 comment(s)

Generando una solución con AjGenesis usando archivos hbm de NHibernate

Hace unos meses, escribí un ejemplo de generación de código que usa los archivos .hbm como modelo inicial:

Generating Code with AjGenesis Using NHibernate Hbm Files

Generando código con AjGenesis usando archivos de mapeo de NHibernate

Ya conocen que AjGenesis puede usar como modelo lo que uno decida. Prefiero un modelo libre, pero, según el bueno de Fabio Maulo, “los archivos hbm los usan un montón de gente”. Estos ejemplos me sirven también para demostrar que se puede usar un modelo distinto de los que propongo en otros ejemplos.

Desde esos posts, he mejorado el ejemplo. Pueden bajarlo del trunk del proyecto en Codeplex:

i

Como lo mío es un apostolado, les dejo preparado un paquete listo para ejecutar, en mi Skydrive, que incluye el ejemplo y los binarios de AjGenesis en desarrollo:

Examples > AjGenesis > NHibernateMappingExample02.zip

Esta es su estructura de directorios:

Hay dos proyectos: AjFirstExample, y AjTest. El primero tiene solamente dos simples entidades. El segundo tiene más entidades, con relaciones uno a varios. Pueden generar el código para una solución completa, lanzando los comandos:

GenerateAjFirstExample.cmd
GenerateAjTest.cmd

Son simples “shortcuts” para los comandos

GenerateProject AjFirstExample
GenerateProject AjTest

Como ejemplo, esta es la solución que genera para el proyecto AjTest (ver que queda dentro de un directorio Build, que si no existe lo crea):

Pueden ejecutar el sitio web (el script para generar la base de datos, está dentro del directorio Sql de cada proyecto, ej Projects\AjTest\Sql):

En este ejemplo, uso los archivos .hbm como modelo. Lo nuevo del ejemplo, es que enriquecí el modelo con el uso de tags meta, como en este  Employee.hbm de AjTest:

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
  assembly="AjTest.Entities"
  namespace="AjTest.Entities"
  >
  <class name="Employee" table="employees">
    <meta attribute="SetName">Employees</meta>
    <meta attribute="Descriptor">Employee</meta>
    <meta attribute="SetDescriptor">Employees</meta>
    
    <id name="Id" column="Id" type="Int32">
      <meta attribute="Description">Id</meta>
      <generator class="native"/>
    </id>
    <property name="EmployeeCode" type="String">
      <meta attribute="Description">Employee Code</meta>
    </property>
    <property name="LastName" type="String">
      <meta attribute="Description">Last Name</meta>
    </property>
    <property name="FirstName" type="String">
      <meta attribute="Description">First Name</meta>
    </property>
    <many-to-one name="Department" column="IdDepartment"
                 class="AjTest.Entities.Department, AjTest.Entities" />
    <bag name="Tasks" lazy="true" inverse="true" cascade="all">
      <key column="IdEmployee"/>
      <one-to-many class="AjTest.Entities.Task, AjTest.Entities"/>
    </bag>  
    <bag name="EmployeeSkills" lazy="true" inverse="true" cascade="all">
      <key column="IdEmployee"/>
      <one-to-many class="AjTest.Entities.EmployeeSkill, AjTest.Entities"/>
    </bag>  
  </class>
</hibernate-mapping>

Note the use of the meta tag at class level, and at property level. AjGenesis run tasks, one is Tasks\LoadMappings.ajg:

include "Utilities/Utilities.tpl"
include "Utilities/FileUtilities.tpl"
include "Utilities/TypeUtilities.tpl"
Include("Utilities/NHibernateUtilities.tpl")
include "Templates/CSharp/UtilitiesCs.tpl"
include "Templates/CSharp/CSharpFunctions.tpl"
AssemblyManager.LoadFrom("Libraries/NHibernate.dll")
parser = new NHibernate.Cfg.MappingSchema.MappingDocumentParser()
if not Project.BuildDir then
  Project.BuildDir = "Build/${Project.Name}/CSharp"
end if
Project.Entities = CreateList()
for each MappingName in Project.Mappings
  filename = "Projects/${Project.Name}/Mappings/${MappingName}.hbm.xml"
  mapping = parser.Parse(OpenAsStream(filename))
    
  for each hbmclass in mapping.Items where IsType(hbmclass, "HbmClass")
    Entity = CreateObject()
    
    Project.Entities.Add(Entity)
  
    Entity.ClassName = hbmclass.name
    Entity.Name = hbmclass.name
    Entity.Namespace = mapping.namespace
    
    Message "Processing Mapping of Entity " & Entity.Name
        
    Entity.Properties = CreateList()
    
    if hbmclass.Id then
      Property = CreateObject()
      Property.Name = hbmclass.Id.name
      Property.Type = HbmTypeToCSharp(hbmclass.Id.type1, Entity.Namespace)
      Property.IsId = True
      for each meta in hbmclass.Id.meta
        Property.SetValue(meta.attribute, meta.GetText())
      end for
      Entity.Properties.Add(Property)
    end if
    
    for each meta in hbmclass.meta
      Entity.SetValue(meta.attribute, meta.GetText())
    end for
        
    for each item in hbmclass.Items
      Message "Processing Item " & item.GetType().Name
      
      if IsType(item, "HbmProperty") then
        Property = CreateObject()
        Property.Name = item.name
        Property.SetValue("Description", "Description " & item.name)
        Property.Type = HbmTypeToCSharp(item.type1, Entity.Namespace)
        Entity.Properties.Add(Property)
        for each meta in item.meta
          Property.SetValue(meta.attribute, meta.GetText())
        end for
      end if
      
      if IsType(item, "HbmManyToOne") then
        Property = CreateObject()
        Property.Name = item.name
        Property.Type = HbmTypeToCSharp(item.class, Entity.Namespace)
        Property.Reference = HbmTypeToCSharp(item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
      if IsType(item, "HbmSet") then
        Property = CreateObject()
        Property.Name = item.name
        Property.IsSet = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
      if IsType(item, "HbmBag") then
        Property = CreateObject()
        Property.Name = item.name
        Property.IsList = true
        Property.Type = HbmTypeToCSharp(item.Item.class, Entity.Namespace)
        Entity.Properties.Add(Property)
      end if
    end for    
  end for
end for

Noten el uso de Property.SetValue(…), Entity.SetValue(…) para enriquecer el modelo en-memoria con la nueva metadata contenida en los archivos de mapeo. Cualquier dato simple que en anteriores ejemplos ponía en los modelos libres, ahora puede estar en el hbm. Como en otros ejemplos de AjGenesis, uso la metadata para mostrar leyendas, textos en la presentación, o para tomar decisiones o usarlas como quiera en las tareas y templates.

Este es un ejemplo “proof-of-concept”. Debería ser mejorado para usar todo el poder de NHibernate. Pero es una demostración de un modelo inicial, para crear gran cantidad de artefactos de textos, en este caso, una aplicación lista para ejecutar y extender.

Tengo que escribir más ejemplos, volviendo a usar un modelo libre, como adelanté en:

AjGenetizando una aplicación

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Mon, Jul 5 2010 15:09 by lopez | with no comments

AjGenetizando una aplicación

Hace un tiempo, escribí:

Code Generation with AjGenesis in a Real Project

Generación de código en un proyecto real

En ese proyecto real, apliqué lo que puedo llamar “ajgenetizar” una aplicación. ¿Qué significa esto?

La idea básica es:

- Comenzar escribir una aplicación que funciona, que reseulva los problemas técnicos, como persistencia, validaciones, seguridad, distribución de la lógica, presentación, etc… como para tener un punto de partida sólido.
- Comenzar a descubrir las partes repetitivas: archivos de configuración, archivos de proyectos, directorios, clases de acceso a datos, clases de lógica, clases de dominio, otros archivos de texto,… 
- Extraer un modelo inicial: puede ser, descripción de las entidades, algunos parámetros técnicos como la base de datos a usar, etc..
- Escribir tareas y templates para generar una aplicación similar, automáticamente.
- Especificar claramente, cuáles de esos artefactos de texto

- son regenerados cada vez que el modelo cambia
- son generados una vez, y etnonces, evolucionan manualmente
- son creados manualmente

- Alterar el modelo, y regenerar: todo debería seguir trabajando.
- Escribir otro modelo, para producir otra aplicación, pero reusando el conocimiento adquirido (sobre la lógica de implementación, patrones, tecnología usada).
- Otra “prueba ácida”: cambiar una de las tecnologías que se usaron (de acceso a datos directo a ORM, de validación en código a un framework de validación, agregar un contenedor de Inversion of Control, etc…), y probar la nueva aplicación

Podría comenzar a escribir una serie de posts, adoptando este camino. Pero pienso que se necesita un camino más tipo “tutorial”, para el estado actual del proyecto AjGenesis (poca documentación, pocos posts y bloggers en el tema, pocos ejemplos explicados…). El camino que seguiré es: comenzar con un modelo mínimo, generar unos pocos archivos, y entonces, iterar, para refinar el modelo y el alcance de lo generado, comentando en detalle las decisiones tomadas, publicando el código de cada avance.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Fri, Jul 2 2010 11:42 by lopez | 4 comment(s)