Post on 12-Apr-2017
transcript
SOLID PRINCIPLESDesigngrundlagen objektorientierter Systeme
–Fred Brooks, The Mythical Man Month (Jänner 1975)
„All successful software gets changed.“
JPA
lokale Beans
ohne Interface
alles Remote
CDIkonkrete CMPs
abstrakte CMPs
Java Server Pages
Java Server Faces
MVC 1.0
JPA
lokale Beans
ohne Interface
alles Remote
CDIkonkrete CMPs
abstrakte CMPs
Java Server Pages
Java Server Faces
MVC 1.0Technologie darf
dem Design nicht
im Weg stehen!
The Single Responsibility Principle
A class should have only one reason to change.
Ein Anwendungsfallpublic 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.");}
}
Ein Anwendungsfallpublic 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
Ein Anwendungsfall@Injectprivate 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.");}
}
Ein Anwendungsfall@Injectprivate 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
Ein Anwendungsfall@Injectprivate SolidValidator validator;
@Injectprivate 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);}
Ein Anwendungsfall@Injectprivate SolidValidator validator;
@Injectprivate 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
Ein Anwendungsfall@Injectprivate SolidValidator validator;
@Injectprivate BenutzerRepository users;
@Injectprivate 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);}
Hinweise
Viele Methoden verwenden das selbe
Feld?
Hinweise
Viele Methoden verwenden das selbe
Feld?
private
Hinweise
Viele Methoden verwenden das selbe
Feld?
Technologie & Fachlichkeit
in derselben Klasse?
The Open/Closed Principle
Software entities should be open for extension but closed for modification.
Ein Anwendungsfall
Client Server
Ein Anwendungsfall
Client Server
Welche Methoden darf ich verwenden?
Ein Anwendungsfall
Client Server
Welche Methoden darf ich verwenden?
Welche Methoden werden eigentlich benötigt?
Ein Anwendungsfall
ClientInterfaceClient
Server
Ein Anwendungsfall
ClientInterfaceClient
Server
gehören zusammen
Ein Anwendungsfall
ClientInterfaceClient
Server
gehören zusammen
bestimmt die Funktion
Ein Anwendungsfall
ClientInterfaceClient
Server
gehören zusammen
bestimmt die Funktion
closed formodification
Ein Anwendungsfall
ClientInterfaceClient
Server
gehören zusammen
bestimmt die Funktion
closed formodification
open forextension
Ein Code Beispielpublic enum Typ {
ARBEITSPAKET, MEILENSTEIN}
…
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete){for (Arbeitspaket arbeitspaket : arbeitspakete) {
if(Typ.MEILENSTEIN == arbeitspaket.getTyp()){return arbeitspaket;
}}return null;
}
Ein Code Beispielpublic 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 und ProjektEnde, beides Meilensteine
Ein Code Beispielpublic 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 und ProjektEnde, beides Meilensteine
gute Idee
Ein Code Beispielpublic 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 und ProjektEnde, beides Meilensteine
gute Idee
hier ein Problem
Ein Code Beispiel
public Arbeitspaket findeErstenMeilenstein(List<Arbeitspaket> arbeitspakete) {for (Arbeitspaket arbeitspaket : arbeitspakete) {
if (arbeitspaket.getTyp().isMeilenstein()) {return arbeitspaket;
}}return null;
}
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();
}
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 {
@Overridepublic boolean isArbeitspaket() {
return true;}
},PROJEKTSTART {
@Overridepublic boolean isMeilenstein() {
return true;}
},PROJEKTENDE {
@Overridepublic boolean isMeilenstein() {
return true;}
},MEILENSTEIN {
@Overridepublic boolean isMeilenstein() {
return true;}
};
public boolean isMeilenstein() {return false;
}
public boolean isArbeitspaket() {return false;
}}
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 {
@Overridepublic boolean isArbeitspaket() {
return true;}
},PROJEKTSTART {
@Overridepublic boolean isMeilenstein() {
return true;}
},PROJEKTENDE {
@Overridepublic boolean isMeilenstein() {
return true;}
},MEILENSTEIN {
@Overridepublic boolean isMeilenstein() {
return true;}
};
public boolean isMeilenstein() {return false;
}
public boolean isArbeitspaket() {return false;
}}
Closed fo
r Modification
,
Open for
Extensi
on
Hinweise
Das Hinzufügen oder Entfernen einer Funktion muss
in vielen Klassen nachgezogen werden.
HinweiseSchnittstellen
entsprechen den Anforderungen des Dienstanbieters
und nicht des Benutzers!
Das Hinzufügen oder Entfernen einer Funktion muss
in vielen Klassen nachgezogen werden.
The Liskov Substitution Principle
Subtypes must be substitutable for their base types
Ein Anwendungsfall
Arbeitspaket+getEndDate() : Date +setDuration(int)
Ein Anwendungsfall
Arbeitspaket+getEndDate() : Date +setDuration(int)
Neue Anforderung: Wir benötigen
eine Klasse „Meilensteine“
Der erste Versuch
Arbeitspaket+getEndDate() : Date +setDuration(int)
Meilenstein
Der erste Versuch
Arbeitspaket+getEndDate() : Date +setDuration(int)
Meilenstein
Darf Beginn- und Enddatum eines Meilensteins
voneinander abweichen?
Darf Beginn- und Enddatum eines Meilensteins
voneinander abweichen?
Der zweite Versuch
Arbeitspaket+getEndDate() : Date +setDuration(int)
Meilenstein+setDuration(int)
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.");}
}
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!
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!
Der dritte Versuch
Arbeitspaket
+setDuration(int)
Meilenstein
Phase
+getEndDate() : Date
Der dritte Versuch
Arbeitspaket
+setDuration(int)
Meilenstein
Phase
+getEndDate() : Date
identisches Verhalten
Der dritte Versuch
Arbeitspaket
+setDuration(int)
Meilenstein
Phase
+getEndDate() : Date
identisches Verhalten
unterschiedliches Verhalten
Der dritte Versuch
Arbeitspaket
+setDuration(int)
Meilenstein
Phase
+getEndDate() : Date
identisches Verhalten
unterschiedliches Verhalten
Subtypes are substitu
table
for their base types
Hinweise
Das Verhalten der Elternklasse(n) führt zu Fehlern.
Hinweise
Das Verhalten der Elternklasse(n) führt zu Fehlern.
IsA-Beziehung (extends) zwingt mich Methoden zu
überschreiben.
The Interface Segregation Principle
Clients should not be forced to depend on methods they do not use.
Ein Anwendungsfall
SecurityUser
Projekt+getRoles() : Role[] +getProjekte() : Projekt[]
Ein Anwendungsfall
SecurityUser
Projekt+getRoles() : Role[] +getProjekte() : Projekt[]
unnötige Kopplung!
Ein Anwendungsfall
SecurityUser
Projekt+getRoles() : Role[] +getProjekte() : Projekt[]
unnötige Kopplung!
unnötige Kopplung!
Ein Anwendungsfall
UserImpl
+getProjekte() : Projekt[]
ProjektManager
SecurityUser
Projekt+getRoles() : Role[]
Ein Anwendungsfall
UserImpl
+getProjekte() : Projekt[]
ProjektManager
SecurityUser
Projekt+getRoles() : Role[] FAIL!
Variante 1: Interfaces
SecurityUser
Projekt+getRoles() : Role[]
ProjektUser
+getProjekte() : Projekt[]
ProjektManager
Variante 2: Delegierung
SecurityUser
Projekt
+getRoles() : Role[]
+getProjekte() : Projekt[]
ProjektManager
Variante 3: Vererbung
SecurityUser
Projekt
+getRoles() : Role[]
+getProjekte() : Projekt[]
ProjektManager
Hinweise
Ich muss Methoden implementieren die in der konkreten Klasse gar
keine Funktion erfüllen.
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.
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.
Schichten
UseCase Layer
Service Layer
Utility Layer
Schichten
UseCase Layer
Service Layer
Utility Layer
High Level „Erstelle Meilenstein“
Schichten
UseCase Layer
Service Layer
Utility Layer
High Level „Erstelle Meilenstein“
Low Level „SQL INSERT“
Invertierte Schichten
Service Layer
Utility Layer
UseCase Service
Interface
Service Interface
UseCase Layer
Invertierte Schichten
Service Layer
Utility Layer
UseCase Service
Interface
Service Interface
UseCase Layer Abstractions
Invertierte Schichten
Service Layer
Utility Layer
UseCase Service
Interface
Service Interface
UseCase Layer Abstractions
Details
Invertierte Schichten
Service Layer
Utility Layer
UseCase Service
Interface
Service Interface
UseCase Layer High
Low
Clean Architecture
High
Low
Clean Architecture
High
Lowui, db, web
Clean Architecture
High
Lowui, db, web
online, backoffice, batch
Clean Architecture
High
Lowui, db, web
online, backoffice, batch
domain services, entities
Clean Architecture
fachlich High
Lowui, db, web
online, backoffice, batch
domain services, entities
Clean Architecture
fachlich
technisch
High
Lowui, db, web
online, backoffice, batch
domain services, entities
Clean Architecture
Abhängigkeiten
fachlich
technisch
High
Lowui, db, web
online, backoffice, batch
domain services, entities
Hinweise
Ich kann nicht Testen wenn
System X nicht zur Verfügung steht.
Hinweise
Ich kann nicht Testen wenn
System X nicht zur Verfügung steht.
Ich kann die Fachlichkeit
nicht erkennen und erhalten.
Vielen Dank
mario.rodler@gmx.net