Дизайн ООП с учетом отсутствия изменений существующего дизайна
Вопрос в том:
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();
}
}
Требуется рассчитать, сколько раз вызывается метод eat без изменения интерфейса и самого класса.
Один из способов - расширить класс льва и получить результаты, но для каждого объекта, расширяющего класс, нам придется создавать такие классы.
Есть ли какой-нибудь оптимизированный способ сделать это.
Публикация Подписка - это один из способов, но у нас нет разрешений на изменение интерфейса или самого класса Lion.
Ответы
Вы можете использовать шаблон декоратора для добавления дополнительных обязанностей к Animal без создания подклассов.

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());
}
}
ОБНОВИТЬ
Если мы хотим быть более точными в шаблоне декоратора, мы вообще не можем использовать getEatCount()
геттер, а вместо этого внедрить объект Counter в конструктор.
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());
}
}

Идеальное время для композиции. Создайте новую реализацию, Animal
которая выполняет подсчет, но также делегирует «настоящую» функцию. Как это:
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
}
}
Вам не нужно изменять какой-либо из существующих классов, и вы можете подключить его к любой реализации, Animal
которую захотите. Используйте этот способ:
Animal lion = new LoggingAnimal(new Lion());
lion.eat();
Создайте новый класс с новым поведением. Тогда обновите main
в Test
классе.
class Test {
public static void main(String[] args) {
Animal lion = new AnimalThatKeepsAMealLog();
lion.eat();
lion.eat();
lion.eat();
}
}
или просто прочтите тестовый файл и посчитайте, сколько раз вы звонили eat()
. Полагаю, ответ будет три.
Это своего рода другой подход, который позволит вам избежать использования 50 классов для задач, выполняемых всего несколько раз. Это более общий, очень полезный в технических аспектах, не так много в бизнесе, но все же может быть очень полезным.
btw Builder - это просто для иллюстрации, конечно, вам не обязательно так его использовать, здесь важны preEat / postEat
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());
}
}