Interfaces Web Forms herencia multiple : Javier Luna blog

Monday, April 13, 2009

Interfaces Web Forms herencia multiple

Interfaces en .NET son pocas veces usadas, por NO decir que jamas son mencionadas, en la implementacion de tu producto de software.

¿Que esta sucediendo? ¿Como te sentis al saber que NO estas utilizando todo el potencial de la tecnologia que tienes en tus manos, hablando estrictamente de C#, claro esta?

Pues VB.NET es un juguete para niños, mas nada :)

Lo que sucede es que NO le encuentras, muchas veces, razon de ser a la necesidad de definir una serie de metodos en una Interface que luego deberas implementar en cada Clase o Class que requiera implementarla.

Suena a realizar trabajo doble. A que NO ganas nada, en realidad, desde un limitado punto de vista.

Nada mas falso que la verdad :)

Las Interfaces tienen una valiosa utilidad. Aquella en la que ese comportamiento definido pueda ser utilizado por otras clases con la finalidad de brindar una valvula de escape al famoso, pero poco usado polimorfismo, en un lenguaje de programacion Orientado a Objetos, quimicamente NO puro, como el caso de los lenguajes de .NET ;)

Por ejemplo, la interfaz IEnumerable, que por cierto es la mas utilizada por el .NET Framework, radica su valiosa utilidad en brindarle la posibilidad a la clase que la implemente en que esta pueda ser iterable sobre el famoso foreach. Omnipresente estructura de control de flujo en el C#.

IEnumerable es de un nivel primario que casi forma parte de la esencia misma del lenguaje C#.NET.

Nosotros ya NO implementamos IEnumerable en nuestras propias clases, pues existen diferentes estructuras de datos, que la implementan y nos facilitan ya esa tarea. Solo nos limitamos a usarla, a sabiendas que existe una estructura de control que la recibe para hacer algo con ella, iterarla. Hablamos del curioso foreach.

He alli la clave para la existencia de Interfaces en el modelo de clases orientadas al producto de software que estamos diseñando.

La necesidad de la existencia de una Clase a la par que defines Interfaces para que las utilizen y hagan algo con ellas, el trabajo pesado. Es decir, para que estas sean parte de la implementacion de un comportamiento de orden superior por el cual fueron concebidos.

¿Se entiende? ¿Toy hablando piedras?

Un ejemplo por favor...!

Bueno, el mas trivial de todos se encuentra en la definicion de un Modelo de Clases e Interfaces para el manejo de Web Forms y Web User Controls en productos de software con conectividad a Base de Datos.

Los Web Forms asumen la gestion de las acciones realizadas por el usuario e implementan el comportamiento, como respuesta a dichas acciones.

Los Web User Controls realizan las tareas a un nivel especifico de detalle, hacen el trabajo laborioso: asignar valores a los TextBox, llenar un DropDownList, recolectar los valores seteados de su propia estructura en un Business Entity y entregarselos a quien lo solicita, and other stuffs :)

En ese sentido, son los Web User Controls candidatos ideales a implementar tus propias Interfaces, previamente definidas, con la intencion de centralizar ciertos comportamientos e implementarlos a nivel de Web Forms.

En concreto, tendras una clase UIPage que hereda de Page, facilitando cierta funcionalidad comunmente usada.

class UIPage : Page
{
protected override void OnLoad( EventArgs e )
{
if( ! this.IsPostBack )
{
this.FirstRequest();
}
}

protected virtual void FirstRequest() { }

protected override void OnInit( EventArgs e )
{
this.DefineHandlers();
this.DefineObjects();
}

protected virtual void DefineHandlers() { }
protected virtual void DefineObjects() { }

// Otras tantas funcionalidades
}

A la vez, tendras una clase UIPageForm que hereda de UIPage

class UIPageForm<TForm> : UIPage
where TForm : IForm
{
protected virtual TForm MyForm
{
get { throw new NotImplementedException(); }
}

// Otras tantas funcionalidades
}

Asi mismo, tendras una clase UIPageEdit que hereda de UIPageForm, centralizando el comportamiento para editar los elementos de ciertas entidades de negocio.

class UIPageEdit<TForm, TAtom> : UIPageForm<TForm>
where TForm : IFormEdit<TAtom>
where TAtom : ITransaction
{
protected virtual TAtom MyAtom
{
get { throw new NotImplementedException(); }
}

// Otras tantas funcionalidades
}

Ahora, imaginemos que se requiere un nuevo comportamiento, por el cual al editar un Business Entity sobre el Web Form, uno de los datos ingresados por el usuario, sobre la vista o View, deba ser unico. Permitiendo la posibilidad de validar aquello a traves de una opcion especial en el View. Asi como, tambien, estrictamente validable al elegir la opcion Save.

Entonces optaremos por una implementacion como esta:

class UIPageEditValidate<TForm, TAtom> :
UIPageEdit <TForm, TAtom>
where TForm : IFormEdit<TAtom>, IFormValidate
where TAtom : ITransaction
{
// Se varia el comportamiento base sobre este contexto
// para soportar el nuevo requerimiento.
}

Ahora, se requiere que ciertos Web Forms soporten el manejo de informacion historica por cada registro, permitiendo navegar sobre dicho historico a traves del mismo Web Form.

Este nuevo requerimiento podria aplicar tanto a Web Forms que soporten la funcionalidad de IFormValidate como a los que NO.

Aqui empieza nuestro problema, como consecuencia de la NO existencia de una herencia multiple de clases, como lo pregona aquella preciosa teoria de la POO, que tanto añoramos.

Si nuestro C# fuese un lenguaje Orientado a Objetos quimicamente puro, bastaria implementar la funcionalidad requerida sobre una clase llamada Navigator, llamada asi para efectos didacticos, y hacer que los Web Forms que requieran dicha funcionalidad la herenden, a la par que heredan su tradicional clase base, que en el mas alto nivel hereda de Page.

Pero NO es posible, entonces tendremos que buscar una forma de superar esta limitacion tecnica.

En esa busqueda, el uso correcto de Interfaces sera la solucion para este requerimiento, definiendo una interfaz INavigator y una clase llamada ConcreteNavigator.

Haciendo una simple analogia, con fines didacticos, INavigator seria como IEnumerable y a su vez ConcreteNavigator seria como la estructura de control foreach. Aquella que hace algo con la interfaz, que la utiliza en el contexto de la implementacion de un comportamiento de orden superior a la simple definicion de una Interfaz.

En ese sentido, tendriamos algo como esto para la interfaz INavigator:

public interface INavigator
{
event EventHandler PreviousClicked;
event EventHandler CurrentClicked;
event EventHandler NextClicked;

void StatusPrevious( Boolean enabled );
void StatusCurrent( Boolean enabled );
void StatusNext( Boolean enabled );
}

Por su parte la clase ConcreteNavigator tendria una implementacion como esta.

public class ConcreteNavigator<TForm, TAtom>
where TAtom : IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>
{
StateBag _viewstate;
INavigator _navigator;
TForm _form;
TAtom _atom;

public ConcreteNavigator(
StateBag viewstate,
INavigator navigator,
TForm form,
TAtom atom )
{
this._viewstate = viewstate;
this._navigator = navigator;
this._form = form;
this._atom = atom;
}

public void DoPreviousClicked()
{
// Implementacion del comportamiento requerido
// usando los parametros entregados
// al instanciar ConcreteNavigator
// estando INavigator como parte de ellos.
}

public void DoNextClicked()
{
// Idem
}
public void DoCurrentClicked()
{
// Idem
}
}

Entonces se definiran dos nuevas clases para aquellos Web Forms que requieran utilizar el comportamiento plasmado en ConcreteNavigator.

Asi, tendremos las clase UIPageEditNavigator que hereda de UIPageEdit utilizando una instancia de ConcreteNavigator.

public class UIPageEditNavigator<TForm, TAtom> :
UIPageEdit<TForm, TAtom>
where TAtom : ITransaction, IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>
{
private ConcreteNavigator<TForm, TAtom> _concrete;

protected virtual INavigator MyNatigator
{
get { throw new NotImplementedException(); }
}

protected override void DefineObjects()
{
this._concrete = new ConcreteNavigator<TForm, TAtom>(
this.ViewState,
this.MyNavigator,
this.MyForm,
this.MyAtom );
}

private void MyNavigator_PreviousClicked( Object sender, EventArgs e )
{
this._concrete.DoPreviousClicked();
}

// Otras tanta funcionalidades que se ajustan con
// lo soportado por ConcreteNavigator
}

De la misma forma, definimos la clase UIPageEditValidateNavigator que hereda de la clase UIPageEditValidate utilizando una instancia de ConcreteNavigator

public class UIPageEditValidateNavigator<TForm, TAtom> :
UIPageEditValidate<TForm, TAtom>
where TAtom : ITransaction, IHasHistory, IBuildHistory<TAtom>
where TForm : IFormEditHistory<TAtom>, IFormValidate
{
private ConcreteNavigator<TForm, TAtom> _concrete;

protected virtual INavigator MyNatigator
{
get { throw new NotImplementedException(); }
}

protected override void DefineObjects()
{
this._concrete = new ConcreteNavigator<TForm, TAtom>(
this.ViewState,
this.MyNavigator,
this.MyForm,
this.MyAtom );
}

private void MyNavigator_PreviousClicked( Object sender, EventArgs e )
{
this._concrete.DoPreviousClicked();
}

// Otras tanta funcionalidades que se ajustan con
// lo soportado por ConcreteNavigator
}


De esta manera, se supera la limitacion tecnica de la NO herencia multiple de clases inexistente en los lenguajes de .NET, como C#.

La clase ConcreteNavigator hace el trabajo pesado de una funcionalidad que luego es posible reflejar en diferentes clases, evitando el COPY & PASTE.

Al NO lograr este nivel de abstraccion, estaremos con seguridad, dejando de utilizar todas las bondades que la tecnologia nos brinda, hablamos de la POO.

Nuestro producto de software muy probablemente este disgregado de codigo, como consecuencia irresponsable del COPY & PASTE por everywhere.

Haciendo de este dolorasemente mantenible y vergonzosamente presentable :)

No comments: