One UML class, one Java class
Structural Aspects
Gerson Sunyé gerson.sunye@univ-nantes.fr
Introduction
Implementation Strategy
Classes
Attributes
Associations
UML has several diagrams representing different aspects of the model.
The mapping from UML concepts (classes, associations, signals, states, operations, etc.) to OO concepts (classes, fields, and methods) is not trivial.
UML lacks semantics: there is no universal rule for mapping design to code.
| The developer needs an «Implementation Strategy» to guide the translation. |
A set of rules that specifies how to translate design models to code.
Different parts: components, classes, attributes, operations, state charts, etc.
A correspondence table between UML types and the target language types.
Occasionally: a specific UML profile (set of tags and stereotypes), generation templates, configurations, etc.
Implement component
Implement unit test
For each class in a component:
Implement class
Implement unit tests
Introduction
Implementation Strategy
Classes
Attributes
Associations
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.
Different approaches:
Simple correspondence.
For each UML class, create a Java class.
Class-Interface.
For each UML class, create a pair (class, interface)
Generation Gap Pattern.
For each UML class, create a triple (interface, abstract class, concrete class)
One UML class, one Java class
public class HTMLPage {
// (...)
}One UML class, one pair: (Class,Interface):
For each UML class, create a pair (class, interface):
public interface HTMLPage {
// (...)
}
public class BasicHTMLPage implements HTMLPage {
// (...)
}Useful when the class HTMLPage is used in different contexts: DAO, RPC, Persistence, Tests, etc.
Based on the «Generation Gap» pattern.
For each UML class, create a triple (interface, abstract class, concrete class)
public interface HTMLPage {
// (...)
}
public abstract class BasicHTMLPage implements HTMLPage {
// (...)
}
public class UserHTMLPage extends BasicHTMLPage {
// (...)
}Useful in a automatic code generation context:
The subclass UserHTMLPage is only generated once.
User code is never overwritten.
Class/Interface proliferation.
Use a common interface:
public interface Common {
Common copy();
Common deepCopy();
boolean equals(Common);
String toString();
Map<String,Object> values();
}Facility methods available for all objects.
Can be extended with reflection methods, e.g.:
Object get(String fieldName)
void set(String fieldName, Object value)
void call(String methodName)
etc.
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.
Base UML only has 5 primitive types.
However, new datatypes can be added using «profiles».
| Type | Values |
|---|---|
Integer | -1, 0, 1, 2, … |
Boolean | true, false |
UnlimitedNatural | 0, 1, * |
String | "to be or not to be" |
Real | 1.5, 3.14, … |
| UML | Java | MySQL | TypeScript |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| UML | Java |
|---|---|
|
|
|
|
|
|
|
|
|
|
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.

Attributes are a typed structural property, which specify the structure of all instances of a given classifier.

code : Integer [1]
-- monovalued mandatory attribute.
-- e.g.: 1; 2; 99.
last name : String [0..1]
-- monovalued optional attribute.
-- e.g.: null; "john"; "paul".
first names : String [*]
-- multivalued attribute.
-- e.g.: {}, {"john", "paul"}, {"ringo", "george"}
readOnly, id, redefines <p>,
| Symbol | Visibility |
|---|---|
| Public |
| Protected |
| Package |
| Private |

Constraints respect the OCL syntax.

| Derived attributes are specified in the Object Constraint Language (OCL). |

Naive implementation.
Getters and Setters.
Attribute Wrappers.
UML Attribute = Java Field
Same visibility
Read-only attribute = final field
public class HTMLPage {
public final String title;
Integer version;
protected String contents;
private Boolean visibility = new Boolean(true);
}Derived attributes (e.g. size).
Constraints.
For each mono-valued attribute:
Create a private field for each non-derived attribute.
Create a getter method for each attribute, respecting the visibility.
Create a setter method for each non read-only, non derived attribute.
Create a private field for each non-derived attribute.
public class HTMLPage {
private final String title;
private Integer version;
private String contents;
private Boolean visibility = new Boolean(true);
}Create a getter method for each attribute, respecting the visibility.
public class HTMLPage {
public String getTitle() {
return title;
}
public Integer getSize() {
return contents.size();
}
Integer getVersion() {
return version;
}
protected String getContents() {
return contents;
}
private Boolean getVisibility() {
return visibility;
}
}Create a setter method for each non-read-only, non-derived attribute.
public class HTMLPage {
void setVersion(Integer aVersion) {
version = aVersion;
}
protected void setContents(String str) {
contents = str;
}
private void setVisibility(Boolean bool) {
visibility = bool;
}
}All fields are private.
Visibility is ensured by method access.
Getters and Setters can implement read-only and derived attributes.
Create a wrapper class[1] for attribute types.
For each mono-valued attribute:
Create a private field for each non-derived attribute.
Create an accessor method for each attribute, respecting the visibility.
Create a wrapper class for attribute types
public class Attribute<T> {
private T value;
public Attribute();
public Attribute(T t) {
value = t;
}
public void set(T newValue) {
value = newValue;
}
public T get() {
return value;
}
}public class ReadOnlyAttribute<T> {
private final T value;
public Attribute(T t) {
value = t;
}
public void set(T newValue) {
throw new UnsupportedOperationException();
}
public T get() {
return value;
}
}public class SizeAttribute<T> {
private final Attribute<String> contents;
public SizeAttribute(Attribute<String> attr) {
contents = attr;
}
public void set(T newValue) {
throw new UnsupportedOperationException();
}
public T get() {
return contents.get().size();
}
}Create a private filed for each non-derived attribute.
public class HTMLPage {
private final Attribute<String> title =
new ReadOnlyAttribute<String>();
private final Attribute<Integer> version =
new Attribute<Integer>();
private final Attribute<String> contents =
new Attribute<String>();
private final Attribute<Boolean> visibility =
new Attribute<Boolean>(true);
private final SizeAttribute<Integer> size =
new SizeAttribute(contents);
}Create an accessor method for each attribute, respecting the visibility.
public class HTMLPage {
public ReadOnlyAttribute<String> title() {
return title;
}
public SizeAttribute<Integer> size() {
return size;
}
Attribute<Integer> version() {
return version;
}
protected Attribute<String> contents() {
return contents;
}
private Attribute<Boolean> visibility() {
return visibility;}
}All fields are private.
Visibility is ensured by method access.
Class/object multiplication.
Wrappers can implement read-only and derived attributes, but a specific class may be necessary.
Extensible
other methods/behaviors can be implemented, e.g. reset(), isSet()
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.

Multiplicities and Properties

ordered, unique, non-unique, sequence (or seq)
union, subsets <p>

Naive implementation.
Accessors
Attribute Wrappers.
UML Attribute = Java Field
Same visibility
Relay on the Java Collections Framework, JCF
public class Patient {
public final Set<String> pathologies =
new HashSet<String>;
public final Collection<String> exams =
new ArrayList<String>();
public final List<Double> temperatures =
new ArrayList<Double>;
public final Collection<String> notes =
new ArrayList<String>();
}Multiplicities.
Constraints.
For each multi-valued attribute:
Create a private field for each non-derived attribute.
Create add(), remove(), and iterator() methods for each attribute, respecting the visibility.
Create a private field for each non-derived attribute.
public class Patient {
private final Set<String> pathologies =
new HashSet<String>();
private final Collection<String> exams =
new ArrayList<String>();
private final List<Double> temperatures =
new ArrayList<Double>();
private final Collection<String> notes =
new ArrayList<String>();
}Create a add() method for each attribute
public class Patient {
public boolean addPathologie(String str) {
return this.pathologies.add(str);
}
public boolean addExam(String str) {
return this.exams.add(str);
}
public boolean addTemperature(Double d) {
return this.temperatures.add(d);
}
public boolean addNote(String str) {
if (notes.size == 5) return false;
return this.notes.add(str);
}
}Create a remove() method for each attribute
public class Patient {
public boolean removePathologie(String str) {
return this.pathologies.remove(str);
}
public boolean removeExam(String str) {
return this.exams.remove(str);
}
public boolean removeTemperature(Double d) {
return this.temperatures.remove(d);
}
public boolean removeNote(String str) {
return this.notes.remove(str);
}
}Create a iterator() method for each attribute
public class Patient {
public Iterator<String> iterator() {
return this.pathologies.iterator();
}
public Iterator<String> iterator() {
return this.exams.iterator();
}
public Iterator<Double> iterator() {
return this.temperatures.iterator();
}
public Iterator<String> iterator() {
return this.notes.iterator();
}
}All fields are private.
Visibility is ensured by method access.
Accessors can implement maximum multiplicity checks.
Limited interface for dealing with collections: only 3 methods vs. 25 for Java List interface.
Method proliferation.
Create a wrapper class for attribute types.
For each multi-valued attribute:
Create a private field for each attribute.
Create an accessor method for each attribute, respecting the visibility.
Create a wrapper class for attribute types
public class MultivaluedAttribute<T> {
private final List<T> values;
public MultivaluedAttribute(List<T> l) {
this.valued = l;
}
public boolean add(T t) {
return this.values.add(t);
}
public boolean remove(T t) {
return this.values.remove(t);
}
// (...)
}Create a private field for each attribute
public class Patient {
private final MultivaluedAttribute<String> pathologies =
new MultivaluedAttribute<String>(new HashSet<String>());
private final MultivaluedAttribute<String> exams =
new MultivaluedAttribute<String>(new ArrayList<String>());
private final MultivaluedAttribute<Double> temperatures =
new MultivaluedAttribute<Double>(new ArrayList<Double>());
private final MultivaluedAttribute<String> notes =
new MultivaluedAttribute<String>(new ArrayList<String>());
}Create an accessor method for each attribute, respecting the visibility.
public class Patient {
public MultivaluedAttribute<String> pathologies() {
return this.pathologies;
}
public MultivaluedAttribute<String> exams() {
return this.examns;
}
public MultivaluedAttribute<Double> temperature() {
return this.temperatures;
}
public MultivaluedAttribute<String> notes() {
return this.notes;
}
}All fields are private.
Visibility is ensured by method access.
Class/object proliferation.
Wrappers can implement read-only and derived attributes, but a specific class may be necessary.
Wrappers can implement maximum multiplicity checks.
Extensible: other methods/behaviors can be implemented, e.g. the Java List interface.
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.

An association between two (or more) classes represents a stable link between two (or more) objects, instances from theses classes.

| Symbol | Name |
|---|---|
Association | |
Shared Aggregation | |
Composite Aggregation |


subsets, redefines, union, ordered, bag, sequence

Accessors.
Cursors.
Monovalued roles ([0..1], [1]): similar to attributes.
Multivalued roles ([0..2], [*], etc.): use the Collection interface:
Applies the Decorator design pattern.
Visibility is ensured by accessor visibilities.
public class Card {
private Account account;
public Account getAccount() {
return account;
}
public void setAccount(Account anAccount) {
this.account = anAccount;
}
}public class HTMLFolder {
private Collection<HTMLPage> pages =
new PageCollection(new HashSet<HTMLPage>());
public Collection<HTMLPage> getPages() {
return pages;
}
}public class PageCollection implements Collection<HTMLPage> {
private Collection<HTMLPage> pages;
public PageCollection(Collection<HTMLPage> list) {
this.pages = list;
}
public int size() {
return pages.size();
}
public boolean isEmpty() {
return pages.isEmpty();
}
public boolean contains(Object o) {
return pages.contains(o);
}
public Iterator<HTMLPage> iterator() {
return pages.iterator();
}
public Object[] toArray() {
return pages.toArray();
}
public <T> T[] toArray(T[] a) {
return pages.toArray(a);
}
public boolean add(HTMLPage htmlPage) {
return pages.add(htmlPage);
}
public boolean remove(Object o) {
return pages.remove(o);
}
public boolean containsAll(Collection<?> c) {
return pages.containsAll(c);
}
public boolean addAll(Collection<? extends HTMLPage> c) {
return pages.addAll(c);
}
public boolean addAll(int index, Collection<? extends HTMLPage> c) {
return pages.addAll(index, c);
}
public boolean removeAll(Collection<?> c) {
return pages.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return pages.retainAll(c);
}
public void clear() {
pages.clear();
}
public HTMLPage get(int index) {
return get(index);
}
public HTMLPage set(int index, HTMLPage element) {
return set(index, element);
}
public boolean remove(int index) {
return pages.remove(index);
}
public int lastIndexOf(Object o) {
return lastIndexOf(o);
}
}Mono- and multi-valued roles have different interfaces.
The Collection decorator allows the addition of new behavior:
Upper-bound multiplicity check.
Constraints.
Unique roles may use the JCF Set implementations (HashSet, TreeSet, etc.).
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.


A file cannot belong to two folders at the same time.
Adding a file to a folder should have the same effect as setting the folder of a file.

doc.setFolder(archive)archive.getFiles().add(doc)


Getters and Setters.
Cursors.


public class File {
private Folder folder;
public File(Folder aFolder) {
folder = aFolder;
}
public Folder getFolder() {
return folder;
}
}
file.setFolder(folder) must call folder.getFiles().add(file) and
folder.getFiles().add(file) must call file.setFolder(folder)
Problem: How to avoid the loop?
Folder folder = new Folder();
File file = new File();
file.setFolder(folder);
folder.getFiles().add(file);All clients must respect this rule.
Difficult to ensure during the class lifetime.
Add a method called basicSet(Folder) to the class File.
Add a method called basicAdd(File) to the class FileCollection.


Getter/Setter approach: basicAdd() is not in the List interface.
Cursor approach: each cursor must know its opposite (not so simple).
Class implementation.
Type correspondence.
Mono-valued attribute implementation.
Multi-valued attribute implementation.
Unidirectional association implementation.
Bidirectional association implementation.
Operation implementation.

An Operation is a feature of a class that specifies the name, type, parameters, and constraints for invoking an associated behavior.
A Reception specifies that a class is prepared to receive a Signal.
Methods can implement both, operations and receptions. Method execution is either synchronous, or asynchronous.




public class Book {
public boolean reserve(Reader aReader) {
// TODO
return false;
}
public void deliver() {
// TODO
}
public void borrow() {
// TODO
}
public void returnBook() {
// TODO
}
}
public class Notify implements Serializable {
public final String message;
public Notify(String message) {
this.message = message;
}
}
public class Alarm {
private final BlockingQueue<Notify> notifications =
new ArrayBlockingQueue<Notify>(10);
public void accept(Notify notify) {
this.notifications.offer(notify);
}
// TODO: write a thread that reads the blocking queue and executes the notification.
}UML is a rich modeling language with many interesting features.
There are several possible ways to translate designs to code.
We presented different implementation strategies.