การออกแบบ 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 เอง

คำตอบ

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()หรือเพียงแค่อ่านไฟล์ทดสอบและนับจำนวนครั้งที่คุณเรียกว่า ฉันเดาว่าคำตอบน่าจะเป็นสาม

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