OOP Design non considerando modifiche al design esistente
La domanda è:
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();
}
}
Il requisito è calcolare quante volte viene chiamato il metodo eat senza modificare l'interfaccia e la classe stessa.
Un modo è estendere la classe lion e ottenere i risultati, ma per ogni oggetto che estende la classe dovremo creare tali classi.
C'è un modo ottimizzato per farlo.
Pubblica Sottoscrivi è un modo, ma non abbiamo i permessi per modificare l'interfaccia o la classe Lion stessa.
Risposte
È possibile utilizzare il motivo Decorator per aggiungere ulteriori responsabilità a un animale senza sottoclassi.
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());
}
}
AGGIORNARE
Se vogliamo essere più fedeli al Decorator Pattern non possiamo usare affatto il getEatCount()getter, e invece iniettare un oggetto Counter nel costruttore.
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 perfetto per la composizione. Creare una nuova implementazione Animalche faccia il conteggio, ma deleghi anche la funzione "reale". Come questo:
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
}
}
Non è necessario modificare nessuna delle classi esistenti e puoi collegarla insieme a qualsiasi implementazione Animaldesideri. Usa in questo modo:
Animal lion = new LoggingAnimal(new Lion());
lion.eat();
Crea una nuova classe con il nuovo comportamento. Quindi aggiorna mainin Testclasse.
class Test {
public static void main(String[] args) {
Animal lion = new AnimalThatKeepsAMealLog();
lion.eat();
lion.eat();
lion.eat();
}
}
oppure leggi semplicemente il file Test e conta il numero di volte che hai chiamato eat(). Immagino che la risposta sarà tre.
Una specie di approccio diverso, che ti permetterà di evitare di avere 50 lezioni per attività svolte solo poche volte. È uno più generico, molto utile negli aspetti tecnici, non tanto in quelli aziendali, può comunque essere molto utile.
btw Builder è solo per illustrare, ovviamente non devi usarlo in questo modo, preEat / postEat sono importanti qui
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());
}
}