У нас есть проект, который использует внешнюю систему путешествий и позволяет покупать билеты. Мы храним информацию о билетах локально, но фактическое состояние определяется во внешней системе:
FirstCorpService srv1 = new FirstCorpService();
FirstCorpTicket tkt1 = srv1.buyTicket(...);
tktEntity1 = ticketRepository.save(CorpCode.CORP1, tkt1.getId(), Status.NEW);
...
srv1.applyDiscount(tkt1);
...
tktEntity1 = ticketRepository.findById(id);
FirstCorpTicket tkt1 = srv1.loadTicket(tktEntity1.getExternalId());
if ( ! tkt1.isCancelled()) {
srv1.cancel(tkt1);
tktEntity1.setStatus(State.CANCELLED);
ticketRepository.save(tktEntity1);
}
Теперь у нас есть еще две системы заявок с тем же доменом, объектной моделью и небольшим отличием в рабочем процессе.
Нам нужно абстрагироваться от конкретных типов FirstCorpService
и FirstCorpTicket
.
Важно абстрагироваться от service и data одновременно. Конкретный сервис не может работать с общими данными (тикетом). Чтобы применить скидку или отменить билет, вам нужны данные действительной системы бронирования, которые различаются у разных поставщиков.
Существуют ли шаблоны проектирования, которые решают поставленную задачу проектирования, когда и состояние, и алгоритм должны соответствовать друг другу?
TL; DR Чтобы сохранить независимость от поставщика реализации, я думаю об использовании полиморфизма для объекта, охватывающего как service , так и data . Возможно, я нарушаю SOLID / GRASP в следующем дизайне реализации, поэтому я прошу помощи.
Операции с абстрактными билетами:
interface TicketI {
long getId();
void applyDiscount();
boolean isCancelled();
void cancel();
}
Поскольку типичный язык ООП не поддерживает полиморфизм для оператора new
, нам нужны фабрики для создания оболочки для конкретного билета:
interface TicketFactoryI {
TicketI buy(...);
TicketI load(long externalId);
}
Бетонный завод и билетный делегат работают на конкретную службу:
interface Corp2TicketWrapper implements TicketI {
Corp2Service srv;
Corp2Ticket tkt;
constructor(Corp2Service srv, Corp2Ticket tkt) {
this.srv = srv;
this.tkt = tkt;
}
long getId() { return tkt.getId(); }
boolean isCancelled() { return tkt.isCancelled(); }
void applyDiscount() {
srv.applyDiscount(tkt);
}
void cancel() {
srv.cancel(tkt);
}
}
class Corp2TicketFactory implements TicketFactoryI {
Corp2Service srv;
TicketI buy(...) {
Corp2Ticket tkt = srv.buyTicket(...);
return new Corp2TicketWrapper(srv, tkt);
}
TicketI load(long externalId) {
Corp2Ticket tkt = srv.loadTicket(externalId);
return new Corp2TicketWrapper(srv, tkt);
}
}
В зависимости от CorpCode.CORP1
/ CorpCode.CORP2
/ etc мы можем предоставить фабрику фабрики, которая будет единственным местом для диспетчеризации реализации:
class TicketFactoryFactory {
static getInstance(CorpCode corporationCode) {
switch (corporationCode) {
case CorpCode.CORP1: return new Corp1TicketFactory();
case CorpCode.CORP2: return new Corp2TicketFactory();
case CorpCode.CORP3: return new Corp3TicketFactory();
}
}
}
В оригинальной программе не будет ссылок на конкретный код поставщика:
TicketFactoryI tktFactory = TicketFactoryFactory.getInstance(CorpCode.CORP2);
TicketI tkt = tktFactory.buyTicket(...);
tktEntity = ticketRepository.save(CorpCode.CORP2, tkt.getId(), Status.NEW);
...
tkt.applyDiscount();
...
tktEntity = ticketRepository.findById(id);
TicketI tkt = tktFactory.load(tktEntity.getExternalId());
if ( ! tkt.isCancelled()) {
tkt.cancel();
tktEntity.setStatus(State.CANCELLED);
ticketRepository.save(tktEntity);
}
ОБНОВЛЕНИЕ С https://stackoverflow.com/a/670337/173149
Это классическая проблема в ООД. Если ваш набор классов (животных) является фиксированным, вы можете использовать шаблон посетителя. Если ваш набор действий (например, подача) ограничен, вы просто добавляете метод feed () в Animal. Если ничего из этого не выполняется, простого решения не существует.