Programación: "Orientada a la Implementación" versus "Orientada a la Interface" - Parte 1 (revisado 11/12/2008)

Gran dilema les voy a plantear. Acompáñenme al esotérico mundo del Diseño Orientado a Objetos ;-)

Supongamos que tenemos un sistema que hará las funciones de "máquina de escribir", donde obtendrá información a través de un dispositivo de entrada y luego enviará esta información a un dispositivo de salida.

Nuestro diseño estará compuesto por dos paquetes (en el contexto UML) que nos ofrecen los siguientes servicios:

Lógica: donde tendremos la implementación de "alto nivel" de nuestro problema (donde se manipularán los dispositivos).

Dispositivos: donde la implementación de nuestro paquete de "lógica" tendrá acceso a servicios de "bajo nivel" provisto por este paquete.


Entrando en el diseño interno de los paquetes, tendremos:

- Dentro del paquete "Dispositivos": las clases "Teclado", que retornará información en formato de texto, e "Impresora", que en algún momento recibirá un texto para ser impreso.

- Dentro del paquete "Lógica": la clase "MaquinaDeEscribir", que se encargará de crear las instancias correspondientes a los dispositivos de entrada/salida, y pasar la información que devuelve una clase a otra clase.

Imprementación

Seguiremos textualmente los diagramas UML para hacer la codificación de nuestras clases. Empezaremos con los dispositivos.

Archivo: Teclado.php

class Teclado
{
public function leer()
{
// a efectos del ejemplo, solo retornará un
// un texto como representación de una
// entrada de datos
return "texto ingresado";
}
}


Archivo: Impresora.php

class Impresora
{
function escribir($texto)
{
echo $texto;
}
}


Archivo: MaquinaDeEscribir.php


require_once 'dispositivos/Teclado.php';
require_once 'dispositivos/Impresora.php';

abstract class MaquinaDeEscribir
{
static function run()
{
$miTeclado = new Teclado();
$miImpresora = new Impresora();

$miImpresora->escribir($miTeclado->leer());
}
}

¿Cómo se prueba?

Una posible solución creativa, tratando de llevar a PHP hacia un lenguaje 100% Orientado a Objetos (como Java), donde *todo* debería ser un objeto, se usará el "index.php" como una clase "Index".

Nota1: siguiendo el comportamiento estándar de los servidores web, cuando nos posicionemos en la url de nuestro sitio el servidor buscará todos los documentos que inicien con el nombre "index" (minúsculas) y luego probará varias extensiones hasta dar con el archivo inicial (.html, .htm, .php, etc). Hay autores que usan "class.NombreClase.php" o NombreClase.class.php, nosotros usaremos el estádar definido por Zend (NombreClase.php) y para este caso particular simplemente "index.php".

Resumiendo: Solo en este caso, para mantener el estándar web, nuestro archivo se llamará "index.php" y la clase internamente será "Index" (la primer letra en mayúsculas y la palabra en singular).

Nota2: cuando en un diagrama UML tenemos una flecha que apunta hacia otra clase (cualquier flecha), en PHP se traduce *siempre* (o por lo menos hasta la fecha de hoy) en incluir ese fuente con la sentencia include, require, etc. En el caso siguiente, y basado en nuestro diagrama UML, nuestra clase "Index" requerirá *solo* de la clase "MaquinaDeEscribir".

Nota3: como no hace falta instanciar la clase para invocar un método, creo una clase "abstracta" y ejecuto directamente la clase e invoco su método, evitando crear una instancia (menos código, no reservo memoria para la instancia, etc).


require_once "logica/MaquinaDeEscribir.php";

abstract class Index
{
static public function run()
{
echo 'Ejecuto máquina de escribir->';
MaquinaDeEscribir::run();
}
}

Index::run();

Ok, nuestro ejemplo está funcionando.

Ahora la pregunta es: ¿existe alguna otra mejor forma de hacerlo?

Programación Orientada a la Interface

Por suerte sí, existe otra forma: usando interfaces. Para poder aplicar esta nueva forma de solucionar el problema, debemos "dar vuelta" la forma que usamos los objetos de nuestro ejemplo. En el caso anterior, la clase de "alto nivel" dependía de dos clases de "bajo nivel", lo que significa algo muy grave: "la clase de alto nivel es *dependiente* de los cambios que puedan sucederle a las clases de bajo nivel, es decir, nuestro alto nivel depende de los detalles de implementación del bajo nivel".

El problema de nuestro diseño es que estamos programando "orientado a la implementación".

¿El diseño anterior, puede considerarse un buen diseño?

  • ¿Cuan mantenible será nuestro sistema si elementos de bajo nivel pueden generar cambios en todo el modelo?
  • ¿Es correcto que nuestra MaquinaDeEscribir dependa de cómo se toman los datos y de cómo se imprimen?
  • ¿Qué es lo más importante en nuestro diseño? ¿la Lógica o los Dispositivos?
  • ¿Este diseño, es reutilizable? ¿podemos reutilizar el MaquinaDeEscribir en otros contextos?
  • ¿Su costo de mantenimiento es alto o bajo? ¿cuanto código hay que agregar para extender su comportamiento (para cubrir un nuevo requerimiento)?
  • ¿el código presentado se mantiene, o hay que adaptarlo cada vez que queramos extender su funcionamiento?

Todas estas preguntas serán desveladas en el próximo capítulo ;-)

5 comentarios:

Anónimo dijo...

Hola, estoy deseando leer la segunda parte. Creo que empieza a gustarme eso de la programación OOP, hasta ahora solo daba "credibilidad" a la programación altamente estructurada con estructuras complejas como el C.

Muy buen artículo.

Saludos,
fegor.
http://www.fegor.com

enrique_place dijo...

El tema es que estos conceptos no son nuevos; son usados más frecuentemente en ambientes que hace años trabajan con la OOP (C++, Java, .Net, etc).

La gran ventaja de PHP5 es que el soporte a la orientación a objetos a subido a niveles *similares* a estos lenguajes 100% orientados a objetos, permitiendo que podamos subir de nivel nuestra programación.

Lo que trato de transmitir con mis artículos a los programadores de PHP es que existen otros mundos, no es todo "programación scripting", no todo es "programación estructurada", no todo es "programación estructurada usando algunos objetos" ... actualmente se puede empezar (con un poco de creatividad) a subir el nivel de nuestros diseños.

Nos quedan ver otros patrones de diseño, recorrer principios de diseño, etc.

¡Sigamos adelante!

Andrés Guzmán dijo...

excelente, muy buen articulo.

Muy agradecido y espero con ansias el 2º capitulo.

Alfredo Alonso dijo...

Enrique,
Primero que nada, muy buen artículo. Me parece muy buena tu colaboración para incentivar un aprogramación con fundamentos sólidos orientados a objetos.
¿Escribiste ya la segunda parte? ;-)

Quería remarcar dos cosas del código.

Primero
En la clase Index hacés llamado a Copy::escribir(), método que no existe dentro de la clase Copy.
Si lo cambio por Copy::copy() me genera el segundo punto que te escribo.

Segundo
Al intentar implementar tu código en mi computadora (la del trabajo en realidad), cuando queria ejecutarlo (haciendo el cambio que indicqué arriba), PHP5 me mostraba el siguiente error: 'Fatal error: Constructor Copy::copy() cannot be static in D:\xampplite\htdocs\test\logica\Copy.php on line 15'

Investigué en la documentación de PHP y me enteré que por cuestiones de compatibilidad con PHP4, si una clase no tiene declarado el método __construct, PHP5 verifica si existe un método con el mismo nombre que la clase para utilizarlo como constructor.

En resumen
Creo que hice mucho lio =p
Supongo que hay que cambiar el nombre del método copy de la clase Copy por el nombre escribir.

enrique_place dijo...

Estimado Alfredo:

> Primero que nada, muy buen
> artículo. Me parece muy buena tu
> colaboración para incentivar un
> aprogramación con fundamentos
> sólidos orientados a objetos.

En sí no es nada del otro mundo si venimos de Java, pero bueno, en el mundo de los ciegos el tuerto soy yo ;-)

> ¿Escribiste ya la segunda parte? ;-)

Se las mandé de deberes terminar a todos los alumnos de los talleres de POO PHP5 ;-)

> Primero

> En la clase Index hacés llamado
> a Copy::escribir(), método que
> no existe dentro de la clase
> Copy.
> Si lo cambio por Copy::copy() me
> genera el segundo punto que te
> escribo.

Sí, fue un error involuntario, en sí sería Copy::run() (ya está corregido en el post).

> Segundo

> Al intentar implementar tu
> código en mi computadora (la del
> trabajo en realidad), cuando
> queria ejecutarlo (haciendo el
> cambio que indicqué arriba),
> PHP5 me mostraba el siguiente
> error: 'Fatal error: Constructor
> Copy::copy() cannot be static in
> D:\xampplite\htdocs\test\logica
> \Copy.php on line 15'

> Investigué en la documentación
> de PHP y me enteré que por
> cuestiones de compatibilidad con
> PHP4, si una clase no tiene
> declarado el método __construct,
> PHP5 verifica si existe un
> método con el mismo nombre que
> la clase para utilizarlo como
> constructor.

Muy interesante ;-) Sí, es verdad, en PHP5 aún funciona el constructor de PHP4 que sería hacer un método con el mismo nombre de la clase, aunque debería ser en mayúsculas y no en minúsculas.

> En resumen

> Creo que hice mucho lio =p
> Supongo que hay que cambiar el
> nombre del método copy de la
> clase Copy por el nombre
> escribir.

Ya quedó solucionando como te comenté, lástima que te estoy respondiendo tan tarde ;-)

Dentro de poco, cuando termine con los talleres de este año, escribiré la segunda parte, que por suerte, los alumnos del taller en su mayoría pudieron completarla solos ;-)

Saludos!

Entradas populares