Diseño de OOP sin considerar modificaciones al diseño existente
La pregunta es:
interface Animal {
void eat();
}
class Lion implements Animal{
public void eat(){
//do somethng
}
}
class Test {
public static void main(String[] args) {
Animal lion = new Lion();
lion.eat();
lion.eat();
lion.eat();
}
}
El requisito es calcular cuántas veces se llama al método eat sin modificar la interfaz y la clase en sí.
Una forma es extender la clase lion y obtener los resultados, pero para cada objeto que extienda la clase tendremos que crear tales clases.
¿Hay alguna forma optimizada de hacer esto?
Publicar Suscribirse es una forma, pero no tenemos los permisos para modificar la interfaz o la clase Lion en sí.
Respuestas
Puede usar el patrón de decorador para agregar responsabilidades adicionales a un animal sin subclases.

public interface Animal {
void eat();
}
public class Lion implements Animal {
public void eat() {
// do something
}
}
/* In the original Decorator pattern,
the decorator is an abstract class,
but for the sake of brevity,
in this example it's a concrete class. */
public class AnimalWithEatCountDecorator implements Animal {
private Animal animalWeWantToCountEats;
private int eatCount=0;
public AnimalWithEatCountDecorator(Animal animal) {
this.animalWeWantToCountEats= animal;
}
public void eat(){
this.animalWeWantToCountEats.eat();
this.eatCount++;
}
public int getEatCount() {
return this.eatCount;
}
}
public class Test {
public static void main(String[] args) {
AnimalWithEatCountDecorator lion = new AnimalWithEatCountDecorator(new Lion());
lion.eat();
lion.eat();
lion.eat();
System.out.println(lion.getEatCount());
}
}
ACTUALIZAR
Si queremos ser más fieles al Patrón Decorador, no podemos usar el getEatCount()
getter en absoluto, y en su lugar inyectar un objeto Counter en el constructor.
public interface Counter {
public void increment();
public int getCount();
}
/* I will omit the trivial implementation of Counter */
public class AnimalWithEatCountDecorator implements Animal {
private Animal animalWeWantToCountEats;
private Counter counterThingy;
public AnimalWithEatCountDecorator(Animal animal, Counter counterThingy) {
this.animalWeWantToCountEats= animal;
this.counterThingy=counterThingy;
}
public void eat(){
this.animalWeWantToCountEats.eat();
this.counterThingy.increment();;
}
}
public class Test {
public static void main(String[] args) {
Counter counterThingy = new CounterThingy();
AnimalWithEatCountDecorator lion =
new AnimalWithEatCountDecorator(new Lion(), counterThingy);
lion.eat();
lion.eat();
lion.eat();
System.out.println(counterThingy.getCount());
}
}

Momento perfecto para la composición. Crea una nueva implementación Animal
que hace el conteo, pero también delega la función "real". Me gusta esto:
public final class LoggingAnimal implements Animal {
private final Animal delegate;
private int eatCount = 0;
public LoggingAnimal(Animal delegate) {
this.delegate = delegate;
}
@Override
public void eat() {
eatCount++;
delegate.eat();
log("Animal ate {} times", eatCount); // Pseudo-functionality
}
}
No tiene que modificar ninguna de las clases existentes, y puede conectar esto junto con cualquier implementación Animal
que desee. Use de esta manera:
Animal lion = new LoggingAnimal(new Lion());
lion.eat();
Crea una nueva clase con el nuevo comportamiento. Luego actualice main
en la Test
clase.
class Test {
public static void main(String[] args) {
Animal lion = new AnimalThatKeepsAMealLog();
lion.eat();
lion.eat();
lion.eat();
}
}
o simplemente lea el archivo de prueba y cuente la cantidad de veces que llamó eat()
. Supongo que la respuesta será tres.
Una especie de enfoque diferente, que te permitirá evitar tener 50 clases para tareas que solo se realizan unas pocas veces. Es más genérico, muy útil en los aspectos técnicos, no tanto en los comerciales, aunque puede ser muy útil.
Por cierto Builder es solo para ilustrar, por supuesto que no tiene que usarlo así, preEat / postEat son importantes aquí
class PointcutAnimal implements Animal {
private Runnable preEat;
private Runnable postEat;
@NonNull
private Animal downstream;
@Override
public void eat() {
if(preEat != null)
preEat.run();
downstream.eat();
if(postEat != null)
postEat.run();
}
}
class Test {
public static void main(String[] args) {
AtomicInteger eatCount = new AtomicInteger(0);
Animal lion = PointcutAnimal.builder(new Lion())
.postEat(eatCount::getAndIncrement)
.build();
lion.eat();
lion.eat();
lion.eat();
System.out.println(eatCount.get());
}
}