Domain-Specific Languages in der Praxis
-
Upload
sven-efftinge -
Category
Technology
-
view
782 -
download
2
Transcript of Domain-Specific Languages in der Praxis
User-Interface
Programmierung mit
externen DSLs
Sven Efftinge, Jan Köhnlein (itemis AG)
1
• DB-Anwendung
• Oracle DB
• OracleForms
Ausgangslage
2
• Oracle DB
• Java Rich Client
• JPA
• Spring
• Swing / JGoodies Forms
Zielarchitektur
3
Mengengerüst
• 1722 Tabellen
• 19572 Spalten
• über 300 Forms
Aufgabe• Abstraktionen finden
• Code vereinfachen
4
Domänenmodel5
@SuppressWarnings("serial")
@Entity
@Table(name = "BUCHUNGSKREISE_F")
public class BuchungskreiseF extends AbstractEntity implements Serializable {
!
! @SuppressWarnings("unused")
! @Id
! @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bkrIdSeq")
! @SequenceGenerator(name = "bkrIdSeq", sequenceName = "BKR_SEQ", allocationSize = 1)
! @Column(name = "BKR_ID", nullable = false)
! private Long bkrId;
! public Long getBkrId() {
! ! return bkrId;
! }
! public void setBkrId(final Long bkrId) {
! ! this.bkrId = bkrId;
! }
! @Column(name = "KONTO_NR", nullable = false, length = 45)
! private String kontoNr;
! public String getKontoNr() {
! ! return kontoNr;
! }
! public void setKontoNr(final String kontoNr) {
! ! String oldValue = this.kontoNr;
! ! this.kontoNr = kontoNr;
! ! firePropertyChangeEvent("kontoNr", oldValue, this.kontoNr);
! }
! @Column(name = "RG_NR_BKR_IDENTIFIKATOR", nullable = false, length = 1)
! private Long rgNrBkrIdentifikator;
! public Long getRgNrBkrIdentifikator() {
! ! return rgNrBkrIdentifikator;
! }
! public void setRgNrBkrIdentifikator(final Long rgNrBkrIdentifikator) {
! ! Long oldValue = this.rgNrBkrIdentifikator;
! ! this.rgNrBkrIdentifikator = rgNrBkrIdentifikator;
! ! firePropertyChangeEvent("rgNrBkrIdentifikator", oldValue,
! ! ! ! this.rgNrBkrIdentifikator);
! }
! @Column(name = "REFERENZCODE_KONTO_NR", nullable = false, length = 45)
! private String referenzcodeKontoNr;
! public String getReferenzcodeKontoNr() {
! ! return referenzcodeKontoNr;
! }
! public void setReferenzcodeKontoNr(final String referenzcodeKontoNr) {
! ! String oldValue = this.referenzcodeKontoNr;
! ! this.referenzcodeKontoNr = referenzcodeKontoNr;
! ! firePropertyChangeEvent("referenzcodeKontoNr", oldValue,
! ! ! ! this.referenzcodeKontoNr);
! }
! @Column(name = "PC_NR_RG", nullable = false, length = 45)
! private String pcNrRg;
! public String getPcNrRg() {
! ! return pcNrRg;
! }
! public void setPcNrRg(final String pcNrRg) {
! ! String oldValue = this.pcNrRg;
! ! this.pcNrRg = pcNrRg;
! ! firePropertyChangeEvent("pcNrRg", oldValue, this.pcNrRg);
! }
! @Column(name = "PC_NR_PVA", nullable = false, length = 45)
! private String pcNrPva;
! public String getPcNrPva() {
! ! return pcNrPva;
! }
! public void setPcNrPva(final String pcNrPva) {
! ! String oldValue = this.pcNrPva;
! ! this.pcNrPva = pcNrPva;
! ! firePropertyChangeEvent("pcNrPva", oldValue, this.pcNrPva);
! }
! @Column(name = "MWST_NR", nullable = false, length = 10)
! private Long mwstNr;
! public Long getMwstNr() {
! ! return mwstNr;
! }
! public void setMwstNr(final Long mwstNr) {
! ! Long oldValue = this.mwstNr;
! ! this.mwstNr = mwstNr;
! ! firePropertyChangeEvent("mwstNr", oldValue, this.mwstNr);
! }
! @Column(name = "J_VERSION", nullable = false, insertable = false, updatable = false,
length = 22)
!
@org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.ALWAYS)
! @Version
! private Long jVersion;
! public Long getJVersion() {
! ! return jVersion;
! }
! public void setJVersion(final Long jVersion) {
! ! Long oldValue = this.jVersion;
! ! this.jVersion = jVersion;
! ! firePropertyChangeEvent("jVersion", oldValue, this.jVersion);
! }
! @Column(name = "IBAN", nullable = false, length = 34)
! private String iban;
! public String getIban() {
! ! return iban;
! }
! public void setIban(final String iban) {
! ! String oldValue = this.iban;
! ! this.iban = iban;
! ! firePropertyChangeEvent("iban", oldValue, this.iban);
! }
! @Column(name = "BUCHUNGSKREIS_NR", nullable = false, length = 4)
! private Long buchungskreisNr;
! public Long getBuchungskreisNr() {
! ! return buchungskreisNr;
! }
! public void setBuchungskreisNr(final Long buchungskreisNr) {
! ! Long oldValue = this.buchungskreisNr;
! ! this.buchungskreisNr = buchungskreisNr;
! ! firePropertyChangeEvent("buchungskreisNr", oldValue,
! ! ! ! this.buchungskreisNr);
! }
}
Entity
BuchungskreiseF
Id
Long bkrId
String kontoNr
nullable = false
nullable = false 45
BUCHUNGSKREISE_F
BKR_ID
KONTO_NR
BKR_SEQ
6
@SuppressWarnings("serial")
@Entity
@Table(name = "BUCHUNGSKREISE_F")
public class BuchungskreiseF extends AbstractEntity implements Serializable {
!
! @SuppressWarnings("unused")
! @Id
! @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bkrIdSeq")
! @SequenceGenerator(name = "bkrIdSeq", sequenceName = "BKR_SEQ", allocationSize = 1)
! @Column(name = "BKR_ID", nullable = false)
! private Long bkrId;
! public Long getBkrId() {
! ! return bkrId;
! }
! public void setBkrId(final Long bkrId) {
! ! this.bkrId = bkrId;
! }
! @Column(name = "KONTO_NR", nullable = false, length = 45)
! private String kontoNr;
! public String getKontoNr() {
! ! return kontoNr;
! }
! public void setKontoNr(final String kontoNr) {
! ! String oldValue = this.kontoNr;
! ! this.kontoNr = kontoNr;
! ! firePropertyChangeEvent("kontoNr", oldValue, this.kontoNr);
! }
! @Column(name = "RG_NR_BKR_IDENTIFIKATOR", nullable = false, length = 1)
! private Long rgNrBkrIdentifikator;
! public Long getRgNrBkrIdentifikator() {
! ! return rgNrBkrIdentifikator;
! }
! public void setRgNrBkrIdentifikator(final Long rgNrBkrIdentifikator) {
! ! Long oldValue = this.rgNrBkrIdentifikator;
! ! this.rgNrBkrIdentifikator = rgNrBkrIdentifikator;
! ! firePropertyChangeEvent("rgNrBkrIdentifikator", oldValue,
! ! ! ! this.rgNrBkrIdentifikator);
! }
! @Column(name = "REFERENZCODE_KONTO_NR", nullable = false, length = 45)
! private String referenzcodeKontoNr;
! public String getReferenzcodeKontoNr() {
! ! return referenzcodeKontoNr;
! }
! public void setReferenzcodeKontoNr(final String referenzcodeKontoNr) {
! ! String oldValue = this.referenzcodeKontoNr;
! ! this.referenzcodeKontoNr = referenzcodeKontoNr;
! ! firePropertyChangeEvent("referenzcodeKontoNr", oldValue,
! ! ! ! this.referenzcodeKontoNr);
! }
! @Column(name = "PC_NR_RG", nullable = false, length = 45)
! private String pcNrRg;
! public String getPcNrRg() {
! ! return pcNrRg;
! }
! public void setPcNrRg(final String pcNrRg) {
! ! String oldValue = this.pcNrRg;
! ! this.pcNrRg = pcNrRg;
! ! firePropertyChangeEvent("pcNrRg", oldValue, this.pcNrRg);
! }
! @Column(name = "PC_NR_PVA", nullable = false, length = 45)
! private String pcNrPva;
! public String getPcNrPva() {
! ! return pcNrPva;
! }
! public void setPcNrPva(final String pcNrPva) {
! ! String oldValue = this.pcNrPva;
! ! this.pcNrPva = pcNrPva;
! ! firePropertyChangeEvent("pcNrPva", oldValue, this.pcNrPva);
! }
! @Column(name = "MWST_NR", nullable = false, length = 10)
! private Long mwstNr;
! public Long getMwstNr() {
! ! return mwstNr;
! }
! public void setMwstNr(final Long mwstNr) {
! ! Long oldValue = this.mwstNr;
! ! this.mwstNr = mwstNr;
! ! firePropertyChangeEvent("mwstNr", oldValue, this.mwstNr);
! }
! @Column(name = "J_VERSION", nullable = false, insertable = false, updatable = false,
length = 22)
!
@org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.ALWAYS)
! @Version
! private Long jVersion;
! public Long getJVersion() {
! ! return jVersion;
! }
! public void setJVersion(final Long jVersion) {
! ! Long oldValue = this.jVersion;
! ! this.jVersion = jVersion;
! ! firePropertyChangeEvent("jVersion", oldValue, this.jVersion);
! }
! @Column(name = "IBAN", nullable = false, length = 34)
! private String iban;
! public String getIban() {
! ! return iban;
! }
! public void setIban(final String iban) {
! ! String oldValue = this.iban;
! ! this.iban = iban;
! ! firePropertyChangeEvent("iban", oldValue, this.iban);
! }
! @Column(name = "BUCHUNGSKREIS_NR", nullable = false, length = 4)
! private Long buchungskreisNr;
! public Long getBuchungskreisNr() {
! ! return buchungskreisNr;
! }
! public void setBuchungskreisNr(final Long buchungskreisNr) {
! ! Long oldValue = this.buchungskreisNr;
! ! this.buchungskreisNr = buchungskreisNr;
! ! firePropertyChangeEvent("buchungskreisNr", oldValue,
! ! ! ! this.buchungskreisNr);
! }
}
Entity
BuchungskreiseF
Id
Long bkrId
String kontoNr
nullable = false
nullable = false 45
BUCHUNGSKREISE_F
BKR_ID
KONTO_NR
BKR_SEQ
6
entity BuchungskreiseF (id=bkrId sequenceName=BKR_SEQ) {
String kontoNr
Long rgNrBkrIdentifikator
String referenzcodeKontoNr
}
BUCHUNGSKREISE_F
RG_NR_BKR_IDENTIFIKATOR
External DSL mit TMF Xtext
7
entity BuchungskreiseF (id=bkrId sequenceName=BKR_SEQ) {}
Database Schema
TABLE BUCHUNGSKREISE_F
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
8
9
Formulare
10
11
public class PersonenForm extends
Form<Personen> {
11
public class PersonenForm extends
Form<Personen> {
public class PersonenHauptSubForm extends
SubForm<Personen> {
11
public class PersonenForm extends
Form<Personen> {
public class PersonenHauptSubForm extends
SubForm<Personen> {
private JComponent
vornameTextField;
11
public class PersonenForm extends
Form<Personen> {
public class PersonenHauptSubForm extends
SubForm<Personen> {
private JComponent
vornameTextField;
@Override
protected void initComponents() {
...
vornameTextField =
builder.createTextField(desc.vorname(),
Editable.PROPERTY_DEFAULT,
MANDATORY);
! ! gepardBuilder.setNoLeadingBlanks
(vornameTextField);
11
public class PersonenForm extends
Form<Personen> {
public class PersonenHauptSubForm extends
SubForm<Personen> {
private JComponent
vornameTextField;
@Override
protected void initComponents() {
...
vornameTextField =
builder.createTextField(desc.vorname(),
Editable.PROPERTY_DEFAULT,
MANDATORY);
! ! gepardBuilder.setNoLeadingBlanks
(vornameTextField);
@Override
! protected JComponent buildPanel() {
TwoColumnsPanelBuilder builder =
TwoColumnsPanelBuilder.instance(getBuilderFactory(),
getResourceMap());
...
builder.add("vorname", vornameTextField);
!
11
GUI-Builder
• WYSIWYG
• teilweise mit Databinding
• zu viele Freiheitsgrade
• Referenz auf Java-Code,nicht auf Domain-DSL
12
Graphische GUI-DSL
13
Graphische GUI-DSL
13
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
ID NAME PROP1 DATE ATTR1
model : gepard;
import "platform:/resource/com.affichage.it21.gp.dao/src/main/model/types.dao"
com.affichage.it21.gp.dao {
! flaechen {
! ! readOnly entity WaehrungF (id =(rvLowValue)) {
! ! }
! ! readOnly entity GepardVerwendungPvF (id =(pvOid)) {
! ! ! temporal manyToOne GeschpartnerAllBsF geschpartner (joinColum
! ! }
! }
!
! verkauf {
! ! readOnly entity GepardVerwendungKdvtF (id =(kdvtId)) {
! ! ! temporal notNull manyToOne GeschpartnerAllBsF geschpartner (j
! ! ! notNull Number istLangfrist (castTo=Boolean)
! ! ! notNull Number istLokaldispo (castTo=Boolean)
! ! }
! }
! gepard {
! ! readOnly entity AbcKundenF (id = (rvLowValue)) {
! !
Referenzen
14
Validierung
15
16
addValidator(new Validator<Institutionen>() {
! ! ! @Override
! ! ! public ValidationResult validate(final Institutionen institution) {
! ! ! ! final ValidationResult result = new ValidationResult();
! ! ! ! if (institution != null
&& institution.getEsrNr() != null
! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(
Long.parseLong(institution.getEsrNr())) {
! ! ! ! ! result.add(new SimpleValidationMessage(
getResourceMap().getString("validation.esr.msg"),
Severity.ERROR, getModel(Institutionen.DESC.esrNr())));
! ! ! ! }
! ! ! ! return result;
! ! ! }
! ! });
17
addValidator(new Validator<Institutionen>() {
! ! ! @Override
! ! ! public ValidationResult validate(final Institutionen institution) {
! ! ! ! final ValidationResult result = new ValidationResult();
! ! ! ! if (institution != null
&& institution.getEsrNr() != null
! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(
Long.parseLong(institution.getEsrNr())) {
! ! ! ! ! result.add(new SimpleValidationMessage(
getResourceMap().getString("validation.esr.msg"),
Severity.ERROR, getModel(Institutionen.DESC.esrNr())));
! ! ! ! }
! ! ! ! return result;
! ! ! }
! ! });
Framework-Code
17
addValidator(new Validator<Institutionen>() {
! ! ! @Override
! ! ! public ValidationResult validate(final Institutionen institution) {
! ! ! ! final ValidationResult result = new ValidationResult();
! ! ! ! if (institution != null
&& institution.getEsrNr() != null
! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(
Long.parseLong(institution.getEsrNr())) {
! ! ! ! ! result.add(new SimpleValidationMessage(
getResourceMap().getString("validation.esr.msg"),
Severity.ERROR, getModel(Institutionen.DESC.esrNr())));
! ! ! ! }
! ! ! ! return result;
! ! ! }
! ! });
Framework-Code
Nullpointer abfragen
17
addValidator(new Validator<Institutionen>() {
! ! ! @Override
! ! ! public ValidationResult validate(final Institutionen institution) {
! ! ! ! final ValidationResult result = new ValidationResult();
! ! ! ! if (institution != null
&& institution.getEsrNr() != null
! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(
Long.parseLong(institution.getEsrNr())) {
! ! ! ! ! result.add(new SimpleValidationMessage(
getResourceMap().getString("validation.esr.msg"),
Severity.ERROR, getModel(Institutionen.DESC.esrNr())));
! ! ! ! }
! ! ! ! return result;
! ! ! }
! ! });
Framework-Code
Nullpointer abfragen
Static imports
17
addValidator(new Validator<Institutionen>() {
! ! ! @Override
! ! ! public ValidationResult validate(final Institutionen institution) {
! ! ! ! final ValidationResult result = new ValidationResult();
! ! ! ! if (institution != null
&& institution.getEsrNr() != null
! ! ! ! ! && !CheckUtils.checkPcKontoNrPruefziffer(
Long.parseLong(institution.getEsrNr())) {
! ! ! ! ! result.add(new SimpleValidationMessage(
getResourceMap().getString("validation.esr.msg"),
Severity.ERROR, getModel(Institutionen.DESC.esrNr())));
! ! ! ! }
! ! ! ! return result;
! ! ! }
! ! });
Framework-Code
Nullpointer abfragen
Static imports
Bibliotheks Methoden definieren und benutzen
error("validation.esr.msg", desc.esrNr());
17
! void checkEsrMsg() {
! ! if (!checkPcKontoNrPruefziffer(parseLong(_this.getEsrNr())))
! ! ! error("validation.esr.msg",desc.esrNr());
! }
Internal DSL mit Java
• Junit-like
• fängt Nullpointer-Exceptions
18
Warum Abstrahieren?
19
0 TZ
60 TZ
120 TZ
180 TZ
240 TZ
300 TZ
0 20 40
29248 (DSL)+ 49948 (Codegenerator)+ 170 * Anzahl Entitäten
7303 * Anzahl Entitäten
Anzahl Zeichen im Vergleich (nur Entity-DSL)
Mit DSL
Ohne DSL
11 Entitäten
20
67%
8%
7%
5%
6%5%2%
RequirementsSpecificationDesignCodingUnit TestingIntegrationMaintenance
Software Life-Cycle Costs (Schach 2002)
21
Fragen?
22