SOLID Prinzipien, Designgrundlagen objektorientierter Systeme

Post on 12-Apr-2017

539 views 4 download

Transcript of SOLID Prinzipien, Designgrundlagen objektorientierter Systeme

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