Encapsulamiento, herencia, polimorfismo y abstracción — herramientas y trampas de cada uno
class BankAccount { public double balance; public String owner; } // Cualquiera puede hacer: account.balance = -5000; account.owner = null; // No hay reglas de negocio posibles // El objeto no protege su integridad // → cualquier bug es un bug de estado
class BankAccount { private double balance; private final String owner; public void deposit(double amount) { if (amount <= 0) throw new IllegalArgumentException( "Amount must be positive"); this.balance += amount; } // balance solo cambia por reglas del negocio // owner es inmutable desde la creación public double getBalance() { return balance; } }
Stack extends ArrayList es el ejemplo clásico: Stack no ES un ArrayList. Resultado: la subclase hereda métodos que no debería tener (LSP roto).// Stack NO ES un ArrayList // Solo hereda para reutilizar lógica class Stack<T> extends ArrayList<T> { public T pop() { /* ... */ } public void push(T item) { /* ... */ } } // Ahora Stack tiene add(), remove(), // set(), get(index)… que NO son Stack. // Cualquier código que use ArrayList // puede romper Stack → LSP violado.
abstract class Shape { abstract double area(); void describe() { System.out.println("Area: " + area()); } } // Circle ES-UN Shape → IS-A real class Circle extends Shape { private final double radius; double area() { return Math.PI * radius * radius; } } // Circle puede sustituir a Shape siempre
if (obj instanceof Circle) para decidir comportamiento, estás negando el polimorfismo — el compilador ya lo puede resolver.void drawShape(Object shape) { if (shape instanceof Circle) { ((Circle) shape).drawCircle(canvas); } else if (shape instanceof Rectangle) { ((Rectangle) shape).drawRect(canvas); } else if (shape instanceof Triangle) { ((Triangle) shape).drawTri(canvas); } // nueva figura → modificar aquí siempre }
interface Drawable { void draw(Canvas canvas); } class Circle implements Drawable { public void draw(Canvas c) { /* círculo */ } } class Triangle implements Drawable { public void draw(Canvas c) { /* triángulo */ } } void drawShape(Drawable shape) { shape.draw(canvas); // dispatch automático } // nueva figura = nueva clase, nada más
// El servicio expone su implementación class NotificationService { public JavaMailSenderImpl mailSender; public Session smtpSession; public void sendRawMime(MimeMessage msg) { /* detalles SMTP */ } } // el cliente necesita saber de SMTP, // MimeMessage y JavaMail → acoplado
// Abstracción: solo expone intención interface NotificationPort { void notify(String to, String subject, String body); } // Implementación: oculta los detalles class SmtpNotification implements NotificationPort { public void notify(...) { // SMTP, MimeMessage, etc. aquí // el cliente no sabe nada de esto } }