Conception POO ne considérant aucune modification de la conception existante
La question est:
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();
}
}
La condition est de calculer combien de fois la méthode eat est appelée sans modifier l'interface et la classe elle-même.
Une façon est d'étendre la classe lion et d'obtenir les résultats, mais pour chaque objet étendant la classe, nous devrons créer de telles classes.
Existe-t-il un moyen optimisé de le faire.
Publier S'abonner est un moyen, mais nous n'avons pas les autorisations pour modifier l'interface ou la classe Lion elle-même.
Réponses
Vous pouvez utiliser le modèle de décorateur pour ajouter des responsabilités supplémentaires à un animal sans sous-classer.
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());
}
}
METTRE À JOUR
Si nous voulons être plus fidèles au modèle Decorator, nous ne pouvons pas du tout utiliser le getEatCount()
getter, et à la place injecter un objet Counter dans le constructeur.
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());
}
}
Moment parfait pour la composition. Créez une nouvelle implémentation Animal
qui effectue le comptage, mais délègue également la fonction «réelle». Comme ça:
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
}
}
Vous n'avez pas à modifier l'une des classes existantes, et vous pouvez le brancher avec n'importe quelle implémentation de Animal
votre choix. Utilisez de cette façon:
Animal lion = new LoggingAnimal(new Lion());
lion.eat();
Créez une nouvelle classe avec le nouveau comportement. Ensuite, mettez main
à jour dans la Test
classe.
class Test {
public static void main(String[] args) {
Animal lion = new AnimalThatKeepsAMealLog();
lion.eat();
lion.eat();
lion.eat();
}
}
ou lisez simplement le fichier Test et comptez le nombre de fois que vous avez appelé eat()
. Je suppose que la réponse sera trois.
Une sorte d'approche différente, qui vous permettra d'éviter d'avoir 50 classes pour des tâches effectuées seulement quelques fois. C'est un plus générique, très utile dans les aspects techniques, pas autant dans les affaires, peut encore être très utile.
btw Builder est juste pour illustrer, bien sûr, vous n'avez pas à l'utiliser comme ça, preEat / postEat sont importants ici
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());
}
}