OOP Tasarımı, mevcut tasarımda hiçbir değişiklik yapılmadığını düşünür

Aug 17 2020

Soru:

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

Gerekli olan, arayüzü ve sınıfın kendisini değiştirmeden eat yönteminin kaç kez çağrıldığını hesaplamaktır.

Bunun bir yolu, aslan sınıfını genişletmek ve sonuçları elde etmektir, ancak sınıfı genişleten her nesne için bu tür sınıflar oluşturmamız gerekecek.

Bunu yapmanın optimize edilmiş bir yolu var mı?

Yayınlama Aboneliği bir yoldur, ancak arayüzü veya Lion sınıfının kendisini değiştirme iznine sahip değiliz.

Yanıtlar

25 TulainsCórdova Aug 17 2020 at 15:07

Alt sınıflandırma yapmadan bir Hayvana ek sorumluluklar eklemek için Dekoratör Modeli'ni kullanabilirsiniz.


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

}

GÜNCELLEME

Dekoratör Modeline daha sadık olmak istiyorsak, getEatCount()alıcıyı hiç kullanamayız ve bunun yerine yapıcıya bir Sayaç nesnesi enjekte ederiz .

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

Kompozisyon için mükemmel bir zaman. Bunun yeni bir uygulamasını oluşturun Animal, ancak saymayı yapar, ancak aynı zamanda "gerçek" işlevi delege eder. Bunun gibi:

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
   }
}

Mevcut sınıflardan herhangi birini değiştirmeniz gerekmez ve Animalbunu istediğiniz herhangi bir uygulama ile birleştirebilirsiniz . Bu şekilde kullanın:

Animal lion = new LoggingAnimal(new Lion());
lion.eat();
1 candied_orange Aug 17 2020 at 14:01

Yeni davranışla yeni bir sınıf oluşturun. Ardından güncelleme mainde Testsınıfta.

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

veya sadece Test dosyasını okuyun ve kaç kez aradığınızı sayın eat(). Tahminimce cevap üç olacak.

Shadov Aug 18 2020 at 23:35

Sadece birkaç kez yapılan görevler için 50 sınıftan kaçınmanıza izin verecek farklı bir yaklaşım. Daha genel bir yöntemdir, teknik açıdan çok yararlıdır, iş dünyasında olduğu kadar değil, yine de çok yararlı olabilir.

btw Builder sadece göstermek içindir, tabii ki bu şekilde kullanmak zorunda değilsiniz, burada preEat / postEat önemlidir

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