Date post: | 13-Apr-2017 |
Category: |
Software |
Upload: | roland-mast |
View: | 159 times |
Download: | 1 times |
SOLID mit Java 8
Java Forum Stuttgart 2016
Roland MastSybit GmbHAgiler Software-ArchitektScrum [email protected]
Roland MastSybit GmbHAgiler [email protected]
5 Principles
• Single responsibility
• Open Closed
• Liskov Substitution
• Interface Segregation
• Dependency Inversion
SOLID und Uncle Bob
Single ResponsibilityPrinciple?Single ResponsibilityPrinciple?
A class should have one, and only one, reason to change.
A class should have one, and only one, reason to change.
// Strings in einem Array nach deren Länge sortieren
Arrays.sort(strings, new Comparator<String>() {public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());}
});
Arrays.sort(strings, (a, b) -> Integer.compare(a.length(), b.length()));
// Strings alphabetisch sortieren
Arrays.sort(strings, String::compareToIgnoreCase);
Lambdas
List<Person> persons = Arrays.asList(// name, age, sizenew Person("Max", 18, 1.9),new Person("Peter", 23, 1.8),new Person("Pamela", 23, 1.6),new Person("David", 12, 1.5));
Double averageSize = persons.stream().filter(p -> p.age >= 18).collect(Collectors.averagingDouble(p -> p.size));
Lambdas in Streams
Iteration über die Daten
Filtern anstattif (age >= 18) {
keep();
}Collectors berechnet den Durchschnitt
Single Responsibility
public interface BinaryOperation {long identity();long binaryOperator(long a, long b);
}
public class Sum implements BinaryOperation {@Overridepublic long identity() { return 0L; }
@Overridepublic long binaryOperator(long a, long b) {
return a + b;}
}
Single Responsibility
private long[] data;public long solve() {
int threadCount = Runtime.getRuntime().availableProcessors();ForkJoinPool pool = new ForkJoinPool(threadCount);pool.invoke(this);return res;
}protected void compute() {
if (data.length == 1) {res=operation.binaryOperator(operation.identity(), data[0]);
} else {int midpoint = data.length / 2;long[] l1 = Arrays.copyOfRange(data, 0, midpoint);long[] l2 = Arrays.copyOfRange(data, midpoint, data.length);SolverJ7 s1 = new SolverJ7(l1, operation);SolverJ7 s2 = new SolverJ7(l2, operation);ForkJoinTask.invokeAll(s1, s2);res = operation.binaryOperator(s1.res, s2.res);
}}
Daten müssen selbst aufgeteilt und zusammengeführt werden
Interface und Klasse definieren Operation
Parallele SummenberechnungThreadpool muss selbst erzeugt werden
private long[] data;
public long sumUp() {return LongStream.of(data)
.parallel()
.reduce(0, (a, b) -> a + b);}
Parallel ReduceSingle Responsibility
Single Responsibility Principle
Lambdas + Streams
Kapseln Verantwortlichkeiten
Open/ClosedPrinciple?Open/ClosedPrinciple?
You should be able to extend a classes behavior, without modifying it.
You should be able to extend a classes behavior, without modifying it.
public interface Iterable<T> {Iterator<T> iterator();
}
Iterable Interface
public interface Iterable<T> {Iterator<T> iterator();
void forEach(Consumer<? super T> action);Spliterator<T> spliterator();
}
Open/Closed
public interface Iterable<T> {Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {for (T t : this) { action.accept(t); }
}
default Spliterator<T> spliterator() {return Spliterators.spliteratorUnknownSize(iterator(), 0);
}}
Default-ImplementationOpen/Closed
Open/Closed Principle
Default-Implementierungen in Interfaces
Flexibel Frameworks erweitern
Liskov's Substitution Principle?Liskov's Substitution Principle?
“No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.”
Derived classes must be substitutable for their base classes.
Derived classes must be substitutable for their base classes.
/*** Find the first occurrence of a text in files
* given by a list of file names.*/
public Optional<String> findFirst(String text, List<String> fileNames) {
return fileNames.stream().map(name -> Paths.get(name))
.flatMap(path -> Files.lines(path))
.filter(s -> s.contains(text))
.findFirst();}
Checked Exception in StreamsLiskov‘s Substitution
public final class Files {
public static Stream<String> lines(Path path)throws IOException {
BufferedReader br =Files.newBufferedReader(path);
try {return br.lines()
.onClose(asUncheckedRunnable(br));} catch (Error|RuntimeException e) {
….. 8< ……………………br.close();
….. 8< ……………………}
}
IOException
/*** Find the first occurrence of a text in files
* given by a list of file names.*/
public Optional<String> findFirst(String text, List<String> fileNames) {
return fileNames.stream().map(name -> Paths.get(name))
.flatMap(path -> mylines(path))
.filter(s -> s.contains(text))
.findFirst();}
Handle IOExceptionLiskov‘s Substitution
private Stream<String> mylines(Path path){try (BufferedReader br =
Files.newBufferedReader(path)) {
return br.lines().onClose(asUncheckedRunnable(br));
} catch (IOException e) {throw new UncheckedIOException(e);
}}
private static Runnable asUncheckedRunnable(Closeable c) {return () -> {
try {c.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}};
}
Close StreamLiskov‘s Substitution
java.io.BufferedReader::lines + java.nio.file.Files::find, lines, list, walkwerfen UncheckedIOException beim Zugriff innerhalb des Streams
Liskov´s Substitution Principle
Files + BufferedReader
UncheckedIOException nur bei neuen Methoden
Interface Segregation Principle?Interface Segregation Principle?
Make fine grained interfaces that are client specific.
Make fine grained interfaces that are client specific.
public interface ParserContext {Reader getReader();void handleLine(String line);void handleException(Exception e);
}
public void parse(final ParserContext context) {try (BufferedReader br = new BufferedReader(context.getReader())) {
String line;do {
line = br.readLine();if (line != null) { context.handleLine(line); }
} while (line != null)} catch (IOException e) { context.handleException(e); }
}
InterfacesInterface Segregation
1 Interface mit3 Methoden
<T> T withLinesOf(Supplier<Reader> reader,Function<Stream<String>, T> handleLines,Function<Exception, RuntimeException> transformException) {
try (BufferedReader br = new BufferedReader(reader.get())) {return handleLines.apply(br.lines());
} catch (IOException e) {throw transformException.apply(e);
}}
Functional InterfacesInterface Segregation
3 separate Interfaces
withLinesOf(() -> reader,
lines -> lines.filter(line -> line.contains("ERROR")).map(line -> line.split(":")[1]).collect(toList()),
LogAnalyseException::new);
Functional InterfacesInterface Segregation
LocalTimeInterface Segregation
Interface Segregation Principle
Functional Interfaces
Ermutigen zum Auftrennen von Interfaces
Dependency Inversion Principle?Dependency Inversion Principle?
Depend on abstractions, not on concretions.Depend on abstractions, not on concretions.
Warning: JDK internal APIs are unsupported andprivate to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependency on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
jdeps -jdkinternalsDependency Inversion
jdeps -jdkinternals classes
classes -> C:\Program Files\Java\jdk1.8.0_74\jre\lib\rt.jar
de.sybit.warranty.facade.impl.DefaultWarrantyFacade (classes)
-> com.sun.xml.internal.fastinfoset.stax.events.UtilJDK internal API (rt.jar)
jdepsDependency Inversion
return (Util.isEmptyString(param) || "*".equals(param));
public List<String> filterError(Reader reader) {try (BufferedReader br =new BufferedReader(reader)){
return br.lines().filter(line -> line.contains("ERROR")).map(line -> line.split(":")[1]).collect(toList());
} catch (IOException e) {throw new LogAnalyseException(e);
}}
LambdasDependency Inversion
Filterimplementierung ist abhängig von konkreten Implementierungen
private Parser parser = new Parser();
public List<String> filterError(Reader reader) {return parser.withLinesOf(
reader,lines -> lines
.filter(line -> line.contains("ERROR"))
.map(line -> line.split(":")[1])
.collect(toList()),LogAnalyseException::new);
}
LambdasDependency Inversion
filterError() ist nur noch abhängig von Abstraktionen
public class Parser {<T> T withLinesOf(Reader reader,
Function<Stream<String>, T> handleLines,Function<Exception, RuntimeException> onError) {
try (BufferedReader br = new BufferedReader(reader)) {return handleLines.apply(br.lines());
} catch (IOException e) {throw onError.apply(e);
}}
}
LambdasDependency Inversion
withLinesOf() kapselt die Abhängigkeiten
Dependency Inversion Principle
Lambdas
Sind starke Abstraktionen
module MyClient {
requires MyService;
}
module MyService {
exports de.sybit.myservice;
}
SOLID mit Java 9 - Jigsaw
“The principles of software design still apply, regardless of your programming style. The fact that you've decided to use a [functional] language that doesn't have an assignment operator does not mean that you can ignore the Single Responsibility Principle; or that the Open Closed Principle is somehow automatic.”
Robert C. Martin: OO vs FP (24 November 2014)http://blog.cleancoder.com/uncle-bob/2014/11/24/FPvsOO.html
SOLID mit Java – OO vs FP