การออกแบบ 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 เอง
คำตอบ
คุณสามารถใช้รูปแบบมัณฑนากรเพื่อเพิ่มความรับผิดชอบเพิ่มเติมให้กับสัตว์โดยไม่ต้องแบ่งคลาสย่อย

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