Home » Beitrag verschlagwortet mit 'Java'
Schlagwort-Archive: Java
TLS/SSL probleme im Maven-Build lösen
mvn -Djavax.net.debug=ssl,handshake clean install
Zusammengehörende Objekte in einer Collection zusammenfassen – Mit Java Stream API
Angenommen wir haben eine Collection von Einkäufen (Joghurt, Bananen, Deodorant, Äpfel, Käse, …) und wir wollen diese zusammenfassen (z.B. Milchprodukte, Früchte, …) dann können wir das wie folgt machen:
Map<String, Purchase> condensedMap = purchases.stream()
.collect(Collectors.groupingBy(Purchase::getCategory,
Collectors.collectingAndThen(Collectors.reducing(
(a,b) -> new Category(a.getCategory().name(), Math.sum(a.price(), b.price())),
Optional::get))));
List<Category> condensedList = condensedMap.values();
Inspired by Java stream merge or reduce duplicate objects
Zyklische Dependencies Vermeiden, mit Dependeny Inversion
Problem
Wir haben ein Low Level (core) Module das von einem High Level (public Web) Module verwendet wird.
Nun gibt es einen neuen Fall, wo das High Module von vom Low Level Module aufgerufen werden muss. Dies ist eine umgekehrte Abhängigkeit!
edu.learn.highlevel.TuWasBean
interface HighLevelModuleTuWasServiceIntf{
void tuWas(TuWasBean bean);
}
Das Problem ist, dass nun eine Zyklische Abhängigkeit (Cyclic Dependency) entsteht. Dies wird in unserem Fall zuallererst schmerzhaft, da High Level und Low Level Module im selben Build derselben Applikation gebaut werden. Dabei wird zuerst Low Level gebildet und dann High Level. Wenn man nun eine Anpassung am HighLevelModuleServiceIntf macht, dann muss man zuerst einen High Level Build laufen lassen, bevor man das Low Level auch adaptieren und bauen kann. (Sonst wäre ja das geänderte High Level Interface – auf das Low Level bereits aufbaut – noch gar nicht da.)
Lösung – Auflösung der Zyklischen Dependency
Die Lösung heisst „Dependency Inversion“. In unserem Fall heisst das, dass das Low Level Module so tut als sei der Service von ihm! Es definiert den Service selbst (und verlangt vom High Level Module, dass es selbst auf sein High Level Interface mappt).
Low Level Module formuliert also:
edu.learn.lowLevel.RepresentationOfTuWasBean
interface RepresentationOfTheHighLevelModuleTuWasServiceIntf{
void tuWas(RepresentationOfTuWasBean bean);
}
Low Level codiert nun gegen sein eigenes Interface. Zur Runtime bezieht es vertrauensvoll eine Implementaion von RepresentationOfTheHighLevelModuleTuWasServiceIntf:
@Autowired
RepresentationOfTheHighLevelModuleTuWasServiceIntf service;
public void meinBusiness(){
service.tuEtwas(RepresentationOfTuWasBean bean);
}
High Level muss dazu eine Implementierung des Low Level Interfaces zur Verfügung stellen. High Level Module mappt also selber das Low Level API zu seinem eigenen hin:
package edu.learn.highlevel.mapper;
import edu.learn.lowLevel.RepresentationOfTuWasBean
import edu.learn.highlevel.TuWasBean;
@Autowired
private OurBeanMapper beanMapper;
@Autowired
private HighLevelModuleTuWasServiceIntf highLevelService;
class TuWasServiceClientAdapter implements RepresentationOfTheHighLevelModuleTuWasServiceIntf {
void tuWas(RepresentationOfTuWasBean lowLevelBean){
TuWasBean highLevelBean = beanMapper.map(lowLevelBean);
highLevelService.tuWas(highLevelBean);
}
}
Durch diese Implementierung kann nun das High Level Module unabhängig vom Low Level Module Anpassungen an diesem API machen. Es ist einfach selbst verantwortlich das Low Level Interface weiterhin einzuhalten.
Das Low Level Module definiert den Zusammenarbeits-Vertrag. Das heisst: Auch hier ist nun High Level vom Low Level Abhängig.
Es ist nun auch möglich das Interface und dessen Nutzung (High und Low Level) anzupassen und gleichzeitig einem Build zuzuführen. Zuvor war das unmöglich, weil im ersten Schritt Low Level gebildet wurde, welches die angepassten Felder schon (zur Build-Zeit) im High Level Interface referenzierte. Nun kennt Low Level High level nicht mehr.
Zusätzlich Hilfreich …
… ist der Einsatz eines Bean-Mappers. Allein schon durch seinen Einsatz schlagen Feld-Anpassungen im Ziel-API nicht direkt auf Kompilierprobleme beim Consumer durch.
Java Runnable (EXE) erstellen / Runnable JAR erstellen
Wie erstelle ich ein Java-Programm, das ich mal einfach so einem Kollegen senden kann, das dieser dann auf seinen Computer kopiert und per Doppelklick ausführt.
(Java Web Start wäre wohl eine gute Wahl dafür gewesen, doch diese Technologie existiert seit Java 11 nicht mehr.)
Hat das Programm ein GUI (oder präziser: Braucht es keine Kommandozeile für die User-Interaktion) kann man ein „Doppelklickbares“ JAR aus dem Code erstellen.
Ausführbare JAR Datei erstellen
Eine doppelklickbare JAR Datei kann wie folgt erstellt werden.
Dies ist nicht zielführend mit Kommandozeilenprogrammen, die (ad hoc) Parameter verlangen. Nützlich ist diese Option hingegen für GUI Programme.
Main Klasse in Manifest referenzieren
Im MANIFEST.MF soll diese Zeile stehen:
Main-Class: ch.meine.MainKlasse
Dies kann erreicht werden indem dem pom.xml diese Plugin-Definition hinzugefüt wird:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>ch.meine.MainKlasse</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
Nun muss das Projekt gebildet werden:
mvn clean package assembly:single
Das entstande JAR-File kann mittels Doppelklick gestartet werden.
Wenn das Programm kein GUI ist wird es kaum sichtbar sein, weil es wohl anläuft aber potentiell sehr schnell wieder verschwindet.
Alternativ kann das JAR gestartet werden mit:
java -jar MeinProgramm.jar <parameters>
Auf Kommandozeile ausführen
Ein (wie oben beschrieben) ausführbar gemachtes JAR kann wie folgt ausgeführt werden:
java -jar MeinProgramm.jar <parameters>
Selbstverständlich kann dieser Aufruf in Shortcuts oder in (Power)Shell/Bat Scripts eingabaut werden (und eventuell so als ZIP dem Kunden geliefert werden).
Java Design Patterns
Siehe auch: Java Design Principles
Ein wunderbarer Kurs: Design Patterns in Java: The Big Picture by Esteban Herrera
Design Pattern Types
Unterscheidung 1: Structural | Behavioral | Creational
Unterscheidung 2: Class | Object centric
Wichtigste Design Patterns
Creational
Abstract Factory
Factory Method
Gemäss Herrera: Gleich wie ‚Abstract Factory‘ aber links und Creator und Produkt haben nur eine Hierarchie.
Gemäss Steinhauser: Eine getFactory() Methode erzeugt die Factory anhand eines Discriminators.
Builder
Singleton Pattern
Sinn: Es soll in der Laufzeitumgebung nur genau ein Objekt einer bestimmten Klasse geben.
Vorgehen: Einzigen parameterlosen Konstruktor der Klasse auf private setzen. Statisches Feld ‚instance‘, das die eine Instanz aufnimmt. Eine statische getInstance Methode wird benutzt um 1., falls ‚instance‘ noch NULL ist die Klasse zu instqanzieren und ‚instance‘ zu setzen und 2. die Instanz zu retournieren.
Behavioural
Strategy
State
Command
Observer Pattern
Template Method Pattern
Eine Basisklasse enthälte eine Methode (Bsp. processDocument()), die sowohl Verarbeitungsschritte enthält, die für alle Spezialfälle gleich sind (Bsp. backup(), validate()), aber auch solche, die je nach Fall differenziert werden sollen (Bsp. process()). Die zu differenzierenden Schritte werden in der Basisklasse als abstrakte Methoden ausgelagert und in spezifischen Subklassen konkretisiert.
Spezialfall:
Hooks: Wie Template Pattern, aber methoden sind nicht abstrakt und müssen deshalb nicht überschriben werden (default).
Visitor
Beschreibung fehlt noch!
Iterator
Beschreibung fehlt noch!
Structural
Facade Pattern
Zweck: Einen Orchestrator haben, der weiss wo was zu holen und wie zu verarbeiten ist. ==> Wie eine Main Methode.
Decorator Pattern
Um eine Pizza mit verschiedenen Zutaten zu dekorieren:
Das bedingt, dass die Decorators eine Instanz des Decoree halten und eine gemeinsame Methode (=>Interface) implementieren:
Proxy Pattern
(Object) Adapter Pattern
Zweck: Koppelung von Services ohne direkt von ihren Interfaces abhängig zu sein.
Composite
Bridge
Traps/Fallen beim Java-Programmieren – Anfängerfehler
assertThat(booleanExpression)
Achtung! Der Ausdruck assertThat(booleanExpression) prüft nichts!
Falsch:
assterThat("Hans".equals("Peter"));
// wird keine JUnit-Failed Test ausweisen!
Richtig:
assertThat("Hans").isEqualTo("Peter");
// wird JUnit-Failed Test ausweisen!
Vordefinierte Java Functional Interfaces – Welches kann ich benutzen?
Parameter | Functional Interface |
---|---|
keine | Runnable |
1 input | Consumer |
1 output | Supplier |
1 input / 1 output | Function |
2 input | BiConsumer |