Die SOLID Prinzipien gehören heutzutage zum festen Handwerkszeug eines jeden Clean Code Developers. Java 8 enthält großartige Neuerungen und Sprach-Features, die aus der Sicht der SOLID-Prinzipien beleuchtet werden. Sie werden erfahren, in welchen Bereichen die Sprache uns Entwickler noch besser unterstützt und wie die JDK-Designer ihrerseits die SOLID-Prinzipien berücksichtigt haben.
Gehen Sie mit mir auf einen Streifzug durch die Java 8 Welt und lernen Sie die neuen Sprachfeatures SOLIDe kennen.
6. // 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
7. List<Person> persons = Arrays.asList(
// name, age, size
new 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 anstatt
if (age >= 18) {
keep();
}
Collectors berechnet
den Durchschnitt
Single Responsibility
8. public interface BinaryOperation {
long identity();
long binaryOperator(long a, long b);
}
public class Sum implements BinaryOperation {
@Override
public long identity() { return 0L; }
@Override
public 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 Summenberechnung
Threadpool muss selbst
erzeugt werden
9. private long[] data;
public long sumUp() {
return LongStream.of(data)
.parallel()
.reduce(0, (a, b) -> a + b);
}
Parallel ReduceSingle Responsibility
15. 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.
16. /**
* 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
17. /**
* 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);
}
}
18. 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, walk
werfen UncheckedIOException beim Zugriff innerhalb des Streams
27. Warning: JDK internal APIs are unsupported and
private 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/Jav
a+Dependency+Analysis+Tool
jdeps -jdkinternalsDependency Inversion
34. “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