Как бороться с Java-полиморфизмом в сервис-ориентированной архитектуре - PullRequest
13 голосов
/ 23 сентября 2011

Каков путь наименьшего зла при работе с полиморфизмом и наследованием типов сущностей в сервис-ориентированной архитектуре?

Принцип SOA (насколько я понимаю) состоит в том, чтобы иметь классы сущностей в виде простых конструкций данных, в которых отсутствует какая-либо бизнес-логика. Вся бизнес-логика содержится в узко ограниченных, слабосвязанных сервисах. Это означает, что реализации служб настолько малы, насколько это возможно, что способствует слабой связи, и означает, что сущности избегают знать о каждом поведении, которое система может выполнять с ними.

Из-за довольно странного решения Java использовать объявленный тип 1009 * при решении, какой перегруженный метод использовать , любое полиморфное поведение в реализациях службы вместо этого заменяется серией условных проверок. object.getClass() или используя instanceof. Это кажется довольно отсталым в OOPL.

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

UPDATE

Я определенно имею в виду перегрузку, а не переопределение.

Я определяю SOA как означающее, что поведение системы по сценариям использования группируется в интерфейсы, а затем логика для них, как правило, реализуется в одном классе на интерфейс. Таким образом, класс сущностей (скажем, Product) становится не чем иным, как POJO с геттерами и сеттерами. Он абсолютно не должен содержать какой-либо бизнес-логики, связанной с сервисом, потому что тогда вы вводите одну точку привязки, посредством которой класс сущности должен знать обо всех бизнес-процессах, которые могут когда-либо работать на нем, полностью отрицая цель слабо связанной SOA .

Таким образом, поскольку не следует встраивать поведение, специфичное для бизнес-процессов, в класс сущностей, нельзя использовать полиморфизм с этими классами сущностей - нет поведения, которое можно переопределить.

ОБНОВЛЕНИЕ 2

Вышеупомянутое поведение более просто объясняется как перегруженный путь, выбранный в время компиляции , и переопределенный путь в время выполнения .

Было бы плохой практикой иметь подкласс реализации вашей службы для каждого подтипа класса модели домена, на который он действует, так как же люди справляются с проблемой перегрузки во время компиляции?

Ответы [ 5 ]

5 голосов
/ 22 октября 2011

Вы можете избежать этой проблемы, разрабатывая бизнес-логику в разных классах на основе типа сущности, основываясь на принципе единой ответственности, и это будет наилучшим способом, когда вы помещаете бизнес-логику на уровень обслуживания и используете фабрику длялогическая реализация, например

enum ProductType
{
    Physical,
    Service
}


interface IProduct
{
    double getRate();
    ProductType getProductType();    
}

class PhysicalProduct implements IProduct
{
    private double rate;

    public double getRate()
    {
        return rate;
    }

    public double getProductType()
    {
        return ProductType.Physical;
    }
}

class ServiceProduct implements IProduct 
{
    private double rate;
    private double overTimeRate;
    private double maxHoursPerDayInNormalRate;

    public double getRate()
    {
        return rate;
    }

    public double getOverTimeRate()
    {
        return overTimeRate;
    }

    public double getMaxHoursPerDayInNormalRate;()
    {
        return maxHoursPerDayInNormalRate;
    }

    public double getProductType()
    {
        return ProductType.Service;
    }
}

interface IProductCalculator
{
    double calculate(double units);
}

class PhysicalProductCalculator implements IProductCalculator
{
    private PhysicalProduct product;

    public PhysicalProductCalculator(IProduct product)
    {
        this.product = (PhysicalProduct) product;
    }

    double calculate(double units)
    {
        //calculation logic goes here
    }
}

class ServiceProductCalculator implements IProductCalculator
{
    private ServiceProduct product;

    public ServiceProductCalculator(IProduct product)
    {
        this.product = (ServiceProduct) product;
    }

    double calculate(double units)
    {
        //calculation logic goes here
    }
}

class ProductCalculatorFactory
{
    public static IProductCalculator createCalculator(IProduct product)
    {
        switch (product.getProductType)
        {
            case Physical:
                return new PhysicalProductCalculator ();
            case Service:
                return new ServiceProductCalculator ();
        }
    }
}

//this can be used to execute the business logic
ProductCalculatorFactory.createCalculator(product).calculate(value);
3 голосов
/ 23 сентября 2011

Из-за довольно непонятного решения Java использовать объявленный тип при решении, какой перегруженный метод использовать

Кто бы вам не дал эту идею?Java был бы бесполезным языком, если бы он был таким!

Прочитайте это: Учебник Java> Наследование

Вот простая тестовая программа:

public class Tester{
    static class Foo {
        void foo() {
            System.out.println("foo");
        }
    }
    static class Bar extends Foo {
        @Override
        void foo() {
            System.out.println("bar");
        }
    }
    public static void main(final String[] args) {
        final Foo foo = new Bar();
        foo.foo();
    }
}

Вывод, конечно, "bar", а не "foo" !!

2 голосов
/ 25 октября 2011

Мне понадобилось время, чтобы прочитать то, что вы действительно просили.

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

Обычно я стараюсь избегать иерархии широкого или глубокого типа и работаю с instanceof и т. д., где требуется один или два случая.

Когда по какой-либо причине должна существовать широкая иерархия типов, я бы, вероятно, использовал шаблон обработчика, подобный приведенному ниже.

class Animal {

}
class Cat extends Animal {

}

interface AnimalHandler {
    void handleAnimal(Animal animal);
}

class CatHandler implements AnimalHandler {

    @Override
    public void handleAnimal(Animal animal) {
        Cat cat = (Cat)animal;
        // do something with a cat
    }

}

class AnimalServiceImpl implements AnimalHandler {
    Map<Class,AnimalHandler> animalHandlers = new HashMap<Class, AnimalHandler>();

    AnimalServiceImpl() { 
        animalHandlers.put(Cat.class, new CatHandler());
    }
    public void handleAnimal(Animal animal) {
        animalHandlers.get(animal.getClass()).handleAnimal(animal);
    }
}
2 голосов
/ 24 октября 2011

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

Ориентация объектов в Java при работе в такой среде применима к каждому домену.Таким образом, иерархии и богатые доменные объекты, моделируемые с использованием чего-то вроде доменного дизайна, будут жить на уровне ниже сервисов в SOA-решении.Существует уровень между службой, работающей в других контекстах, и подробной моделью домена, которая будет создавать расширенные объекты для работы с доменом.

Решение каждой архитектуры контекста / приложений с помощью SOA не даст очень хорошего приложения,Точно так же, как решение взаимодействия между ними с помощью ОО.

Итак, чтобы попытаться ответить на вопрос щедрости более конкретно: вопрос не заключается в разработке этой проблемы.Это вопрос применения правильного шаблона для каждого уровня проектирования.

Для большой корпоративной экосистемы SOA - это способ решения взаимодействия между системами, например, системой управления персоналом и расчетом заработной платы.Но при работе с HR (или, возможно, с каждым контекстом в HR) и заработной платой я бы использовал шаблоны из DDD.

Надеюсь, это немного очистит воды.

1 голос
/ 27 октября 2011

Подумав немного об этом, я подумал об альтернативном подходе, который упрощает дизайн.

abstract class Animal {
}

class Cat extends Animal {
    public String meow() {
        return "Meow";
    }
}

class Dog extends Animal {
    public String  bark() {
        return "Bark";
    }
}

class AnimalService { 
    public String getSound(Animal animal) {
        try {
            Method method = this.getClass().getMethod("getSound", animal.getClass());
            return (String) method.invoke(this, animal);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public String getSound(Cat cat) {
        return cat.meow();
    }
    public String getSound(Dog dog) {
        return dog.bark();
    }
}

public static void main(String[] args) {
    AnimalService animalService = new AnimalService();
    List<Animal> animals = new ArrayList<Animal>();
    animals.add(new Cat());
    animals.add(new Dog());

    for (Animal animal : animals) {
        String sound = animalService.getSound(animal);
        System.out.println(sound);
    }
}
...