기존 설계에 대한 수정이없는 OOP 설계
질문은 ~이야:
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 클래스를 확장하고 결과를 얻는 것이지만 클래스를 확장하는 모든 객체에 대해 이러한 클래스를 만들어야합니다.
이를 수행하는 최적화 된 방법이 있습니까?
게시 구독은 한 가지 방법이지만 인터페이스 나 Lion 클래스 자체를 수정할 권한이 없습니다.
답변
데코레이터 패턴을 사용하여 서브 클래 싱없이 동물에 추가 책임을 추가 할 수 있습니다.
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()
getter를 전혀 사용할 수없고 대신 생성자에 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()
. 대답은 3이 될 것 같아요.
다른 접근 방식으로, 몇 번만 수행되는 작업에 대해 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());
}
}