기존 설계에 대한 수정이없는 OOP 설계

Aug 17 2020

질문은 ~이야:

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 클래스 자체를 수정할 권한이 없습니다.

답변

25 TulainsCórdova Aug 17 2020 at 15:07

데코레이터 패턴을 사용하여 서브 클래 싱없이 동물에 추가 책임을 추가 할 수 있습니다.


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());


    }

}

22 RobertBräutigam Aug 17 2020 at 14:39

작곡을위한 완벽한 시간. 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();
1 candied_orange Aug 17 2020 at 14:01

새 동작으로 새 클래스를 만듭니다. 그런 다음 수업 main에서 업데이트 하십시오 Test.

class Test {
    public static void main(String[] args) {
        Animal lion = new AnimalThatKeepsAMealLog();
        lion.eat();
        lion.eat();
        lion.eat();
    }
}

또는 테스트 파일을 읽고 전화를 건 횟수를 세십시오 eat(). 대답은 3이 될 것 같아요.

Shadov Aug 18 2020 at 23:35

다른 접근 방식으로, 몇 번만 수행되는 작업에 대해 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());
    }
}