Donde la programación orientada a objetos se encuentra con los sabores más deliciosos. Explora nuestros servicios y aprende conceptos de POO de manera práctica.
La herencia permite que las clases compartan características comunes. En nuestro sistema, los productos heredan de una clase base MenuItem.
public abstract class MenuItem implements Identifiable, Validatable, Summarizable {
protected String name;
protected double basePrice;
// ...métodos abstractos...
}
// IceCreamCup y Milkshake heredan de MenuItem
public class IceCreamCup extends MenuItem {
private CupSize size;
private List<Scoop> scoops;
private List<Topping> toppings;
@Override
public double totalPrice() {
double toppingsPrice = toppings.size() * 0.5;
return basePrice + toppingsPrice;
}
}
Ubicación: Se encuentra en el archivo MenuItem.java. Las clases IceCreamCup y Milkshake heredan de MenuItem, compartiendo propiedades como name y basePrice.
El polimorfismo permite que objetos de diferentes tipos respondan al mismo mensaje de manera específica. Cada producto implementa los métodos de forma diferente.
// En IceCreamCup.java
@Override
public double totalPrice() {
double toppingsPrice = toppings.size() * 0.5;
return basePrice + toppingsPrice;
}
@Override
public String summarize() {
return String.format("%s (%s) - %d scoops, %d toppings - $%.2f",
name, size.name(), scoops.size(), toppings.size(), totalPrice());
}
// En Milkshake.java
@Override
public double totalPrice() {
return basePrice; // Implementación diferente
}
@Override
public String summarize() {
return String.format("%s (%s flavor) - $%.2f", name, flavor.name(), totalPrice());
}
Ubicación: Una implementación se encuentra en Milkshake.java y la otra en IceCreamCup.java. Ambas clases implementan los mismos métodos pero con comportamiento específico.
La asociación representa una relación entre clases donde una clase usa o interactúa con otra, pero ambas pueden existir independientemente.
// En Order.java
private final Customer customer;
public class Order {
private String id;
private Customer customer; // Asociación con Customer
private List<OrderLine> lines;
public Order(String id, Customer customer) {
this.customer = customer; // El Customer existe independientemente
this.lines = new ArrayList<>();
}
}
// En OrderLine.java
private final MenuItem item;
public class OrderLine {
private MenuItem item; // Asociación con MenuItem
private int quantity;
public OrderLine(MenuItem item, int quantity) {
this.item = item; // El MenuItem existe independientemente
this.quantity = quantity;
}
}
Ubicación: private final Customer customer; (en Order.java) y private final MenuItem item; (en OrderLine.java). Ambas clases pueden existir independientemente.
La composición es una relación fuerte donde una clase contiene objetos de otra clase y controla su ciclo de vida completamente.
// En Order.java
private final List<OrderLine> lines = new ArrayList<>();
public class Order {
private List<OrderLine> lines; // Composición fuerte
public Order(String id, Customer customer) {
this.lines = new ArrayList<>(); // Order crea las OrderLines
}
public void addLine(MenuItem item, int quantity) {
OrderLine line = new OrderLine(item, quantity);
this.lines.add(line); // Order controla las OrderLines
}
// Si Order se destruye, las OrderLines también
}
// En IceCreamCup.java
private final List<Scoop> scoops = new ArrayList<>();
public class IceCreamCup extends MenuItem {
private List<Scoop> scoops; // Composición con Scoops
public void addScoop(Flavor flavor) {
Scoop scoop = new Scoop(flavor); // IceCreamCup crea los Scoops
this.scoops.add(scoop);
}
}
Ubicación: private final List<OrderLine> lines = new ArrayList<>(); (en Order.java) y private final List<Scoop> scoops = new ArrayList<>(); (en IceCreamCup.java).
La agregación es una relación más débil donde una clase contiene referencias a objetos de otra clase, pero no controla completamente su ciclo de vida.
// En IceCreamCup.java
private final List<Topping> toppings = new ArrayList<>();
public class IceCreamCup extends MenuItem {
private List<Topping> toppings; // Agregación
public IceCreamCup(String id, String name, double price, CupSize size) {
this.toppings = new ArrayList<>();
}
public void addTopping(Topping topping) {
this.toppings.add(topping); // Los toppings pueden existir independientemente
}
public void removeTopping(Topping topping) {
this.toppings.remove(topping); // Relación más débil
}
}
Ubicación: private final List<Topping> toppings = new ArrayList<>(); (en IceCreamCup.java). Los toppings pueden conceptualmente existir sin la copa.
La encapsulación oculta los detalles internos de implementación y expone solo lo necesario a través de métodos públicos controlados.
// En Customer.java
public class Customer implements Identifiable, Validatable {
private final String id; // ✅ Atributos PRIVADOS
private String name; // ✅ No acceso directo desde fuera
private String email; // ✅ Protegidos del acceso externo
// ✅ Acceso controlado a través de métodos públicos
@Override
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Ubicación: En Customer.java. Todos los atributos son privados y el acceso se controla mediante métodos públicos getter y setter con validación.