Desain OOP mempertimbangkan tidak ada modifikasi pada desain yang sudah ada

Aug 17 2020

Pertanyaannya adalah:

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

Persyaratannya adalah menghitung berapa kali metode makan dipanggil tanpa memodifikasi antarmuka dan kelas itu sendiri.

Salah satu caranya adalah dengan memperluas kelas singa dan mendapatkan hasil tetapi untuk setiap objek yang memperluas kelas kita harus membuat kelas seperti itu.

Apakah ada cara yang dioptimalkan untuk melakukan ini.

Publikasikan Berlangganan adalah salah satu cara tetapi kami tidak memiliki izin untuk mengubah antarmuka atau kelas Lion itu sendiri.

Jawaban

25 TulainsCórdova Aug 17 2020 at 15:07

Anda dapat menggunakan Pola Dekorator untuk menambahkan tanggung jawab tambahan pada Hewan tanpa subclassing.


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

}

MEMPERBARUI

Jika kita ingin lebih setia pada Pola Dekorator kita tidak bisa menggunakan getEatCount()getter sama sekali, dan sebagai gantinya menyuntikkan objek Counter di konstruktor.

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

Waktu yang tepat untuk komposisi. Buat implementasi baru Animalyang melakukan penghitungan, tetapi juga mendelegasikan fungsi "nyata". Seperti ini:

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

Anda tidak perlu mengubah kelas yang ada, dan Anda dapat menyambungkannya bersama dengan implementasi yang AnimalAnda inginkan. Gunakan cara ini:

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

Buat kelas baru dengan perilaku baru. Kemudian perbarui maindi Testkelas.

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

atau cukup baca file Tes dan hitung berapa kali Anda menelepon eat(). Saya menduga jawabannya adalah tiga.

Shadov Aug 18 2020 at 23:35

Jenis pendekatan yang berbeda, yang akan memungkinkan Anda untuk menghindari 50 kelas untuk tugas yang hanya dilakukan beberapa kali. Ini lebih umum, sangat berguna dalam aspek teknis, tidak banyak dalam bisnis, masih bisa sangat berguna.

btw Builder hanya untuk ilustrasi, tentunya tidak harus menggunakan seperti itu, preEat / postEat penting disini

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