Der EMF-generierte Code - Uni

Werbung
Der EMF-generierte Code
10. Dezember 2014
Überblick
 Wie sieht der aus einem EMF-Modell generierte
Code aus? Wie ist die Beziehung zwischen Modell
und Code?
 Wie kann generierter Code durch
handgeschriebenen erweitert bzw. ersetzt werden?
 Wie kann ein Instanzmodell erstellt werden?
 Durch einen Instanzeditor
 Anwendung der generierten Klassen
 Durch Anwendung von reflektiven Methoden auf dem EMFModell
 Welcher Test-Code wird erstellt?
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
193
Eclipse Modeling Framework (EMF)
Java
Klassen zur
Manipulation
des Modells
EMF
Klassendiagramm
EMF Generator
benutzt
einfacher
baumbasierter
Editor
basierend auf
JET
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
194
Struktur des generierten Codes
 Struktur des Modellcodes:
 Schnittstellenklassen
 Implementierungsklassen
 weitere nützliche Klassen
 aus einem EMF-Modell
generierte Plugins:
 … .edit
 … .editor
 … .tests
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
195
Modellierte Klassen
 für eine modellierte Klasse wird
erzeugt:
Model.java:
 eine Schnittstellenklasse
 eine Implementierungsklasse
 create-Methode in der FactoryKlasse
public interface Model extends EObject {
…
}
ModelImpl.java:
public class ModelImpl extends MinmalEObjectImpl.Container
implements Model {
…
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
196
Abstrakte Klassen
 Für eine abstrakte Klasse wird
erzeugt:
 eine Schnittstellenklasse
 eine abstrakte Impl-Klasse
 keine create-Methode in der
Factory-Klasse
Type.java:
public interface Type extends EObject {
…
}
TypeImpl.java:
public abstract class TypeImpl extends
MinimalEObjectImpl.Container implements Type {
…
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
197
Abstrakte und Schnittstellenklassen
Abstrakte Klasse:
 in EClass:

Attribut abstract = true
 im generierten Code:
 abstrakte
Implementierungsklasse
 keine create-Methode in der
Factory
Taentzer
Schnittstellenklasse:
 in EClass:
 Attribut interface = true
 Attribut instanceClass nicht
gesetzt
 im generierten Code:
 keine
Implementierungsklasse,
aber Schnittstellenklasse
 keine create-Methode in der
Factory
Modellgetriebene Entwicklung von mobilen Anwendungen
198
Vererbung
 Vererbung von EObject und
EObjectImpl
 Mehrfachvererbung im
Modell - Mehrfachvererbung
der entsprechenden
Schnittenstellen, Impl.Klasse erbt von nur einer
Impl.-Klasse
Entity.java:
public interface Entity extends Type
EntityImpl.java:
public class EntityImpl extends TypeImpl implements Entity
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
199
Structural Features: Zugriffsmethoden
Model.name
 Für jedes Attribut und jede
Referenz:
 eine getter-Methode
 falls changeable = true:
 eine setter-Methode
Model.java:
public interface Model extends EObject {
...
String getName();
void setName(String value);
...
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
200
Einfache Attribute
ModelImpl.java:
protected static final String
NAME_EDEFAULT = null;
protected String name = NAME_EDEFAULT;
public String getName() {
return name;
}
public void setName(String newName) {
String oldName = name;
name = newName;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this,Notification.SET,
MyCMPackage.MODEL_NAME,
oldName, name));
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
201
Subject-Observer-Prinzip
 Subject: Objekt aus dem
EMF-Modell
 Observer: andere Objekte,
die das EMF-Objekt
benutzen
Subject
Observer
Observer
Observer
Observer
 Observer müssen sich als
Listener anmelden.
 bei Änderung des Subjects:
 Benachrichtungung an alle
Observer
 Observer in EMF: Adapter
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
202
Unidirektionale Referenzen
Falls resolveProxies = true:
 Zusätzlicher Code zur Behandlung
von Proxy-Objekten
EntityImpl.java:
protected Entity extends_;
public Entity getExtends() {
//…
return extends_;
}
public void setExtends(Entity newExtends){
//…
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
203
Bidirektionale Referenzen
TypeImpl.java:
public void setModel(Model newModel) {
if (newModel != eInternalContainer || (eContainerFeatureID() !=
MCMPackage.TYPE__MODEL && newModel != null)) {
//…
NotificationChain msgs = null;
if (eInternalContainer() != null)
msgs = eBasicRemoveFromContainer(msgs);
if (newModel != null)
msgs = ((InternalEObject)newModel).eInverseAdd(this,
MyCMPackage.MODEL__ELEMENTS, Model.class, msgs);
msgs = basicSetModel(newModel, msgs);
if (msgs != null) msgs.dispatch();
}
else if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET,
MyCMPackage.TYPE__MODEL, newModel, newModel));
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
204
Factories
Eine Factory ist die zentrale Klasse, in der neue Objekte angelegt werden.
MyCMFactory.java:
public interface MyCMFactory extends EFactory {
MyCMFactory eINSTANCE =
org.xtext.example.mydsl.myCM.impl.MyCMFactoryImpl.init();
Model createModel();
ImportedClass createImportedClass();
SimpleType createSimpleType();
Entity createEntity();
Property createProperty();
Method createMethod();
Parameter createParameter();
Enumeration createEnumeration();
Literal createLiteral();
MyCMPackage getMyCMPackage();
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
205
Factories
• Es kann mehrere Implementierungen für eine Factory geben.
• Es gibt eine DefaultFactory.
public class MyCMFactoryImpl extends
EFactoryImpl implements MyCMFactory {
public static MyCMFactory init() {
try {
MyCMFactory theMyCMFactory =
(MyCMFactory)EPackage.Registry.INSTANCE.
getEFactory(MyCMPackage.eNS_URI);
if (theMyCMFactory != null) {
return theMyCMFactory;
}
}
catch (Exception exception) {
EcorePlugin.INSTANCE.log(exception);
}
return new MyCMFactoryImpl();
}
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
206
Codeerweiterung
 An verschiedensten Stellen
sind Codeerweiterungen
wünschenswert.
 Mit EMF kann kein Verhalten
modelliert werden -> Codeerweiterung
 Nach der Codeerweiterung
muss eine erneute Codegenerierung möglich sein.
 Generierte Methoden haben
ein @generated Tag.
Taentzer
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public boolean checkModel() {
// TODO: implement this method
// Ensure that you remove @generated or
mark it @generated NOT
throw new
UnsupportedOperationException();
}
 Nach manueller Codeerweiterung muss das Tag
@generated NOT gesetzt
werden.
Modellgetriebene Entwicklung von mobilen Anwendungen
207
Beispiel: Codeerweiterung
ModelImpl.java:
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*/
public boolean checkModel() {
EList<Type> modelTypes = this.getElements();
for(Type t1: modelTypes) {
for( Type t2: modelTypes){
if (t1 != t2)
if (t1.getName() == t2.getName())
return false;
}
}
return true;
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
208
Erstellung eines Instanzmodells
Beispiel: Erstellung eines Modells
tests.MyCMExample.java:
import
import
import
import
import
import
org.eclipse.emf.common.util.URI;
org.eclipse.emf.ecore.resource.Resource;
org.eclipse.emf.ecore.resource.ResourceSet;
org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
org.xtext.example.mydsl.myCM.;
//...
// Create a resource set to hold the resources.
ResourceSet resourceSet = new ResourceSetImpl();
// Register the appropriate resource factory to handle all file extensions.
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put
(Resource.Factory.Registry.DEFAULT_EXTENSION,
new XMIResourceFactoryImpl());
// Register the package to ensure it is available during loading.
resourceSet.getPackageRegistry().put
(MyCMPackage.eNS_URI,
MyCMPackage.eINSTANCE);
//...
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
209
Erstellung eines Instanzmodells
Beispiel: Erstellung eines Modells
tests.MyCMExample.java:
//...
// If there are no arguments, emit an appropriate usage message.
if (args.length == 0) {
System.out.println("Enter a list of file paths or URIs that have content
like this:");
try {
Resource resource =
resourceSet.createResource(URI.createURI("http://My.cm"));
Model root = MyCMFactory.eINSTANCE.createModel();
resource.getContents().add(root);
resource.save(System.out, null);
//Ab hier eigener Code
}
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
210
Erstellung eines eigenen Modells
Beispiel: Erstellung eines Modells
tests.MyCMExample.java:
//...
Model root = MyCMFactory.eINSTANCE.createModel();
resource.getContents().add(root);
MyCMFactory mFactory = MyCMFactory.eINSTANCE;
Model myModel = mFactory.createModel();
Entity person = mFactory.createEntity();
person.setModel(myModel);
person.setName("Person");
Entity address = mFactory.createEntity();
person.setModel(myModel);
person.setName("Address");
myModel.getElements().add(person);
myModel.getElements().add(address);
//...
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
211
Aufruf einer eignen Methode
Beispiel: Erstellung eines Modell
tests.MyCMExample.java:
//...
if (myModel.checkModel())
System.out.println("Alle Typnamen sind eindeutig.");
else
System.out.println("Es gibt zwei Typen mit dem gleichen Namen.");
//...
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
212
Einlesen eines Instanzmodells
public static void main(String[] args) {
MyCMPackageImpl.init();
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().
put(„cm", new XMIResourceFactoryImpl());
ResourceSet resourceSet = new ResourceSetImpl();
URI fileURI = URI.createURI("src/Test.cm");
Resource resource = resourceSet.getResource(fileURI, true);
Model myModel = (Model)resource.getContents().get(0);
}//main
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
213
JUnit
 Prinzipien:
 Zu jeder verfassten Klasse eine Testklasse entwerfen.
 Testcode und Anwendungscode sind strikt getrennt.
 Zum Schreiben von Tests werden lediglich benötigt:
 (TestSuite), TestCase, Assert
 Testsuiten dienen dazu, verschiedene Tests in einer
bestimmten Reihenfolge aufzurufen.
 Suiten können dazu verwendet werden,
verschiedene Klassen eines Paketes bzw. Projektes
auf einmal zu testen.
 Pakettests, Klassentests, Methodentests
public class MyCMTests extends TestSuite { …
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
214
Definition von Testfällen
 Testfälle werden in einer normalen Klasse erstellt.
 Framework führt die definierten Testfälle aus.
 Jeder Test wird gekapselt:
 Es kann ein Fixture definiert werden. Damit wird ein
wiederverwendbares Testobjekt festgehalten.
 Verschiedene Tests nutzen keine gemeinsamen Daten im
Fixture. Jeder Test hat sein eigenes Fixture.
 Assert für den Vergleich von Soll- mit Istwerten
 Wir prüfen, ob das Ergebnis eines Tests ein bestimmtes
Ergebnis zurückliefert.
 Typische Assert-Methoden: assertTrue, assertEqual,
assertNull
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
215
Beispiel für eine Testklasse
Fixture: Grundlage für ein
Testszenario. Dieses wird vor
einem Test auf und danach wieder
abgebaut.
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
216
Beispiel für einen zweiten Testfall
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
217
Zusammenfassung
 Die Beziehung zwischen Modell und Code ist recht
direkt. Die Codegenerierung lässt sich gut durch das
EMF-Modell steuern.
 Der generierte Code hat eine gute Qualität:
 Java-Konventionen werden eingehalten.
 Code Smells werden vermieden, wo möglich.
 Passende Design-Patterns werden verwendet.
 Generierter Code kann durch handgeschriebenen so
erweitert werden, dass der Generator diesen nicht
überschreibt.
Taentzer
Modellgetriebene Entwicklung von mobilen Anwendungen
218
Herunterladen