martes, 20 de noviembre de 2007

Arquitectura del software?


Muchas veces nos hablan de la arquitectura en el desarrollo de sistemas. Palabrita de moda quizás...; ingenieros, analistas y programadores que se hacen llamar "Arquitectos de software" como si de un título se tratase. Hasta yo la he puesto como título en mi Blog, entonces, cual es el significado de la Arquitectura en el desarrollo de un sistema?, tratemos de averiguarlo.

En setiembre de este año se realizaron las 36 jornadas de la Jaiio en mi ciudad (Mar del Plata), y al ver el temario de los seminarios, me informo de un curso especial de 8 hs. seguidas, llamado "Introducción a la arquitectura del software", dictado por el Gerente de relac. académicas de Microsoft y profesor de la UBA Pablo Michelis. Por supuesto para entender mejor este concepto y para agregar valor a mis clases me inscribí; esto intenta ser un breve resúmen del mismo.

Comencemos por definir "Arquitectura del soft":
"Estructura de sistemas o sistema de estructuras que consisten en elementos, sus propiedades externamente visibles y la relación entre ellos"
"Propiedades externamente visibles: qué es lo que debe saber/suponer un componente sobre otro?"
Por último :
"
Toda la arquitectura es diseño, pero no todo el diseño es arquitectura. La arquitectura representa las decisiones de diseño significativas que le dan forma a un sistema, donde lo significativo puede ser medido por el costo del cambio". (Grady Booch)

Creo que es la más simple y directa definición de arquitectura.

Basándonos en esta última definición podemos suponer que lo que distingue a la arquitectura de otro tipo de diseño es la significancia expresada en la dificultad ó costo del cambio.
La arquitectura del soft mantiene unidas las nociones de : diseño, estructura, estilo, racionalidad, proceso y costo.
Ahora bien, como podemos evaluar la calidad de una determinada arquitectura para nuestra solución de software? , quizás una buena arquitectura sea aquella en la que las decisiones de diseño significativas se lograron minimizar, evitando riesgos de cambio futuros. De esta forma estaríamos libres de cambiar varios aspectos del sistema sin el miedo a que tales cambios produzcan cascada de errores y cambios en el diseño que nos llevarían a altos costos de mantenimiento. Una buena arquitectura debe ser sustentable.

Como se relaciona esta visión de la arquitectura con la estructura del sistema?, pues construyamos componentes, clases, paquetes, ensamblados, etc. que sean altamente cohesivos y con bajo acoplamiento (nuestro ying y yang recuerdan?).
En una arquitectura con bajo acoplamiento, donde los puntos de variación futuras estén bien encapsulados, los efectos del cambio estarán bien aislados y el costo será relativamente bajo.
Por esto es que a nivel de arquitectura hablamos de los patrones de diseño, que nos permiten el desarrollo de mejores arquitecturas de software como por ejemplo la 3 capas explicada y ejemplificada en previos posts.

lunes, 19 de noviembre de 2007

Desarrollar en 3 capas con .NET (parte 2) - La controladora y la presentación

Antes de mostrar el manejo de la capa de negocio, les aclaro el concepto de Viatico:
"es una retribución diaria que cobra un empleado cuando la empresa lo envía a realizar alguna tarea fuera de su ámbito normal de trabajo (normalmente a otra ciudad), este importe diario depende de la categoría del mismo. A esto se le suman todos los gastos extras que surjan por movilidad, inscripción a cursos, etc. Normalmente se genera una Solicitud que , en caso de ser aprobada por un superior genera el Viático correspondiente." (los sustantivos marcados en negrita corresponden a algunos de los objetos de negocio de nuestro sistema).

Aclarado este punto importante para poder entender mínimamente el objetivo del sistema, a continuación veremos como desarrollar nuestra capa de negocio.

La capa de negocio (ó dominio del problema) de nuestro sistema, estará compuesta de todos los procesos que fueron analizados como casos de uso, por lo que es común denominar a estas clases : controladora del caso de uso XX.

Capa de negocio (componente ViaticoProcesos)

En mi caso de ejemplo tengo una clase CSolicitud que posee la responsabilidad de controlar todos los procesos inherentes al manejo de Solicitudes de viáticos.
Si quisiéramos darle más flexibilidad (y en consecuencia mayor reutilización futura) al sistema, deberíamos desarrollar una interfaz que agrupe los métodos comunes a todos los posibles sistemas de Viáticos (IViaticoSolicitud) .
Logrando cierta abstracción defino los contratos de mi interfaz:

public interface IViaticoSolicitud
{
void CrearSolicitud(Solicitud nueva);
Viatico AutorizarSolicitud(Solicitud s);
void ImprimirSolicitud(Solicitud s);
List BuscarSolicitud(Solicitante s);
List
BuscarSolicitud(Solicitante s, DateTime Desde);
List BuscarSolicitud(Solicitante s, DateTime Desde, Boolean Aprobadas);
Solicitud BuscarSolicitud(int id);
}

Asi que entonces mi clase CSolicitud implementará mi IViaticoSolicitud:


public class CSolicitud:IViaticoSolicitud
{
// Declaro los DAOs con los que va a trabajar mi controladora

ISolicitudDao solicitudDao;

IViaticoDao viaticoDao;

IEmpleadoDao empleadoDao;
IMovilidadDao movilidadDao;


public CSolicitud()

{ // Creo la fabrica y los DAOs que usaré
NHibDAOFabrica fabricaDAO = new NHibDAOFabrica();

this.solicitudDao = fabricaDAO.GetSolicitudDao();

this.viaticoDao = fabricaDAO.GetViaticoDao();

this.empleadoDao=fabricaDAO.GetEmpleadoDao();

this.movilidadDao = fabricaDAO.GetMovilidadDao();

}


#region IViaticoSolicitud Members


public void CrearSolicitud(Solicitud nueva)

{
if (nueva.Solicitante != null)
{ try { this.solicitudDao.Save(nueva); }
catch { thro
w; }
}
}

public Viatico AutorizarSolicitud(Solicitud s)

{
try
{ // Marco como aprobada la Solicitud
s.Aprobada = true;
// Creo Viatico basado en la Solicitud aprobada
Viatico v = new Viatico();
v.Estado = Viatico.estado_viatico.Creado;
v.Solicitud = s;

v.Numero = this.viaticoDao.DarUltimoNro()+1; // Verificar concurrencia...
// Persisto cambios en Solicitud y Grabo nuevo viatico
this.solicitudDao.SaveOrUpdate(s);

this.viaticoDao.Save(v);

return v;
}
catch { throw ; }
}

public void ImprimirSolicitud(Solicitud s)
{
throw new Exception("The method or operation is not implemented."); }

public List BuscarSolicitud(Solicitante s)
{
try

{
List lista = this.solicitudDao.DarSolicitudesXSolicitante(s,null,null);

return lista;
}
catch { throw ; }

}


public List BuscarSolicitud(Solicitante s, DateTime Desde)
{
try
{
List lista = this.solicitudDao.DarSolicitudesXSolicitante(s, Desde, null);
return lista;
}
catch { throw ; }

}


// etc, etc ..............


#endregion

}

Como verán muchos de los métodos solicitan objetos a la capa de acceso a datos (clases DAO), por lo que termina siendo un llama
do a un método similar en el DAO. Aquí es donde mis alumnos dicen " porqué debemos hacer las cosas por duplicado?, y si llamamos desde la capa de presentación al método del DAO directamente ? ".
OK, el tema es que la Controladora posee métodos como
AutorizarSolicitud(Solicitud s), donde debemos desarrollar código que sólo es inherente al proceso de negocio y además trabajar en una transacción con varios objetos DAO.
En este ejemplo se cambia un atributo del objeto Solicitud, se crea un objeto Viatico, se pide un nro. de viático a un método estático de Viatico, y se persisten ambos objetos, vía llamado a los métodos de sus DAOs.

Capa de presentación Web (componente ViaticoWeb)

Por último, veamos un ejemplo de mi Capa de presentación Web, que permite la autorización de determinada solicitud de viático.
La página web muestra una lista de las solicitudes a autorizar por este usuario (jefe).
Selecciona una, y pulsa el botón "Autorizar".



Al pulsar este botón lo captura el evento "click" del mismo y este es el código:

protected void ButtonAutorizar_Click(object sender, EventArgs e)
{
Solicitud elegida=(Solicitud)Session["SolicitudBuscada"];
if (elegida!= null)
{
// creo controladora de Proceso de Solicitudes
CSolicitud cSolicitud = new CSolicitud();
try
{
Viatico v=cSolicitud.AutorizarSolicitud(elegida);
this.CargarGrid((Usuario)Session["Usuario"]);
this.LabelError.Text = "Viatico"+v.Numero.ToString()+" CREADO";
}
catch
{
Exception ex = new Exception();
this.LabelError.Text = ex.Message;
}
Response.Redirect("~/Admin/Autorizar.aspx");
}
else
this.LabelError.Text = "Seleccione una Solicitud para Autorizar...";
}

Como ven , se crea un objeto de tipo CSolicitud y se llama a su método AutorizarSolicitud pasándole como parámetro la solicitud seleccionada por el usuario. Cualquier error en el proceso llamado será capturado (catch), mostrándose el mensaje de error pertinente.

Capa de Objetos de negocio (componente ViaticoBO)

Mis objetos de negocio son clases independientes de la tecnología que se use para persistencia.
Ejemplo: mi BO Solicitud.

public class Solicitud
{
public Solicitud()
{ }

int id;

public int Id
{
get { return id; }
set { id = value; }
}
Solicitante solicitante;

public Solicitante Solicitante
{
get { return solicitante; }
set { solicitante = value; }
}
System.DateTime fecha_soli;

public System.DateTime Fecha_soli
{
get { return fecha_soli; }
set { fecha_soli = value; }
}
string motivo;

public string Motivo
{
get { return motivo; }
set { motivo = value; }
}

int numero;

public int Numero
{
get { return numero; }
set { numero = value; }
}
string origen;

public string Origen
{
get { return origen; }
set { origen = value; }
}
string destino;

public string Destino
{
get { return destino; }
set { destino = value; }
}
System.DateTime desde;

public System.DateTime Desde
{
get { return desde; }
set { desde = value; }
}
System.DateTime hasta;

public System.DateTime Hasta
{
get { return hasta; }
set { hasta = value; }
}
Movilidad ida;

public Movilidad Ida
{
get { return ida; }
set { ida = value; }
}
Movilidad vuelta;

public Movilidad Vuelta
{
get { return vuelta; }
set { vuelta = value; }
}

Boolean aprobada;

public Boolean Aprobada
{
get { return aprobada; }
set { aprobada = value; }
}

}

En este componente también poseo los archivos de mapeo XML que necesita NHibernate (1 por clase BO), ya que recomiendan que estén en la misma .dll que las clases que mapean.

Bueno, hasta aquí, esta breve explicación de mi desarrollo con la arquitectura 3 capas usando .NET y NHibernate; no quise profundizar en el uso de ninguna herramienta, sólo mostrar ejemplos del desarrollo a nivel de arquitectura y componentes de soft.
Quedo a las órdenes de cualquiera que desee aportar comentarios sobre mejoras, experiencias, ó preguntas sobre la forma de trabajo; que quizás no hayan quedado del todo claras en este post.


jueves, 15 de noviembre de 2007

Desarrollar en 3 capas con .NET y NHibernate - Parte 1 (Acceso a datos)

Luego de haber posteado que es el diseño en 3 capas, y que es un ORM con ejemplos de NHibernate, estamos en condiciones de comprender el desarrollo de software mediante esta arquitectura con el framework .NET.
Echemos un vistazo a este diagrama de componentes que aplica esta arquitectura de 3 capas (para el desarrollo de un sistema de viáticos):


















Podemos ver que además de las capas de presentación, procesos del negocio y acceso a datos, tenemos los objetos de negocio aparte como si de otra capa se tratase. Aunque en la realidad debería estar en la capa del medio, esto presenta problemas en la capa de acceso a datos que pasaría a desconocer los objetos con los que debe trabajar NHibernate en nuestras clases DAO. Para comprender claramente de que estoy hablando debemos ver las clases que se encuentran en cada componente de software.

Empezando por el acceso a datos:




















NHibernateCore

Aqui estan : una interfaz para los DAO (data access object) genérica (IDAO), una clase abstracta que implementa IDAO (NHibDAOAbstracto), y manejo de sesiones y transacciones de NHibernate. Su contenido responde al excelente trabajo de Billy McCafferty: NHibernate best practices with ASP.Net, cuya lectura recomiendo, antes de comenzar a desarrollar una capa de acceso a datos.

Por ej. IDAO:

public interface IDAO // maneja Generics de Net 2.0
{
T GetById(IdT id, bool shouldLock);
List GetAll();
List GetByExample(T exampleInstance, params string[] propertiesToExclude);
T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude);
List Find(string consulta);
IList FindRetLista(string consulta);
T Save(T entity);
T SaveOrUpdate(T entity);
void Delete(T entity);
void CommitChanges();
}

  • ViaticoDAO
  • Aquí se encuentran : la fábrica de DAOs, y los DAO de cada objeto de negocio.
    Por ej. la clase SolicitudDAO :
    public class SolicitudDAO : NHibDAOAbstracto, ISolicitudDao
    // Hereda del DAO abstracto definido en NHibernateCore e implementa ISolicitudDao
    // para no hacer uso directo del DAO abstracto
    {

    public List DarSolicitudesXSolicitante(Solicitante s, Nullable Desde, Nullable Aprobadas)
    {
    string criterios = "FROM Solicitud a WHERE a.Solicitante.ID = " + s.ID.ToString();

    if (Desde != null)
    criterios += " and Fecha_soli >=" + ((DateTime)Desde.Value).ToShortDateString();
    if (Aprobadas != null)
    {
    if (Aprobadas == true)
    criterios += " and Aprobada=1";
    else
    criterios += " and Aprobada=0";
    }

    IDaoFabrica daoFabrica = new DAOFabrica();
    ISolicitudDao soliDao = daoFabrica.GetSolicitudDao();

    List solicitudes;
    solicitudes = soliDao.Find(criterios);
    return solicitudes;
    }

    public Solicitud DarSolicitudXid(int id)
    {
    IDaoFabrica daoFabrica = new DAOFabrica();
    ISolicitudDao soliDao = daoFabrica.GetSolicitudDao();

    Solicitud s = soliDao.GetById(id, false);
    return s;
    }
    }


    ISolicitudDao:

    public interface ISolicitudDao : IDAO
    {
    List DarSolicitudesXSolicitante(Solicitante s, Nullable Desde, Nullable Aprobadas);

    Solicitud DarSolicitudXid(int id);
    }

    Por último un ejemplo de DAOFabrica:

    public class DAOFabrica : IDaoFabrica
    {
    public IEmpleadoDao GetEmpleadoDao()
    {
    return new EmpleadoDAO();
    }


    public ICategoriaDao GetCategoriaDao()
    {
    return new CategoriaDAO();
    }


    public ISolicitudDao GetSolicitudDao()
    {
    return new SolicitudDAO();
    }

    ...
    }

    Lo destacable en esta capa de acceso a datos es su independencia del motor de NHibernate, gracias al uso de la Fábrica de DAOs. Por ej. si quisiera reemplazar el NHibernate por Linq for Sql (del Net 3.0), cambiaría en mis DAOs la herencia de la clase abstracta que implementa los métodos contra el motor de acceso a datos:
    public class SolicitudDAO : NHibDAOAbstracto, ISolicitudDao por
    public class SolicitudDAO : LinqDAOAbstracto, ISolicitudDao

    Por supuesto deberé desarrollar mi clase LinqDAOAbstracto para que implemente los métodos de la interfaz IDAO.

    En parte 2 veremos la capa de negocio (procesos y reglas; ó controladoras de los casos de uso) y ejemplos de los objetos de negocio, cuyas clases están implementadas en su propio componente.


    miércoles, 31 de octubre de 2007

    Que es un ORM (object-relational mapping)

    El ORM es un componente de software que me permite trabajar con los datos persistidos como si ellos fueran parte de una base de datos orientada a objetos (en este caso virtual). Debido a que lo standard es trabajar con BD relacionales, se deben realizar operaciones que permitan transformar un registro en objeto y viceversa. A esta funcionalidad se la llama Mapeo objeto-relacional (ORM).
    Uno de los componentes ORM más utilizado (sino el más...) es el Hibernate, surgido del ambiente Java y llevado al uso del framework .NET con la versión NHibernate
    Para poder explicar el porqué de la existencia de esta tecnología, nada mejor que un ppt que preparé para una clase sobre este tema a los alumnos del último año de TSSI, que les dejo disponible a continuación.



    *(Si les aparece el mensaje que "ha sido removido o es privado" pulsen en "VER"
    al lado del logo de "slideshare", gracias)

    sábado, 22 de septiembre de 2007

    Sigamos solucionando nuestro diseño con los patrones

    Siguiendo con nuestra charla de diseño, determinemos la granularidad de los objetos intervinientes .
    Los objetos varían enormemente en tamaño y número, y representan cualquier cosa desde hard a sistemas completos, por lo que como decidir que debería ser un objeto?
    Veamos algunos patrones:
    • Fachada (GoF) describe como representar subsistemas ó procesos completos como objetos.
    • FlyWeight(GoF) describe como permitir un gran número de granularidad muy fina.
    • Abstract Factory (GoF) produce objetos cuya única resp. es crear otros objetos.
    • Visitor y Command (GoF) generan objetos cuya resp. es implementar una petición en otros objs. ó grupo de objs.

    Especificar las interfaces de los objs.

    Los patrones nos ayudan a definir interfaces identificando sus elementos clave y los tipos de datos que se envían a la interfaz. También especifican relaciones entre interfaces, ya que: requieren que algunas clases tengan interfaces parecidas ó les imponen restricciones; por ej:

    Decorador y Proxy (GoF) requieren que las interfaces de sus objs. sean idénticos a los objs. deocrado y representado, respectivamente.

    Especificar las implementaciones

    Herencia de clases vs. herencia de interfaces, muchos de los patrones de diseño dependen de esta distinción. Por ej:

    • Los objs. de una cadena en Chain of Responsability (GoF) deben tener un tipo en común, pero normalmente no comparten la misma implementación.
    • En Composite (GoF), el componente define una interfaz común mientras que el compuesto suele definir una implementación en común.
    • Command, Observer, State y Strategy (GoF) suelen implementarse con clases abstractas que son interfaces puras.

    En este tema, debemos tener en mente uno de los principios fundamentales del diseño OO: programar para interfaces, no para una implementación. Por que?

    • Ventajas de manipular objs. solamente en términos de la interfaz definida por las clases abstractas. Los clientes no tienen que conocer los tipos específicos de los objs. que usan, basta con que estos adhieran a la interfaz que esperan que esperan los clientes.
    • Reducción del acoplamiento entre subsistemas.
    • No se deben declarar variables como instancias de clases concretas, se ajustan simplemente a la interfaz definida por una clase concreta.
    • Para crear instancias de clases concretas usamos los patrones de creación; estos ofrecen distintos modos de asociar una interfaz con su implementación de manera transparente, y aseguran que el sistema se codifique en términos de interfaces, no de implementación.

    Por último, pongamos a funcionar los mecanismos de reutilización:

    Herencia vs. Composición

    Son las 2 técnicas más comunes para reutilizar funcionalidad en sistemas OO.

    La composición requiere que los objs. tengan en cta. las interfaces de otros, lo cual requiere interfaces cuidadosamente diseñadas, que no impidan que un obj. sea utilizado por otros. Optar por la composición ayuda a mantener cada clase encapsulada y centrada en una sola tarea. En los patrones de diseño veremos esta técnica aplicada una y otra vez...otro principio fundamental: favorecer la composición de objetos frente a la herencia de clases.

    Pero qué es la composición?: método por el cual una nueva funcionalidad se obtiene mediante la creación de un objeto compuesto por otros objetos, ésta se logra delegando funcionalidad a uno de los objs. de la composición.

    Mientras que la Herencia es un método por el cual la nueva funcionalidad se obtiene mediante la extensión de la implementación de un objeto existente. La superclase (generalización) captura los métodos y atributos comunes, y las subclases (especializadas) extienden la implementación con métodos y atrib. adicionales. Es más sencilla de llevar a cabo que la composición, pero...

    Desventajas de la herencia:

    • Rompe la encapsulación, ya que expone la subclase a los detalles de implementación de su superclase
    • Reutilización de caja blanca, porque los detalles internos de la superclase son visibles a las subclases
    • Las subclases han de cambiar si la implementación de la superclase cambia

    Entonces que usamos?, según Coad, solo utilizar herencia cuando:

    • una subclase expresa "es un tipo de" y no "es un rol desempeñado por".
    • una instancia de una subclase nunca necesita convertirse en un obj. de otra clase
    • una subclase extiende las resp. de su superclase, no las redefine ni las anula.
    • para una clase del problema del dominio actual, la subclase especializa un rol ó una transacción.

    Por último utilicemos la Delegación cuando no existan relaciones estáticas entre clases, cuando la herencia no tenga sentido, por ej. para gestionar Roles.

    Bien, como vemos obtener un buen diseño es difícil; lo primero que deberíamos saber es : que es un buen diseño?, los conceptos que detallamos antes nos ayudan a dilucidar esta pregunta. Lo segundo será aplicar los principios de diseño de forma sistemática, una buena solución es copiar a los que saben: aplicar los patrones, donde puedan aplicarse.

    sábado, 8 de septiembre de 2007

    Siguiendo con nuestra charla sobre como los patrones pueden socorrernos en nuestros difíciles momentos de diseño, veamos como "encontrar los objs. apropiados para asignarles responsabilidades".
    Coincidamos en que lo más complicado del DOO es descomponer un sistema en objetos.
    Para lograr este objetivo, resumamos las tareas del análisis:
    • Partimos de encontrar los conceptos de nuestro dominio de problema y así efectuar un modelo conceptual donde identificamos las clases que surgen del negocio (nuestros objs. de negocio) y sus asociaciones.
    • Luego deberíamos obtener diag. de secuencia del sistema (basados en nuestros CU) donde determinemos los eventos y operaciones del sistema
    • En este punto sería importante obtener los "contratos" de los CU, donde se responderá: que hacen las operaciones del sistema?. Mediante la descripción de las postcondiciones de los contratos podemos describir cómo cambia el sistema tras cada operación; esta descripción se realiza en términos de : creación y destrucc. de objetos, asociac. formadas y canceladas y modificac. de atributos.
    Con toda esta documentación podremos comenzar nuestra tarea creativa más importante en el desarrollo de un sistema: el DISEÑO.
    Ahora asignemos responsabilidad a nuestros objetos, aunque aclaremos que existen 2 categorías de responsabilidades: de Conocer y de Hacer.

    Conocer

    • los datos privados encapsulados
    • los objetos relacionados
    • las cosas que puedan derivarse ó calcular

    Hacer

    • algo el mismo: crear un obj. ó hacer un cálculo
    • iniciar una acción en otros objs.
    • controlar y coordinar actividades en otros objs.

    Para asignar responsabilidad a nuestros objs. , hablemos de 3 patrones Grasp: Experto en información, Creador y Controlador. (es.wikipedia.org/wiki/Grasp)

    experto en informac.: asignar la resp. a la clase que tiene la informac. necesaria para realizarla. Esto debe hacerse sin generar nuevos acoplamientos, manteniendo la cohesión alta.

    creador: asignar a la clase B la resp. de crear un obj. de la clase A si se cumple que:

    • B agrega objs. de A
    • B contiene objs. de A
    • B registra objs. de A
    • B usa objs. de A cercanamente (directa)
    • B tiene los datos de inicialización que se pasan a un obj. A cuando es creado

    Debemos agregar que muy a menudo la creación de un obj. es de alta complejidad, por lo que sería aconsejable delegar la creación a clases auxiliares llamadas "Fábricas" (factorys)

    controlador: asignar la resp. de recibir ó gestionar un mensaje de evento del sistema a una clase que:

    • represente el sistema global ó subsistema (Fachada)
    • represente un escenario de CU en el que tiene lugar el evento (controladora de CU ó sesión)

    Tener en cuenta que un controlador debería delegar en otros objs. el trabajo que se necesita hacer, sólo coordina y controla la actividad (compuesta de varias tareas).

    Seguimos en el próximo....



    viernes, 7 de septiembre de 2007

    Principios de diseño - Como llegamos a los Design Patterns??



    En una charla con mis alumnos de último año, intenté describir el proceso de pensamiento que nos lleva a la aplicación de los patrones de diseño.


    Para empezar debemos recordar los conceptos básicos del diseño (no exclusivos del DOO), nuestro ying y yang : Acoplamiento y Cohesión, el 1ro. que determina el nivel de dependencia y el 2do. el nivel de unión interno (realizar una única función)

    OK, teniendo en mente aquello, intentemos comprender los términos: Responsabilidad e independencia, con un diagrama UML de paquetes.

    Por ej.:
    Un paquete X del cual dependen muchos paquetes debe ser muy estable. No debe cambiar porque es Responsable de los 3 paquetes que dependen de él.
    • X es Independiente, no depende de nadie.
    • Su Estabilidad = Responsabilidad + Independencia


    Existe alguna medida sencilla para saber que tan estable es un paquete?

    Factor de Inestabilidad (I) => I=(Ce / (Ca + Ce)). Si I[0,1]=0 será totalmernte estable.

    Donde : Ca (cuan responsable soy?) son clases fuera del paquete que dependen del él. Ce (cuan dependiente soy) son clases del paquete que dependen de clases externas a él.

    "La estabilidad de un paquete debe ser proporcional a su nivel de Abstracción" (R.Martin-1996); esto implica que los paquetes con máxima estabilidad deben ser abstractos; y los inestables concretos.

    Esto nos lleva a entender que aquellas decisiones de diseño y arquitectura de alto nivel de abstracción no suelen cambiar, así que los situaremos en paquetes estables. Aquí surge una pregunta interesante: como puede un paquete totalmente estable (I=0) ser lo suficientemente flexible como para soportar los cambios?. O sea, mejorarlo sin cambiarlo.
    Respuesta, otro principio : Abierto-Cerrado. Clases que puedan ser extendidas sin modificar lo ya existente: clases abstractas.

    Ahora bien, cuando intentamos seguir estos principios básicos del diseño, descubrimos que las estructuras que desarrollamos para cumplirlos, se repiten una y otra vez; esto es lo que conocemos como "Patrón de diseño". Estos deben ser soluciones probadas a los problemas de diseño más comunes, resolviéndonos los sig. puntos:

    1. Encontrar los obj. apropiados para asignarles responsabilidad.
    2. Determinar la granularidad (su nivel de abstracción) de los objetos.
    3. Especificar sus interfaces.
    4. Especificar las implementaciones que deben tener los objs. involucrados.
    5. Poner a funcionar los mecanismos de reutilización

    Ahora debemos diferenciar 2 viejas categorías de patrones de diseño: GRASP y GoF

    Los GRASP no implementan las soluciones, más bien nos llevan a pensar en el diseño, a nivel de principios generales (más principios de DOO...); mientras que los GoF nos dan una solución implementable con su propio diagrama de clases que nos muestra la forma en que debemos usarlos.

    En próximas notas iremos viendo los patrones relacionados con cada uno de los 5 puntos que nos resuelven los patrones (patterns).