Я пытаюсь обдумать, как лучше спроектировать систему, над которой я работаю.
Допустим, это приложение, похожее на ломбард. Я сократил поток покупки и перепродажи в нечто, называемое ExecutionStrategy. В этом приложении есть четыре реализации ExecutionStrategy: регистрация клиента, участие в торгах и покупке, ценообразование и размещение в магазине.
Есть основные шаги, которые следует каждой из стратегий, включая основной рабочий процесс выполнения и запись того, что мы сделали в хранилище данных.
В дополнение к этим торгам и закупкам, а также ценообразованию требуется консультация эксперта, прежде чем мы сможем что-либо сделать в рабочем процессе исполнения.
это то, где я немного запутался в решении, которое я хотел бы принять с точки зрения дизайна. кажется, что есть три варианта, и я не уверен, какой из них наиболее правильный. 1) Расширение стратегии выполнения с помощью что-то вроде ExecutionStrategy с ExecutionStrategyWithConsultation, которое объединяет рабочий процесс выполнения стратегии с фазой консультации. 2) Создание шаблона Decorator для ExecutionStrategy и расширяет его с помощью чего-то вроде ConsultationServiceDecorator. 3) создать переменную члена в реализации закупок / торгов и ценообразования для вызова консультационной службы в любое время с помощью интерфейса вокруг службы.
Я обрисую схему ниже.
Некоторые соображения:
- Консультационная служба работает очень и очень медленно. Кэширование на самом деле не вариант здесь, так как данные очень плохо сформированы, и мы не хотим создавать хранилище данных документа только для этого.
- ConsultationService возвращает объект, который соответствует тому, что было дано. В итоге получается 1 метод, который выглядит как
T consultOn(T item)
- Может возникнуть желание вызвать ConsultationService в любое время в рабочем процессе выполнения. В настоящее время единственным вариантом использования является вызов службы перед основным потоком, но сейчас это не обязательно единственный вариант использования.
Плюсы / минусы каждого подхода выше:
Прямое расширение стратегии исполнения:
- PRO: у нас может быть доступ к защищенной переменной ConsultationService в коде
- PRO: При чтении кода у нас есть понимание, скажем,
PurchasingExecutionStrategy extends ExecutionStrategyWithConsultation
, поэтому мы немного знаем о том, какой это рабочий процесс именно из этого.
- CON: Кажется, это нарушает шаблон «композиция поверх наследования». Мы используем наследование для хранения переменных-членов.
- CON: Служба возвращает совершенно новый объект, поэтому после первой строки кода, когда мы выполняем вызов службы, мы имеем дело с совершенно другим объектом, чем тот, который был передан изначально.
Создание декоратора:
- PRO: Мы более тесно соответствуем составу, а не принципу наследования.
- PRO: мы можем обеспечить, чтобы служба вызывалась первой, и передавать этот новый объект в основной рабочий процесс, чтобы он выполнял только свой основной рабочий процесс для переданного объекта.
- CON: Я не придумал, как спроектировать это таким образом, чтобы можно было принимать несколько или несколько вызовов времени.
- CON: Когда мы смотрим на код, мы теряем знания, полученные из
PurchasingExecutionStrategy extends ExecutionStrategyWithConsultation
, если только мы не посмотрим, где на самом деле создается экземпляр PurchasingExecutionStrategy
как аргумент конструктора ConsultationServiceDecorator
Создание переменной-члена с интерфейсом:
- PRO: Те же плюсы, что и # 1. Легко понять, что делает код без копания.
- CON: Те же минусы, что и # 1. Невозможно навести порядок. Исполнение имеет дело с наследственно отличным объектом, чем переданный.
- CON: Если нам нужно сделать несколько вызовов в одном рабочем процессе, это будет очень медленно из-за скорости обслуживания и отсутствия кэша.
Примеры каждого:
//Number 1
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public interface ConsultationService {
public StoreItem consultOn (StoreItem item);
}
public abstract class ExecutionStrategyWithConsultation implements ExecutionStrategy<StoreItem> {
protected ConsultationService consultationService;
}
public class ListingExecutionStrategy extends ExecutionStrategyWithConsultation {
public StoreItem execute(StoreItem item) {
if (item.hasDirectBuyer()) { //hasDirectBuyer is populated by ConsultationService
item.sellTo = item.directBuyer.getId();
} else {
//no direct buyer
SuggestedPriceRange priceRange = item.getConsultationPriceRange(); //consultationPriceRange is populated by ConsultationService
item.priceRange = priceRange;
item.listToWebsite = true;
}
return item;
}
}
//Number 2
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public abstract class ExecutionStrategyDecorator<T> implements ExecutionStrategy<T>{
protected final ExecutionStrategy<T> executionStrategy;
public ExecutionStrategyDecorator(ExecutionStrategy<T> execStrategy) {
executionStrategy = execStrategy;
};
}
public class ExecutionStrategyWithConsultation extends ExecutionStrategyDecorator<StoreItem> {
protected ConsultationService consultationService;
public ExecutionStrategyWithConsultation(ExecutionStrategy<StoreItem> execStrat, ConsultationService service) {
super(execStrat);
consultationService = service;
}
public StoreItem execute(StoreItem item) {
StoreItem itemAfterConsultation = consultationService.consultOn(item);
return execStrategy.execute(itemAfterConsultation);
}
}
public class ListingExecutionStrategy implements ExecutionStrategy<StoreItem> {
public StoreItem execute(StoreItem item) {
if (item.hasDirectBuyer()) { //hasDirectBuyer is populated by ConsultationService
item.sellTo = buyer.getId();
} else {
//no direct buyer
SuggestedPriceRange priceRange = item.getConsultationPriceRange(); //consultationPriceRange is populated by ConsultationService
item.priceRange = priceRange;
item.listToWebsite = true;
}
return item;
}
}
public class ListingExecutionStrategyFactory {
public ExecutionStrategy instantiate() {
return new ExecutionStrategyWithConsultation(new ListingExecutionStrategy(), new ConsultationServiceImpl());
}
}
//Number 3
public interface ExecutionStrategy<T> {
/**
* Perform the main execution workflow
*/
public T execute(T item);
}
public interface ConsultationService {
public DirectBuyer getDirectBuyerIfExists(StoreItemType itemType);
public SuggestedPriceRange getSuggestedPriceRange(StoreItem item);
}
public class ListingExecutionStrategy implements ExecutionStrategy<StoreItem> {
ConsultationService service;
public PurchasingExecutionStrategy(ConsultationService consultService) {
service = ConsultationService;
}
public StoreItem execute(StoreItem item) {
DirectBuyer buyer = service.getDirectBuyerIfExists(item.getItemType())
if (Optional.ofNullable(buyer).isPresent()) {
item.sellTo = buyer.getId();
return item;
} else {
//no direct buyer
SuggestedPriceRange priceRange = service.getSuggestedPriceRange(item);
item.priceRange = priceRange;
item.listToWebsite = true;
return item;
}
}
}
Спасибо за ввод. Ценю помощь.