Desactivar el comportamiento de objeto más allá del acceso a los datos
yTL;DR: Eliminar o reemplazar los getters con métodos ricos en comportamiento que realizan operaciones en lugar de exponer el estado interno.
y
TL;DR: Eliminar o reemplazar los getters con métodos ricos en comportamiento que realizan operaciones en lugar de exponer el estado interno.
Problemas solucionados
- y
- Objetos anémicos y
- Conjunto excesivo y
- La encapsulación perdida y
- Mutaciones esenciales y
- La ley de la violación de Demeter y
- Fuga de información y
- Expuestos internos y
- La obsesión primitiva y
Código relacionado Olor
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xiii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxv
Pasos
- y
- Identificar los getters que exponen el estado de objeto interno y
- Encuentra todos los usos de la codebase y
- Comportamiento de movimiento que utiliza el getter en el objeto mismo y
- Crear métodos de revelación de intenciones que realicen operaciones (suprimir el prefijo Get) y
- Actualiza tu código para utilizar los nuevos métodos y
Código de muestreo
Antes de
public class Invoice {
private List<LineItem> items;
private Customer customer;
private LocalDate dueDate;
public Invoice(Customer customer, LocalDate dueDate) {
this.customer = customer;
this.dueDate = dueDate;
this.items = new ArrayList<>();
}
public void addItem(LineItem item) {
// This is the right way
// to manipulate the internal consistency
// adding assertions and access control if necessary
items.add(item);
}
public List<LineItem> getItems() {
// You are exposing your internal implementation
// In some languages, you also open a backdoor to
// manipulate your own collection unless you return
// a copy
return items;
}
public Customer getCustomer() {
// You expose your accidental implementation
return customer;
}
public LocalDate getDueDate() {
// You expose your accidental implementation
return dueDate;
}
}
Invoice invoice = new Invoice(customer, dueDate);
// Calculate the total violating encapsulation principle
double total = 0;
for (LineItem item : invoice.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// Check if the invoice is overdue
boolean isOverdue = LocalDate.now().isAfter(invoice.getDueDate());
// Print the customer information
System.out.println("Customer: " + invoice.getCustomer().getName());
Después de 🙂
public class Invoice {
private List<LineItem> items;
private Customer customer;
private LocalDate dueDate;
public Invoice(Customer customer, LocalDate dueDate) {
this.customer = customer;
this.dueDate = dueDate;
this.items = new ArrayList<>();
}
public void addItem(LineItem item) {
items.add(item);
}
// Step 3: Move behavior that uses the getter into the object
public double calculateTotal() {
// Step 4: Create intention-revealing methods
double total = 0;
for (LineItem item : items) {
total += item.price() * item.quantity();
}
return total;
}
public boolean isOverdue(date) {
// Step 4: Create intention-revealing methods
// Notice you inject the time control source
// Removing the getter and breaking the coupling
return date.isAfter(dueDate);
}
public String customerInformation() {
// Step 4: Create intention-revealing methods
// You no longer print with side effects
// And coupling to a global console
return "Customer: " + customer.name();
}
// For collections, return an unmodifiable view if needed
// Only expose internal collaborators if the name
// is an actual behavior
public List<LineItem> items() {
return Collections.unmodifiableList(items);
}
// Only if required by frameworks
// or telling the customer is an actual responsibility
// The caller should not assume the Invoice is actually
// holding it
public String customerName() {
return customer.name();
}
// You might not need to return the dueDate
// Challenge yourself if you essentially need to expose it
// public LocalDate dueDate() {
// return dueDate;
// }
}
// Client code (Step 5: Update client code)
Invoice invoice = new Invoice(customer, dueDate);
double total = invoice.calculateTotal();
boolean isOverdue = invoice.isOverdue(date);
System.out.println(invoice.customerInformation());
Tipo
- y
- [x] Semi-automático y
Seguridad ️
Este refactoring es generalmente seguro, pero requiere una ejecución cuidadosa.
Usted debe asegurarse de que todos los usos del getter son identificados y reemplazados por los nuevos métodos de comportamiento.
El mayor riesgo ocurre cuando los getters devuelven objetos o colecciones mutables, ya que el código del cliente podría haber modificado estos objetos.
Debe comprobar que el comportamiento no ha cambiado a través de pruebas completas antes y después de la refactorización.
Para las colecciones, devuelva copias o vistas no modificables para mantener la seguridad durante la transición. Para los marcos que requieren acceso de propiedad, es posible que necesite conservar accesorios simples sin el prefijo "get" junto a sus métodos ricos en comportamiento.
Como de costumbre, debe agregar cobertura comportamental (no estructural) a su código antes de realizar el refactoring.
¿Por qué el código es mejor?
El código refactorizado es mejor porque se adhiere a laNo te preguntesPrincipio, haciendo que sus objetos sean inteligentes en lugar de sólo contenedores de datos anémicos.
La solución centraliza la lógica relacionada con los datos del objeto dentro del objeto mismo, reduciendo la duplicación. oculta los detalles de implementación, permitiendo cambiar la representación interna sin afectar al código del cliente
Este enfoque reduceConexiónLos clientes no necesitan conocer la estructura interna del objeto.
También evita las violaciones de la Ley de Demeter al eliminar las cadenas de getters.
Desde elLa esencia no se mueve, la solución permite una mejor validación y ejecución de reglas de negocio dentro del objeto.
¿Cómo mejorar la bijeción? ️
La eliminación de gérmenes mejora laBijeciónentre el código y la realidad haciendo que los objetos se comporten más como sus homólogos del mundo real.
En el mundo real, los objetos no exponen su estado interno para que otros manipulen; realizan operaciones basadas en solicitudes.
Por ejemplo, usted no pide a una cuenta bancaria su saldo y luego calcula si una retirada es posible usted mismo. En su lugar, le pregunta a la cuenta, "¿Puedo retirar $ 100?"
Crea una representación más fiel de los conceptos de dominio modelando sus objetos para realizar operaciones en lugar de exponer los datos.
Esto fortalece la correspondencia uno a uno entre el mundo real y su modelo computable, haciendo que su código sea más intuitivo y alineado con la forma en que la gente piensa sobre el dominio del problema.
Este enfoque sigue laMapaprincipio garantizando que los objetos computacionales reflejan las entidades del mundo real en estructura y comportamiento.
Limitaciones ☀️
Frameworks y bibliotecas a menudo esperan métodos de serialización/deserialización.
Las bases de código heredadas pueden tener un uso generalizado de getter que es difícil de refactor todo de una vez.
Las pruebas de unidad pueden ser más desafiantes a medida que el estado interno es menos accesible. Recuerde, nunca debe probar métodos privados.
Refactor con AI
yPrompt sugerido: 1. Identificar los getters que exponen el estado de objeto interno 2. Encontrar todos los usos de getter en la base de código 3. Mover el comportamiento que utiliza el getter en el objeto mismo 4. Crear métodos de revelación de intención que realicen operaciones (eliminar el prefixo de get) 5. Actualizar su código para usar los nuevos métodos
y
Prompt sugerido: 1. Identificar los getters que exponen el estado de objeto interno 2. Encontrar todos los usos de getter en la base de código 3. Mover el comportamiento que utiliza el getter en el objeto mismo 4. Crear métodos de revelación de intención que realicen operaciones (eliminar el prefixo de get) 5. Actualizar su código para usar los nuevos métodos
Sin instrucciones adecuadas
Con instrucciones específicas
Con instrucciones específicas
ChatGPT
ChatGPT
ChatGPT
Claude
Perplejidad
piloto
Gemini
profundidad
profundidad
El objetivo es
Grúas
Grúas
Tags ️
- y
- Encapsulación y
Nivel
- y
- [x] El Intermedio y
Reacciones relacionadas
Es decir,
Créditos
Este artículo forma parte de la serie Refactoring.