Библиотека Java - как сделать чистую инъекцию зависимостей, когда состояние является фактором?

Библиотека Java - как сделать чистую инъекцию зависимостей, когда состояние является фактором?
Библиотека Java - как сделать чистую инъекцию зависимостей, когда состояние является фактором? - mlapergolaphoto @ Unsplash

Чтобы подготовить почву, я пытаюсь сделать чистую инъекцию зависимостей для библиотеки Java, которую я создаю, чтобы сделать ее более тестируемой. Поскольку это библиотека, я хочу сделать чистую инъекцию зависимостей без создания корня контейнера/композиции DI, как это обсуждалось Марком Симаном в его сообщениях в блоге*. Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть несколько классов посетителей, которые должны содержать состояние (пример, иллюстрирующий концепцию, приведен ниже). Было бы чище ввести их или просто объявить их с помощью нового ключевого слова? Единственная проблема, которую я вижу при их внедрении, заключается в том, что мне понадобится функция для очистки состояния посетителя после каждого вызова, а не с использованием нового, в котором я бы не нуждался. Есть ли лучшая практика для этого?

public class CarServiceBuilder {

    public CarService buildCarService(){      
      CarPartPrinter carPartPrinter = new CatPartPrinter();
      CarService carService = new CarService(carPartPrinter);
      return carService;
    }

}

public class CarService {

    private final CarPartPrinter printParts;
    
    public CarService(CarPartPrinter carPartPrinter){
    this.carPartPrinter = carPartPrinter;
    }
    
    public void printParts(Car car){
        CarVisitor carVisitor = new CarVisitor();
        car.accept(carVisitor);
        List<Part> parts = carVisitor.getParts();
        carPartPrinter.printParts(parts);
    }
    
}


public class CarVisitor extends Visitor {

   private List<Parts> parts;

   public CarVisitor(){
    parts = new ArrayList<>();
   }

   @Override
   public void visit(Part part){
    parts.add(part);
   }
   
}

ПРОТИВ.

public class CarServiceBuilder {

   public CarService buildCarService(){ 
      CarVisitor carVisitor = new CarVisitor();
      CarPartPrinter carPartPrinter = new CatPartPrinter();
      CarService carService = new CarService(carVisitor, carPartPrinter);
      return carService;
   }

}

public class CarService {

    private final CarVisitor carVisitor;
    private final CarPartPrinter printParts;
    
    public CarService(CarVisitor carVisitor, CarPartPrinter carPartPrinter){
    this.carVisitor = carVisitor;
    this.carPartPrinter = carPartPrinter;
    }
    
    public void printParts(Car car){
        car.accept(carVisitor);
        List<Part> parts = carVisitor.getParts();
        carPartPrinter.printParts(parts);
        carVisitor.clearState();
    }
    
}


public class CarVisitor extends Visitor {

   private List<Parts> parts;

   public CarVisitor(){
    parts = new ArrayList<>();
   }

   @Override
   public void visit(Part part){
    parts.add(part);
   }

   public void clearState(){
    parts = new ArrayList<>();
   }

}

Связанные записи в блоге:

Обычно я не думаю о посетителях как о зависимости, которую вы вводите

Посетители — это всего лишь механизм потока управления, своего рода объектно-ориентированный оператор переключения. Любое состояние посетителя аналогично локальным переменным, которые вы могли бы определить в области действия вызывающего объекта. Вы также обычно точно знаете, какой тип посетителя требуется в данном контексте (как в случае с вашим примером: printParts знает, что ему нужен CarVisitor для сбора списка частей), поэтому проще всего просто создать новый локальный экземпляр и выбросьте его, как только вы закончите. Внедрение объектов с состоянием создает проблемы, подобные той, которую вы уже отметили: вам нужно помнить об очистке состояния после того, как вы закончите, и это не является потокобезопасным.

Что касается тестирования, я бы просто рассматривал посетителя как деталь реализации CarService и напрямую тестировал функциональные требования, то есть действительно ли printParts печатает правильные части, независимо от того, как это реализовано.


LetsCodeIt, 3 января 2023 г., 06:08