Programación orientada a interfaz, ¿cómo operarla?
Programación orientada a interfaces [zz]
En el capítulo anterior, mencionamos un ejemplo de diseño de interfaces. ¿Por qué defendemos el diseño de interfaces? Martin Fowler señaló en su libro Analysis Patterns que los problemas de análisis deberían estar en el nivel conceptual y no en el nivel de implementación. ¿Cuál es el nivel de conceptos? En pocas palabras, se trata de analizar qué debería hacer el objeto, en lugar de cómo debería hacerlo. El primero pertenece a la etapa de análisis y el segundo pertenece a la etapa de diseño o incluso de implementación. En ingeniería de requisitos, existe una cosa llamada tarjeta CRC, que se utiliza para analizar las responsabilidades y relaciones de las clases. De hecho, ese método consiste en realizar un diseño orientado a objetos desde un nivel conceptual. Por lo tanto, si desea analizar desde un nivel conceptual, debe observar cómo el programa representa conceptos en el mundo real desde la perspectiva de un experto en el dominio. La siguiente oración es un poco confusa. Desde la perspectiva de la implementación, el nivel de concepto corresponde al contrato y la forma de implementación del contrato incluye interfaces y clases base. En pocas palabras, el análisis a nivel conceptual consiste en diseñar una interfaz (o clase base) sin preocuparse por la implementación específica de la interfaz (la implementación se pospone a las subclases). Combinado con la discusión anterior, también podemos inferir que la interfaz debe ajustarse a los conceptos del mundo real.
Martin Fowler mencionó un ejemplo de este tipo en otro trabajo, que explica muy bien la idea de la programación de interfaces:
interface Person {
public String name( );
nombre público vacío (String newName);
salario público de dinero ();
salario público vacío (Dinero nuevoSalario);
p >public Money payAmount ();
public void makeManager ();
}
Ingeniero de interfaz extiende Persona{
public void numberOfPatents (valor int);
public int numberOfPatents ();
}
interfaz Vendedor extiende Persona{
public void numberOfSales (int numberOfSales);
public int numberOfSales ();
}
interface Manager extiende Person{
presupuesto vacío público ( Valor monetario);
Presupuesto monetario público ();
}
Como puede ver, para representar a las personas en el mundo real (aquí en realidad se refiere a empleados (conceptos), ingenieros, vendedores y gerentes. El código está diseñado con una jerarquía de herencia basada en los puntos de vista naturales de las personas y logra una buena reutilización. Además, podemos concluir que la interfaz es relativamente estable.
Echemos un vistazo a la parte de implementación:
clase pública PersonImpFlag implementa Persona, Vendedor, Ingeniero,Gerente{
// Implementación de Vendedor
Vendedor estático público nuevoSalesman (Nombre de cadena){
resultado PersonImpFlag;
resultado = nuevo PersonImpFlag (nombre);
resultado.makeSalesman();
devolver resultado ;
};
public void makeSalesman () {
_jobTitle = 1;
};
public boolean isSalesman () {
return _jobTitle == 1;
};
public void numberOfSales (valor int){
requireIsSalesman () ;
_numberOfSales = valor;
};
public int numberOfSales () {
requireIsSalesman ();
p>
return _numberOfSales;
};
private void requireIsSalesman () {
if (! isSalesman() ) throw new PreconditionViolation ("No soy vendedor") ;
};
private int _numberOfSales;
private int _jobTitle;
}
Este es uno de los métodos de implementación llamado Bandera Interna. Aquí solo damos un ejemplo. De hecho, tenemos muchas soluciones, pero no nos importa. Porque siempre que la interfaz sea lo suficientemente estable, se permiten cambios importantes en la implementación interna. Si está interesado en cómo implementarlo, puede consultar el artículo de Matrin Fowler sobre modelado de personajes o una de mis notas mientras lee este artículo.
A través del ejemplo anterior, podemos entender que el mayor beneficio de separar la interfaz y la implementación es que el código de implementación se puede modificar sin que el cliente lo sepa. Esta característica es muy adecuada para la tecnología de capas. Uno se utiliza para llamadas entre capas. Lo más tabú entre capas es un acoplamiento demasiado alto o cambios demasiado frecuentes. Las interfaces bien diseñadas pueden resolver este problema. El otro se utiliza en aquellas partes inestables. Si algunos requisitos son muy variables, definir interfaces también es una solución. Para dar un ejemplo inapropiado, una interfaz bien diseñada es como el enchufe universal que usamos todos los días. Se puede usar sin importar cómo cambie el enchufe.
Finalmente, me gustaría enfatizar que una buena definición de interfaz debe surgir de las necesidades, y definitivamente no es algo que los programadores se devanen los sesos para encontrar.