Software die verwendet wird, muss irgendwann geändert werden. Das ist keine neue Erkenntnis.
Dennoch finden wir uns sehr oft in der Situation wieder, dass sich einfache Anforderungen nur mit erheblichem Aufwand umsetzen lassen oder zu unerwarteten Ergebnissen führen.
SOLID Prinzipien können uns helfen das Design unserer Anwendungen zu verbessern.
4. JPA
lokale Beans
ohne Interface
alles Remote
CDI
konkrete CMPs
abstrakte CMPs
Java Server Pages
Java Server Faces
MVC 1.0Technologie darf
dem Design nicht
im Weg stehen!
6. Ein Anwendungsfall
public void erstelleProjekt(String projektName, String projektManager) {
if (projektName == null || "".equals(projektName)) {
throw new IllegalArgumentException("argument projektName darf nicht null oder leer sein");
}
if (projektManager == null || "".equals(projektManager)) {
throw new IllegalArgumentException("argument projektManager darf nicht null oder leer sein");
}
TypedQuery<User> query = entityManager.createQuery("select u from User u where u.name=:name", User.class);
query.setParameter("name", projektManager);
try {
User user = query.getSingleResult();
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
} catch (EntityNotFoundException e) {
throw new ProAdmException("projekt konnte nicht erstellt werden.");
}
}
7. Ein Anwendungsfall
public void erstelleProjekt(String projektName, String projektManager) {
if (projektName == null || "".equals(projektName)) {
throw new IllegalArgumentException("argument projektName darf nicht null oder leer sein");
}
if (projektManager == null || "".equals(projektManager)) {
throw new IllegalArgumentException("argument projektManager darf nicht null oder leer sein");
}
TypedQuery<User> query = entityManager.createQuery("select u from User u where u.name=:name", User.class);
query.setParameter("name", projektManager);
try {
User user = query.getSingleResult();
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
} catch (EntityNotFoundException e) {
throw new ProAdmException("projekt konnte nicht erstellt werden.");
}
}
Format & Fehlermeldung
8. Ein Anwendungsfall
@Inject
private SolidValidator validator;
public void erstelleProjekt(String projektName, String projektManager) {
validator.validateProjektName(projektName);
validator.validateProjektManagerName(projektManager);
TypedQuery<User> query = entityManager.createQuery("select u from User u where u.name=:name", User.class);
query.setParameter("name", projektManager);
try {
User user = query.getSingleResult();
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
} catch (EntityNotFoundException e) {
throw new ProAdmException("projekt konnte nicht erstellt werden.");
}
}
9. Ein Anwendungsfall
@Inject
private SolidValidator validator;
public void erstelleProjekt(String projektName, String projektManager) {
validator.validateProjektName(projektName);
validator.validateProjektManagerName(projektManager);
TypedQuery<User> query = entityManager.createQuery("select u from User u where u.name=:name", User.class);
query.setParameter("name", projektManager);
try {
User user = query.getSingleResult();
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
} catch (EntityNotFoundException e) {
throw new ProAdmException("projekt konnte nicht erstellt werden.");
}
}
JPA, JPA-QL & ExceptionHandling
10. Ein Anwendungsfall
@Inject
private SolidValidator validator;
@Inject
private BenutzerRepository users;
public void erstelleProjekt(String projektName, String projektManager) {
validator.validateProjektName(projektName);
validator.validateProjektManagerName(projektManager);
Benutzer user = users.getProjektManager(projektManager);
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
}
11. Ein Anwendungsfall
@Inject
private SolidValidator validator;
@Inject
private BenutzerRepository users;
public void erstelleProjekt(String projektName, String projektManager) {
validator.validateProjektName(projektName);
validator.validateProjektManagerName(projektManager);
Benutzer user = users.getProjektManager(projektManager);
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
entityManager.persist(projekt);
}
falsches
Abstraktionslevel
12. Ein Anwendungsfall
@Inject
private SolidValidator validator;
@Inject
private BenutzerRepository users;
@Inject
private ProjektRepository projects;
public void erstelleProjekt(String projektName, String projektManager) {
validator.validateProjektName(projektName);
validator.validateProjektManagerName(projektManager);
Benutzer user = users.getProjektManager(projektManager);
Projekt projekt = new Projekt(projektName);
projekt.setProjektManager(user);
projects.saveProjekt(projekt);
}
25. Ein Code Beispiel
public enum Typ {
ARBEITSPAKET, MEILENSTEIN
}
…
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete){
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if(Typ.MEILENSTEIN == arbeitspaket.getTyp()){
return arbeitspaket;
}
}
return null;
}
26. Ein Code Beispiel
public enum Typ {
ARBEITSPAKET, MEILENSTEIN
}
…
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete){
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if(Typ.MEILENSTEIN == arbeitspaket.getTyp()){
return arbeitspaket;
}
}
return null;
}
Neue Anforderung: ProjektStart undProjektEnde, beides Meilensteine
27. Ein Code Beispiel
public enum Typ {
ARBEITSPAKET, MEILENSTEIN
}
…
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete){
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if(Typ.MEILENSTEIN == arbeitspaket.getTyp()){
return arbeitspaket;
}
}
return null;
}
Neue Anforderung: ProjektStart undProjektEnde, beides Meilensteine
gute Idee
28. Ein Code Beispiel
public enum Typ {
ARBEITSPAKET, MEILENSTEIN
}
…
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete){
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if(Typ.MEILENSTEIN == arbeitspaket.getTyp()){
return arbeitspaket;
}
}
return null;
}
Neue Anforderung: ProjektStart undProjektEnde, beides Meilensteine
gute Idee
hier ein Problem
29. Ein Code Beispiel
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete) {
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if (arbeitspaket.getTyp().isMeilenstein()) {
return arbeitspaket;
}
}
return null;
}
30. Ein Code Beispiel
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete) {
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if (arbeitspaket.getTyp().isMeilenstein()) {
return arbeitspaket;
}
}
return null;
}
public interface ArbeitspaketTyp {
boolean isMeilenstein();
boolean isArbeitspaket();
}
31. Ein Code Beispiel
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete) {
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if (arbeitspaket.getTyp().isMeilenstein()) {
return arbeitspaket;
}
}
return null;
}
public interface ArbeitspaketTyp {
boolean isMeilenstein();
boolean isArbeitspaket();
}
public enum Typ implements ArbeitspaketTyp{
ARBEITSPAKET {
@Override
public boolean isArbeitspaket() {
return true;
}
},
PROJEKTSTART {
@Override
public boolean isMeilenstein() {
return true;
}
},
PROJEKTENDE {
@Override
public boolean isMeilenstein() {
return true;
}
},
MEILENSTEIN {
@Override
public boolean isMeilenstein() {
return true;
}
};
public boolean isMeilenstein() {
return false;
}
public boolean isArbeitspaket() {
return false;
}
}
32. Ein Code Beispiel
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete) {
for (Arbeitspaket arbeitspaket : arbeitspakete) {
if (arbeitspaket.getTyp().isMeilenstein()) {
return arbeitspaket;
}
}
return null;
}
public interface ArbeitspaketTyp {
boolean isMeilenstein();
boolean isArbeitspaket();
}
public enum Typ implements ArbeitspaketTyp{
ARBEITSPAKET {
@Override
public boolean isArbeitspaket() {
return true;
}
},
PROJEKTSTART {
@Override
public boolean isMeilenstein() {
return true;
}
},
PROJEKTENDE {
@Override
public boolean isMeilenstein() {
return true;
}
},
MEILENSTEIN {
@Override
public boolean isMeilenstein() {
return true;
}
};
public boolean isMeilenstein() {
return false;
}
public boolean isArbeitspaket() {
return false;
}
}
Closed for Modification,
Open for Extension
40. Darf Beginn- und Enddatum
eines Meilensteins
voneinander abweichen?
Der zweite Versuch
Arbeitspaket
+getEndDate() : Date
+setDuration(int)
Meilenstein
+setDuration(int)
41. Darf Beginn- und Enddatum
eines Meilensteins
voneinander abweichen?
Der zweite Versuch
Arbeitspaket
+getEndDate() : Date
+setDuration(int)
Meilenstein
+setDuration(int)
public class Meilenstein extends Arbeitspaket{
public void setDuration(int days){
throw new ProAdmException("unsupported operation.");
}
}
42. Darf Beginn- und Enddatum
eines Meilensteins
voneinander abweichen?
Der zweite Versuch
Arbeitspaket
+getEndDate() : Date
+setDuration(int)
Meilenstein
+setDuration(int)
public class Meilenstein extends Arbeitspaket{
public void setDuration(int days){
throw new ProAdmException("unsupported operation.");
}
}
public class Meilenstein extends Arbeitspaket{
public void setDuration(int days){
// ignore
}
}
FAIL!
43. Darf Beginn- und Enddatum
eines Meilensteins
voneinander abweichen?
Der zweite Versuch
Arbeitspaket
+getEndDate() : Date
+setDuration(int)
Meilenstein
+setDuration(int)
public class Meilenstein extends Arbeitspaket{
public void setDuration(int days){
throw new ProAdmException("unsupported operation.");
}
}
public class Meilenstein extends Arbeitspaket{
public void setDuration(int days){
// ignore
}
}
FAIL!
FAIL!
60. Hinweise
Ich muss Methoden implementieren
die in der konkreten Klasse gar
keine Funktion erfüllen.
Ich muss Module importieren
die in meiner Anwendung
gar keine Funktion erfüllen.
61. The Dependency-Inversion Principle
High Level Modules should not depend on low-level modules. Both should
depend on abstractions.
Abstractions should not depend on details. Details should depend upon
abstractions.