Как работать с абстрактными классами, так как входные API-интерфейсы для микросервисов одновременно решают проблему полиморфизма? - PullRequest
0 голосов
/ 02 ноября 2018

Я прохожу дюжину учебных пособий, которые мне очень мало помогают, потому что производственный код не является животным, птицей или человеком. Не оружие типа стрельбы или стрельбы, о котором гораздо сложнее рассуждать.

Итак, возвращаясь к реальности, сценарий: service 1 обменивается сообщениями с service 2 через Kafka, сообщения сериализуются / десериализуются с помощью Jackson, класс модели распределяется между службами как jar.

Теперь часть чумы, кульминация зла:

@JsonTypeInfo(
        use = Id.NAME,
        property = "type",
        visible = true
    )
    @JsonSubTypes({@Type(
        value = InternalTextContent.class,
        name = "text"
    ), @Type(
        value = InternalImageContent.class,
        name = "image"
    ), @Type(
        value = InternalAudioContent.class,
        name = "audio"
    ), @Type(
        value = InternalCustomContent.class,
        name = "custom"
    )})
    public abstract class InternalContent {
        @JsonIgnore
        private ContentType type;

        public InternalContent() {
        }

Очевидно, что когда придет время работать с этим контентом, у нас будет что-то вроде:

message.getInternalContent

, что приводит к множеству switch операторов, if условий, instanceof и ожиданию этого ... удручающее повсюду

И это только один пример свойств, который содержит оберточный объект. Ясно, что я не могу добавить полиморфное поведение к InternalContent, потому что привет, он внутри jar.

Что здесь пошло не так? Это даже неправильно? Как добавить полиморфное поведение? Чтобы добавить новый смягчающий слой, мне все еще нужно instanceof на некоторой фабрике, чтобы создать новый тип семейства полиморфных объектов, которые можно редактировать, чтобы добавить желаемое поведение? Даже не уверен, что это будет лучше, это просто пахнет и заставляет меня хотеть стрелять в адвокатов, которые бросают слепое заявление, как instanceof с унижением - это кодовый запах, "мучающий таких людей, как я, которые искренне заботятся, что заставляет меня задуматься, если они когда-либо работал над реальным проектом. Я намеренно добавил детали системной среды, чтобы понять, как моделировать не только код, но и взаимодействие между системами. Каковы возможные варианты редизайна для достижения решения «по книге»?

Пока что я могу думать о том, что модель совместного использования домена является грехом. Но затем, если я использую разные классы самообслуживания для представления одних и тех же вещей для сериализации / десериализации, я собираю гибкость, но теряю контракт и увеличиваю непредсказуемость. Что технически происходит с HTTP-контрактами.

Должен ли я отправлять разные типы сообщений с разными структурами по проводам вместо попыток совмещать общие части и подтипы для необычных в одном типе сообщения?

Чтобы бросить больше песка в ОО, Pivotal я считаю лучшим среди лучших:

https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java

public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class
                .isAssignableFrom(authentication));
    }

AuhenticationManager имеет список AuthenticationProviders, подобный этому, и выбирает правильный на основе описанного выше метода. Это нарушает полиморфизм? Иногда все это выглядит как шумиха ...

1 Ответ

0 голосов
/ 06 ноября 2018

Использовать шаблон посетителя.

Пример (я ограничусь двумя подклассами, но вы должны понять):

interface InternalContentVisitor<T> {
    T visitText(InternalTextContent c);
    T visitImage(InternalImageContent c);
}

public abstract class InternalContent {
    public abstract <T> T accept(InternalContentVisitor<T> visitor);
    // ...
}

public class InternalTextContent {
    @Override
    public <T> T accept(InternalContentVisitor<T> visitor) {
        return visitor.visitText(this);
    }
}

public class InternalImageContent {
    @Override
    public <T> T accept(InternalContentVisitor<T> visitor) {
        return visitor.visitImage(this);
    }
}

Этот код является полностью общим и может использоваться любым приложением с помощью классов.

Итак, теперь, если вы хотите полиморфно сделать что-то в проекте 1 с InternalContent, все, что вам нужно сделать, - это создать посетителя. Этот посетитель находится вне классов InternalContent и может, таким образом, содержать код, специфичный для project1. Предположим, например, что у project1 есть класс Copier, который можно использовать для создания копии текста или изображения, вы можете использовать

InternalContent content = ...; // you don't know the actual type 
Copier copier = new Copier();
Copy copy = content.accept(new InternalContentVisitor<Copy>() {
    @Override
    public Copy visitText(InternalTextContent c) {
        return copier.copyText(c.getText());
    }

    @Override
    public Copy visitImage(InternalImageContent c) {
        return copier.copyImage(c.getImage());
    }
});

Итак, как вы можете видеть, нет необходимости в корпусе переключателя. Все по-прежнему делается полиморфно, хотя класс InternalContent и его подклассы вообще не зависят от класса Copier, который существует только в project1.

И если появляется новый класс InternalSoundContent, все, что вам нужно сделать, это добавить метод visitSound() в интерфейс посетителя и внедрить его во все реализации этого интерфейса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...