如何在面向服务的架构中处理Java多态性

15

在面向服务的体系结构中处理实体类型的多态性和继承时,最小恶路径是什么?

SOA(据我所知)的原则是将实体类作为纯粹的数据结构,缺乏任何业务逻辑。所有业务逻辑都包含在范围狭窄、松耦合的服务中。这意味着服务实现尽可能小,进一步实现了松耦合,并且意味着实体避免了需要了解系统可能对它们执行的每个行为。

由于Java使用声明的类型来决定使用哪个重载方法,来决定使用哪个重载方法,因此服务实现中的任何多态行为均被替换为一系列条件检查object.getClass()或使用instanceof 。这在OOPL中似乎相当落后。

在SOA中,使用条件是被接受的常规吗?应该放弃实体类中的继承吗?

更新

我肯定意味着重载而不是覆盖。

我将SOA定义为将系统的行为按用例分组为接口,然后这些接口的逻辑在每个接口的一个类中实现,通常是这样。因此,实体类(例如Product )变成了仅具有getter和setter的POJO。它绝对不应包含与服务相关的任何业务逻辑,因为那么您将引入一种耦合的焦点,即实体类需要知道可能对其进行的所有业务流程,从而完全抵消了松散耦合SOA的目的。

因此,既然不能在实体类中嵌入业务过程特定的行为,那么就无法使用这些实体类的多态性 - 没有要覆盖的行为。

更新2

上述行为更简单地解释为,在编译时选择一个重载的路径,并在运行时选择一个重写的路径。

如果为每个域模型类的子类型都创建服务实现的子类,那么这是不好的做法,那么人们如何解决编译时重载的问题呢?


1
Java的决定使用声明类型来决定使用哪个重载方法令人困惑。这是无稽之谈。如果是这样,Java将无法使用! - Sean Patrick Floyd
你不明白你的问题与SOA有什么关系吗?SOA是关于定义具有相应输入和输出的可重用接口(服务)。它更可能是功能性的而不是面向对象的。是的,我没有看到在接口规范中继承的优势。 - home
@home 虽然我同意您对SOA的定义,但我会补充说它还封装了那些接口实现中功能的狭窄条带,而不是只有一个类对某些数据结构进行建模(比如一个产品),并提供可执行于该类上的所有功能。 - DeejUK
@Deejay:我也同意。实际上,您可能对某个实体(功能和数据)有不同的看法,而我不会试图将它们耦合在一起,例如从市场营销和/或制造角度查看产品。 - home
5个回答

6

您可以通过根据实体类型在不同的类中设计业务逻辑来避免此问题,基于单一职责原则,将业务逻辑放置在服务层中并使用工厂创建逻辑实现是最佳选择,例如:

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);

4

我读完这篇文章后需要一段时间才能理解你真正想要什么。

我的理解是,您有一组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);
    }
}

我认为这是目前最好的替代条件逻辑的方法,比其他地方建议的策略工厂在每次调用时创建新实例要更合适。 - DeejUK
认为这有点乏味和复杂,我又提供了另一种解决问题的方法(作为单独的答案)。 - Tom
我说这是适配器模式,对吗? - Jordan Mackie

3
由于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"!!


1
OP提到了overloading,而不是overriding(尽管从上下文判断,我不确定他/她知不知道两者的区别 :-))。 - Péter Török
@PéterTörök 确实如此,但这样做甚至更没有意义,所以我认为他的意思是重写。 - Sean Patrick Floyd
我绝对是指重载而不是覆盖。请参考以下链接进行解释:https://dev59.com/HHI_5IYBdhLWcg3wAeA2#1572499 - DeejUK
1
@Deejay:你能提供一个简短的例子,说明何时“服务实现中的多态行为被一系列条件检查object.getClass()或使用instanceof所替代”吗?我认为我们中的许多人都在努力理解你问题的背景。 - millhouse

2
我认为这里存在一些混淆。SOA是一种解决组件间交互的架构方式。SOA解决方案中的每个组件都将处理更大领域内的一个上下文。每个上下文都是其自身的领域。换句话说,SOA允许领域上下文之间或应用程序之间的松耦合。在Java中的面向对象编程,当在这种环境下工作时,将应用于每个领域。因此,使用类似领域驱动设计的层次结构和丰富的领域对象来建模,这样的对象模型将位于SOA解决方案中服务之下的层级上。在暴露给其他上下文的服务和创建领域可以使用的丰富对象之间有一层,该层将创建详细的领域模型。
对于每个上下文/应用程序的体系结构应用SOA并不能提供非常好的应用程序,就像使用面向对象编程解决它们之间的交互一样。
因此,试图更具体地回答悬赏问题:
这不是围绕问题进行工程处理的问题。这是将正确的模式应用于每个设计层次的问题。对于大型企业生态系统,SOA是我解决系统间交互的方式,例如HR系统和薪资系统。但是,在处理HR(或可能是HR内的每个上下文)和薪资时,我将使用DDD的模式。
希望这能使问题更加清晰明了。

1

经过进一步思考,我想到了一种替代方法,可以实现更简单的设计。

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);
    }
}

1
谢谢你花时间回答两个问题,汤姆!你试过这个吗? - DeejUK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接