从类图中生成代码:两个类之间的组合不会改变生成的 Java 代码中的任何内容

Posted

技术标签:

【中文标题】从类图中生成代码:两个类之间的组合不会改变生成的 Java 代码中的任何内容【英文标题】:Code Generation from Class Diagram: Composition between two classes does not change anything in generated Java code 【发布时间】:2021-11-28 18:20:59 【问题描述】:

我在Visual Paradigm software 中创建了两个简单的类A 和B,这样在这两个类之间就有一个COMPOSITION。但是,生成的代码(在 Java 中)与 OR 相同,但没有这两个类之间的关联。换句话说,如果A类和B类之间存在组合,则生成的代码与我删除A类和B类之间的组合时相同。也就是说,generated code中不考虑类之间的组合视觉范式。 解决办法是什么?

编辑:应@Christophe 的要求,我添加了一个测试示例项目的照片(类图),以及Visual Paradigm 生成的两个Java 类,在这两种情况下(与并且没有两个类之间的组合关系)生成的代码完全相同。

Visual Paradigm 生成的代码:

public class Class 

private int attribute;

public void operation() 
    // TODO - implement Class.operation
    throw new UnsupportedOperationException();

public class Class2 

private int attribute;

public void operation() 
    // TODO - implement Class2.operation
    throw new UnsupportedOperationException();

P.S. 我用 IBM Rational Rhapsody 试了一下,效果更好。也就是说,它在代码中更详细地生成了模型的特征。查看下面 IBM Rational Rhapsody 生成的代码,并与 Visual Paradigm 生成的代码进行比较:

由 IBM Rational Rhapsody 生成的代码:

package Default;


//----------------------------------------------------------------------------
// Default/class_0.java                                                                  
//----------------------------------------------------------------------------

//## package Default 


//## class class_0 
public class class_0 
    
    protected int attribute_0;      //## attribute attribute_0 
    
    protected class_1 itsClass_1;       //## link itsClass_1 
    
    
    // Constructors
    
    //## auto_generated 
    public  class_0() 
        initRelations();
    
    
    //## operation Operation_1() 
    public void Operation_1() 
        //#[ operation Operation_1() 
        //#]
    
    
    //## auto_generated 
    public int getAttribute_0() 
        return attribute_0;
    
    
    //## auto_generated 
    public void setAttribute_0(int p_attribute_0) 
        attribute_0 = p_attribute_0;
    
    
    //## auto_generated 
    public class_1 getItsClass_1() 
        return itsClass_1;
    
    
    //## auto_generated 
    public void __setItsClass_1(class_1 p_class_1) 
        itsClass_1 = p_class_1;
    
    
    //## auto_generated 
    public void _setItsClass_1(class_1 p_class_1) 
        if(itsClass_1 != null)
            
                itsClass_1.__setItsClass_0(null);
            
        __setItsClass_1(p_class_1);
    
    
    //## auto_generated 
    public class_1 newItsClass_1() 
        itsClass_1 = new class_1();
        itsClass_1._setItsClass_0(this);
        return itsClass_1;
    
    
    //## auto_generated 
    public void deleteItsClass_1() 
        itsClass_1.__setItsClass_0(null);
        itsClass_1=null;
    
    
    //## auto_generated 
    protected void initRelations() 
        itsClass_1 = newItsClass_1();
    
    

/*********************************************************************
    File Path   : DefaultComponent/DefaultConfig/Default/class_0.java
*********************************************************************/

二等代号:

package Default;


//----------------------------------------------------------------------------
// Default/class_1.java                                                                  
//----------------------------------------------------------------------------

//## package Default 


//## class class_1 
public class class_1 
    
    protected int attribute_0;      //## attribute attribute_0 
    
    protected class_0 itsClass_0;       //## link itsClass_0 
    
    
    // Constructors
    
    //## auto_generated 
    public  class_1() 
    
    
    //## operation Operation_1() 
    public void Operation_1() 
        //#[ operation Operation_1() 
        //#]
    
    
    //## auto_generated 
    public int getAttribute_0() 
        return attribute_0;
    
    
    //## auto_generated 
    public void setAttribute_0(int p_attribute_0) 
        attribute_0 = p_attribute_0;
    
    
    //## auto_generated 
    public class_0 getItsClass_0() 
        return itsClass_0;
    
    
    //## auto_generated 
    public void __setItsClass_0(class_0 p_class_0) 
        itsClass_0 = p_class_0;
    
    
    //## auto_generated 
    public void _setItsClass_0(class_0 p_class_0) 
        if(itsClass_0 != null)
            
                itsClass_0.__setItsClass_1(null);
            
        __setItsClass_0(p_class_0);
    
    
    //## auto_generated 
    public void setItsClass_0(class_0 p_class_0) 
        if(p_class_0 != null)
            
                p_class_0._setItsClass_1(this);
            
        _setItsClass_0(p_class_0);
    
    
    //## auto_generated 
    public void _clearItsClass_0() 
        itsClass_0 = null;
    
    

/*********************************************************************
    File Path   : DefaultComponent/DefaultConfig/Default/class_1.java
*********************************************************************/

如您所见,IBM 生成的代码还包括关联(组合)和其他功能。

这是IBM Rational Rhapsody生成的上述代码的类图:

【问题讨论】:

您需要知道复合聚合只是一个运行时信息,因此可以确定所有者在复合死亡时将其销毁。 能否请您展示两个有组合和无组合的图表,以便我们可以看到设置(多重性、可导航性等)和生成的单一源代码。您是否还可以偶然验证它不是相同的代码,例如因为您的编辑器在生成期间阻止了文件的更新? @Christophe,完成。 @Questioner 现在很清楚,为什么它不能按预期工作 ;-) 您没有将角色名或多重性添加到您的 Visual Paradigm 模型中(您在 IBM 模型中这样做了)。我可以想象它在生成代码时会起作用。 【参考方案1】:

图表是否提供了所需的内容?

在您的 VP 图中,您添加了一个组合关系,它留下了很多未指定的东西:

它不指示关联是否可导航 它没有显示多重性 它没有指定角色(即关联端的名称)

这些都可能是正确生成代码的障碍:

没有可导航性,该工具不知道您是否打算参考另一侧(或者是否没有可导航性,即没有容易找到另一侧的参考)。 没有多重性,该工具不知道它是否必须生成一个引用一个对象或一组对象的简单 java 字段 没有角色,该工具不知道您要如何命名应该实现关联/组合对象的 java 字段。

不同的工具会做出不同的假设。 VP 显示以下特定于工具的行为:

如果您打开您的合成规范,您会看到如果未指定(这是特定于 VP 而非 UML 标准)的可导航性在两个方向上都被假定为真。 如果在图中未指定,则用于代码生成的默认多重性为 1。 但是缺少角色会导致不生成 java 关联/组合:VP 不知道如何命名它。

如果图表足够,VP 会生成组合

这里我将用于演示目的的图表:

组合的规格如下:

如果有角色名(这里是A和B),结构关系对应的代码就很好生成了:

public class MyClassA 
    private long id;
    Collection<MyClassB> B;  // <========== only if role is in diagram
    public void doSomething() 
        // TODO - implement MyClassA.doSomething
        throw new UnsupportedOperationException();
    


public class MyClassB 
    private string name;
    MyClassA A;   // <=========================
    public string getName() 
        return this.name;
    
    public void setName(string name) 
        this.name = name;
    

顺便说一句,正如我在之前的回答中所解释的,如果您有关联而不是组合,则生成的代码是相同的,因为语义上的差异(在组合中排他性包含并在末尾删除组合元素复合生命周期)很难自动化。

什么时候不生成作文?

如果从规范中删除角色名称,生成的代码会删除实现关联的 java 字段;然后生成的类看起来就像根本没有组合/关联:

public class MyClassA 
    private long id;
    public void doSomething() 
        // TODO - implement MyClassA.doSomething
        throw new UnsupportedOperationException();
    

public class MyClassB 
    private string name;
    public string getName() 
        return this.name;
    
    public void setName(string name) 
        this.name = name;
    

这是你亲身经历的结果。您可以故意利用这种行为,例如在一侧而不是另一侧实现关联(例如,如果 B 不需要知道它拥有 A)。

缺少 VP 功能? 唯一真正缺少的是在代码生成时关于可导航性和缺席角色之间不一致的警告消息。此类消息会为您节省大量时间。

备注:

在您的 IBM 图表中,角色名称在那里。如果缺少角色名称,那么看看代码是否会一样全面会很有趣。 我使用 BOUML 测试了该行为。在没有角色名称的情况下,代码会生成一个匿名成员变量(再次强调,不会为您发明属性名称):组合在生成的代码中,但没有名称会导致编译错误。

【讨论】:

角色名称或多重性由软件自动添加到 IBM Rhapsody 模型中,然后您可以对其进行编辑,但软件不允许角色名称为空。

以上是关于从类图中生成代码:两个类之间的组合不会改变生成的 Java 代码中的任何内容的主要内容,如果未能解决你的问题,请参考以下文章

从类生成数据库结构的 ORM

从类模板生成 lambda

如何使用 Visual Studio(和/或 ReSharper)从类字段生成构造函数?

Python,从类外部访问小部件项

UML类图五种关系与代码的对应关系

UML类图的五种关系小结