mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 20:58:35 +00:00
135 lines
4.4 KiB
Markdown
135 lines
4.4 KiB
Markdown
---
|
|
title: Separated Interface
|
|
shortTitle: Separated Interface
|
|
category: Structural
|
|
language: es
|
|
tag:
|
|
- Decoupling
|
|
---
|
|
|
|
|
|
## Propósito
|
|
|
|
Separe la definición de la interfaz y su implementación en paquetes diferentes. Esto permite al cliente desconozca por completo la implementación.
|
|
|
|
## Explicación
|
|
|
|
Ejemplo del mundo real
|
|
|
|
> Se puede crear un generador de facturas con capacidad para utilizar diferentes calculadoras de impuestos que se pueden añadir en la factura en función del tipo de compra, región, etc.
|
|
|
|
En pocas palabras
|
|
|
|
> El patrón de interfaz separada anima a mantener las implementaciones de una interfaz desacopladas del cliente y su definición, para que el cliente no dependa de la implementación.
|
|
|
|
Un código cliente puede abstraer algunas funcionalidades específicas a una interfaz, y definir la definición de
|
|
la interfaz como una SPI ([Service Programming Interface](https://en.wikipedia.org/wiki/Service_provider_interface)
|
|
es una API pensada y abierta para ser implementada o ampliada por terceros). Otro paquete puede
|
|
implementar esta definición de interfaz con una lógica concreta, que se inyectará en el código del cliente en tiempo de ejecución (con un tercero).
|
|
cliente en tiempo de ejecución (con una tercera clase, inyectando la implementación en el cliente) o en tiempo de compilación
|
|
(utilizando el patrón Plugin con algún archivo configurable).
|
|
|
|
**Ejemplo programático**
|
|
|
|
**Cliente**
|
|
|
|
La clase `InvoiceGenerator` acepta el coste del producto y calcula el importe total a pagar, impuestos incluidos.
|
|
total a pagar, impuestos incluidos.
|
|
|
|
```java
|
|
public class InvoiceGenerator {
|
|
|
|
private final TaxCalculator taxCalculator;
|
|
|
|
private final double amount;
|
|
|
|
public InvoiceGenerator(double amount, TaxCalculator taxCalculator) {
|
|
this.amount = amount;
|
|
this.taxCalculator = taxCalculator;
|
|
}
|
|
|
|
public double getAmountWithTax() {
|
|
return amount + taxCalculator.calculate(amount);
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
La lógica de cálculo de impuestos se delega en la interfaz `TaxCalculator`.
|
|
|
|
```java
|
|
public interface TaxCalculator {
|
|
|
|
double calculate(double amount);
|
|
|
|
}
|
|
```
|
|
|
|
**Paquete de aplicación**
|
|
|
|
En otro paquete (que el cliente desconoce por completo) existen múltiples implementaciones
|
|
de la interfaz `TaxCalculator`. Una de ellas es `ForeignTaxCalculator`, que aplica un impuesto del 60
|
|
para los productos internacionales.
|
|
|
|
```java
|
|
public class ForeignTaxCalculator implements TaxCalculator {
|
|
|
|
public static final double TAX_PERCENTAGE = 60;
|
|
|
|
@Override
|
|
public double calculate(double amount) {
|
|
return amount * TAX_PERCENTAGE / 100.0;
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
Otra es `DomesticTaxCalculator`, que grava con un 20% los productos internacionales.
|
|
|
|
```java
|
|
public class DomesticTaxCalculator implements TaxCalculator {
|
|
|
|
public static final double TAX_PERCENTAGE = 20;
|
|
|
|
@Override
|
|
public double calculate(double amount) {
|
|
return amount * TAX_PERCENTAGE / 100.0;
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
Estas dos implementaciones son instanciadas e inyectadas en la clase cliente por la clase `App.java`.
|
|
en la clase cliente.
|
|
|
|
```java
|
|
var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST, new ForeignTaxCalculator());
|
|
|
|
LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax());
|
|
|
|
var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTaxCalculator());
|
|
|
|
LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax());
|
|
```
|
|
|
|
## Diagrama de clases
|
|
|
|

|
|
|
|
## Aplicabilidad
|
|
|
|
Utilice el patrón de interfaz separada cuando
|
|
|
|
* Estás desarrollando un paquete de framework, y tu framework necesita llamar a algún código de aplicación a través de interfaces.
|
|
* Tienes paquetes separados que implementan las funcionalidades que pueden ser conectadas a tu código cliente en tiempo de ejecución o de compilación.
|
|
* Su código reside en una capa a la que no se le permite llamar a la capa de implementación de la interfaz por norma. Por ejemplo, una capa de dominio necesita llamar a un mapeador de datos.
|
|
|
|
## Tutoriales
|
|
|
|
* [Separated Interface Tutorial](https://www.youtube.com/watch?v=d3k-hOA7k2Y)
|
|
|
|
## Créditos
|
|
|
|
* [Martin Fowler](https://www.martinfowler.com/eaaCatalog/separatedInterface.html)
|
|
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=e08dfb7f2cf6153542ef1b5a00b10abc)
|