veamos la clase, por ejemplo Report
public class Report { public byte[] getBytes(File report) { if(report != null) { if (report.exists()) { return getBytesFromFile(report); } else { throw new FotoEmpleadoInvalida("The report doesn't exists"); } } else { throw new IllegalArgumentException("Unknown report"); } } private byte[] getBytesFromFile(File report) { return new byte[0]; } }
en esta clase se tiene el método getBytes con el que se obtiene un arreglo de bytes que contiene el reporte listo para ser descargado, como vemos, se hacen ciertas validaciones antes de que se genere y descargue el arreglo de bytes, parece todo bien no? pues entonces de lo que hablamos antes, Single responsability, en este caso esta clase hace validaciones y la conversión y obtención del arreglo de bytes del reporte, pues bien, acá es donde podemos aplicar el patrón decorador para separar las validaciones de lo demás quedando las clases de la siguiente forma:
public interface Report { public byte[] getBytes(File report); }
public class NormalReport implements Report { @Override public byte[] getBytes(File report) { return getBytesFromFile(report); } private byte[] getBytesFromFile(File report) { return new byte[0]; } }public class ReportWithFileExistsValidarion implements Report { private final Report report; public ReportWithFileExistsValidarion(Report report) { this.report = report; } @Override public byte[] getBytes(File file) { if (file.exists()) { return this.report.getBytes(file); } else { throw new FotoEmpleadoInvalida("The report doesn't exists"); } } }public class ReportWithNullValidation implements Report { private final Report report; public ReportWithNullValidation(Report report) { this.report = report; } @Override public byte[] getBytes(File file) { if (file != null) { return this.report.getBytes(file); } else { throw new IllegalArgumentException("Unknown report"); } } }
y la forma en que se usan estas clases es:
public class Main { public static final void main(String... args) { Report report = new ReportWithNullValidation( new ReportWithFileExistsValidarion( new NormalReport())); byte[] reportInBytes = report.getBytes(new File("report.csv")); } }
como se ve, se crea una interfaz que define que método debe de existir, y de allí se definen 3 implementaciones de esa interfaz, en donde una de ellas es la que hace el "negocio" de obtener y transformar el reporte y esa es su única responsabilidad, mientras que las otras dos implementaciones se dedican exclusivamente a hacer la validación que le corresponde a cada una, pero estas dos implementaciones a diferencia de la primera reciben en su constructor un Report, con lo que se puede ir haciendo una cadena de validaciones hasta llegar al "negocio" y así poder ejecutar la ación esperada.
A esto que sucede acá es a lo que se llama "decoración" ya que se tiene un "negocio" que se quiere hacer y que está encapsulada en una clase específica y luego esa clase se "decora" con otra que se dedica a hacer la validación de existencia del archivo y luego esa clase se "decora" con la que se encarga de hacer la validación de que le parámetro no sea nulo, así aplicando este patrón se logra que cada una de las clases tenga una responsabilidad única.
Pues bien, chingón, ya se tiene todo bien separado y demás, pero ahora ¿cuál es la ventaja de tener las clases así? pues bien aunque al parecer es más código, esto nos da la ventaja de que las clases sólo cambian por una única razón por lo que posibles errores y cambios son mas localizados y si se quiere se pueden agregar mas validaciones con más decoraciones de ser necesario, por ejemplo si ahora nos dicen que sólo los reportes con extensión .txt son soportados basta con hacer el nuevo decorador y unirlo a la cadena de llamadas o si se encuentra un bug en como obtener el arregla de bytes del archivo pues se sabe específicamente en que clase se debe de hacer el cambio. Otra gran ventaja es la testeabilidad ya que al depender de una interfaz solamente, en las clases de tests se pueden crear mocks y pasarlos como parámetros y así poder testear cada parte por separado.
No hay comentarios.:
Publicar un comentario