Шаблон для абстрагирования объекта и поведения одновременно - PullRequest
0 голосов
/ 15 апреля 2019

У нас есть проект, который использует внешнюю систему путешествий и позволяет покупать билеты. Мы храним информацию о билетах локально, но фактическое состояние определяется во внешней системе:

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. Если ничего из этого не выполняется, простого решения не существует.

...