Home » Java Advanced
Archiv der Kategorie: Java Advanced
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
Optional.ofNullable / Optional.map – Mehrstufig hierarchische NOT NULL Abfrage. Anstatt Abfrage auf != NULL
Schritt 1: Erstelle ein Optional auf dem Object auf das du null-wert-sicher zugreifen möchtest:
Optional isOptionallyNull = Optional.ofNullable(instOfClassANotShureIfNull);
Schritt 2: Greife auf Felder (und Child-Felder) zu Obwohl die Instanz selbst (oder ihre Children) ja null sein könnten: Map:
ClassC unterUnterObject = isOptionallyNull .map(ClassA::getSubObjB).map(ClassB::getSubC).orElseGet(ClassC::new)
Wie oben demonstriert können über mehrere Stufen aggregierte Sub-Objekte so abgefragt werden, obwohl man nicht weiss ob Vater- oder Zwischenobjekte NULL sein könnten.
Filtern von Stream-Objekten und Verarbeitung mit dem gefundenen Objekt (–> ifPresent)
if (v1 != null && issuerMap.containsKey(v1)) {
phcCr.setIssuer(issuerMap.get(v1));
}
… kann dasselbe erreicht werden mit:
Optional.ofNullable(v1).filter(value -> issuerMap.containsKey(value))
.ifPresent(value -> phcCr.setIssuer(issuerMap.get(value)));
SSL debuggen
-Djavax.net.debug=ssl
Mocks erzeugen für via Class spezifizierte Objekte
Probleme:
Problem 1: Dynamisches Mocking:
Eine Methode soll Mocks für verschiedene Service-Klassen definieren. In der Methode selbst kann nicht jede in ihr zu Mockende Klasse separat auscodiert werden.
D.h.:
Mockito.when(MeineKlasse.meinMethode())
ist nicht möglich.
Stattdessen wird der Methode ein Class -Objekt übergeben, die den zu mockenden Service definiert.
Problem 2:
Wenn ein bestimmter request vorliegt muss ein definierter Reply zurück gegeben werden.
MyObj2 mock = Mockito.mock(MyObj2.class); Mockito.when(mock.returnOnObj(new Bohne1("X"))).thenReturn("desired return");
Problem 3 :
Definition einer Sequenz der durch den Mock zurückzugebender Return-Werte.
MyObj2 mock = Mockito.mock(MyObj2.class); Mockito.when(mock.returnOnObj(Mockito.any())).thenReturn("1").thenReturn("2").thenReturn("3");
Beispiel, das alles obigen Probleme löst:
HPServiceMockFactory.
import org.mockito.stubbing.OngoingStubbing; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.when; public class HPServiceMockFactory<T> { public enum MockMatchingType {IN_SEQUENCE, BY_REQUEST_MATCH}; public <T> T prepareMock(T mock, Class bdType, String svcName, MockMatchingType mockMatchingType, Object[] parameters, Object[] desiredReturns){ Method svc = null; for (Method method : bdType.getDeclaredMethods()) { // check if method with this name and number of arguments is declared if ((method.getParameterTypes().length == parameters.length) && (method.getName().equals(svcName))) { // check method parameters if (allinstanceof(parameters, method.getParameterTypes())) { try { Object[] paramsForInvocation = new Object[parameters.length]; for(int i = 0; i < parameters.length; i++){ if(mockMatchingType.equals(MockMatchingType.IN_SEQUENCE)){ // When we want to invoke in sequence: First invocation gets desiredReturn[0], second gets desiredReturn[1], ... // Then we should narrow only to anyObject parameters (or we could narrow the the suiting parameter classes) paramsForInvocation[i] = anyObject(); }else{ // When we want the mock to return an answer which is defined by its request being equal as the one when the mock is called // then we have to call the mock and pass the expected parameters BEFORE (farther below) define the return values (via the "thenReturn(..)) paramsForInvocation[i] = parameters[i]; } } svc = bdType.getDeclaredMethod(svcName, method.getParameterTypes()); svc.invoke(mock, paramsForInvocation); } catch (NoSuchMethodException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } } } } if (svc == null) { throw new IllegalStateException("Service method could not be found"); } OngoingStubbing<Object> ongoingStubbing = when(svc); for(Object desiredReturn : desiredReturns){ ongoingStubbing = ongoingStubbing.thenReturn(desiredReturn); } return mock; } private boolean allinstanceof(Object obj[], Class<?> classes[]) { for (int i = 0; i < obj.length; i++) { Class<?> clazz = classes[i]; Object o = obj[i]; if (clazz.isPrimitive()) { if (clazz == boolean.class) { if (!(o instanceof Boolean)) { return false; } } else if (clazz == int.class) { if (!(o instanceof Integer)) { return false; } } } else { if (o != null && !clazz.isInstance(o)) { return false; } } } return true; } }
Aufrufende Klasse:
import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import java.util.Objects; import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.BY_REQUEST_MATCH; import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.IN_SEQUENCE; /** * Demonstrates how Mockito.when(null) can be used to tell Mockito to * return a given value if objects equals to given ones are passed to the mocked method */ public class HPServiceMockTest { @Test public void mustReturnMockAnswersAsDefinedInASequence(){ HPService mockedObject = Mockito.mock(HPService.class); Object[] parameters = new Object[1]; parameters[0] = new Bohne1("X"); String[] desiredReturns = {"Response 1", "Response 2", "Response 3"}; HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>(); myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", IN_SEQUENCE, parameters, desiredReturns); Assert.assertEquals(desiredReturns[0], mockedObject.returnOnObj(new Bohne1("ANY"))); Assert.assertEquals(desiredReturns[1], mockedObject.returnOnObj(new Bohne1("ANY"))); Assert.assertEquals(desiredReturns[2], mockedObject.returnOnObj(new Bohne1("ANY"))); } @Test public void mustReturnMockAnswersThatMatchTheGivenRequest(){ HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>(); HPService mockedObject = Mockito.mock(HPService.class); Object[] whenReqParams1 = new Object[1]; whenReqParams1[0] = new Bohne1("Request that must match 1"); String[] desiredReturns1 = {"Right Response 1"}; myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams1, desiredReturns1); Object[] whenReqParams2 = new Object[1]; whenReqParams2[0] = new Bohne1("Request that must match 2"); String[] desiredReturns2 = {"Right Response 2"}; myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams2, desiredReturns2); Assert.assertEquals(desiredReturns1[0], mockedObject.returnOnObj(new Bohne1("Request that must match 1"))); Assert.assertEquals(desiredReturns2[0], mockedObject.returnOnObj(new Bohne1("Request that must match 2"))); Assert.assertNull(mockedObject.returnOnObj(new Bohne1("ANY"))); } } class HPService { public String returnOnObj(Bohne1 in){return "not mocked";} } class Bohne1 { public String id; public Bohne1(String id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Bohne1)) return false; Bohne1 bohne1 = (Bohne1) o; return Objects.equals(id, bohne1.id); } @Override public int hashCode() { return Objects.hash(id); } }
Dynamische Mocking
Probleme:
Problem 1: Dynamisches Mocking:
Eine Methode soll Mocks für verschiedene Service-Klassen definieren. In der Methode selbst kann nicht jede in ihr zu Mockende Klasse separat auscodiert werden.
D.h.:
Mockito.when(MeineKlasse.meinMethode())
ist nicht möglich.
Stattdessen wird der Methode ein Class -Objekt übergeben, die den zu mockenden Service definiert.
Problem 2:
Wenn ein bestimmter request vorliegt muss ein definierter Reply zurück gegeben werden.
MyObj2 mock = Mockito.mock(MyObj2.class); Mockito.when(mock.returnOnObj(new Bohne1("X"))).thenReturn("desired return");
Problem 3 :
Definition einer Sequenz der durch den Mock zurückzugebender Return-Werte.
MyObj2 mock = Mockito.mock(MyObj2.class); Mockito.when(mock.returnOnObj(Mockito.any())).thenReturn("1").thenReturn("2").thenReturn("3");
Beispiel, das alles obigen Probleme löst:
HPServiceMockFactory.class
import org.mockito.stubbing.OngoingStubbing; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.when; public class HPServiceMockFactory<T> { public enum MockMatchingType {IN_SEQUENCE, BY_REQUEST_MATCH}; public <T> T prepareMock(T mock, Class bdType, String svcName, MockMatchingType mockMatchingType, Object[] parameters, Object[] desiredReturns){ Method svc = null; for (Method method : bdType.getDeclaredMethods()) { // check if method with this name and number of arguments is declared if ((method.getParameterTypes().length == parameters.length) && (method.getName().equals(svcName))) { // check method parameters if (allinstanceof(parameters, method.getParameterTypes())) { try { Object[] paramsForInvocation = new Object[parameters.length]; for(int i = 0; i < parameters.length; i++){ if(mockMatchingType.equals(MockMatchingType.IN_SEQUENCE)){ // When we want to invoke in sequence: First invocation gets desiredReturn[0], second gets desiredReturn[1], ... // Then we should narrow only to anyObject parameters (or we could narrow the the suiting parameter classes) paramsForInvocation[i] = anyObject(); }else{ // When we want the mock to return an answer which is defined by its request being equal as the one when the mock is called // then we have to call the mock and pass the expected parameters BEFORE (farther below) define the return values (via the "thenReturn(..)) paramsForInvocation[i] = parameters[i]; } } svc = bdType.getDeclaredMethod(svcName, method.getParameterTypes()); svc.invoke(mock, paramsForInvocation); } catch (NoSuchMethodException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } catch (InvocationTargetException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } catch (IllegalAccessException e) { throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e); } } } } if (svc == null) { throw new IllegalStateException("Service method could not be found"); } OngoingStubbing<Object> ongoingStubbing = when(svc); for(Object desiredReturn : desiredReturns){ ongoingStubbing = ongoingStubbing.thenReturn(desiredReturn); } return mock; } private boolean allinstanceof(Object obj[], Class<?> classes[]) { for (int i = 0; i < obj.length; i++) { Class<?> clazz = classes[i]; Object o = obj[i]; if (clazz.isPrimitive()) { if (clazz == boolean.class) { if (!(o instanceof Boolean)) { return false; } } else if (clazz == int.class) { if (!(o instanceof Integer)) { return false; } } } else { if (o != null && !clazz.isInstance(o)) { return false; } } } return true; } }
Test Klassen:
import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import java.util.Objects; import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.BY_REQUEST_MATCH; import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.IN_SEQUENCE; /** * Demonstrates how Mockito.when(null) can be used to tell Mockito to * return a given value if objects equals to given ones are passed to the mocked method */ public class HPServiceMockTest { @Test public void mustReturnMockAnswersAsDefinedInASequence(){ HPService mockedObject = Mockito.mock(HPService.class); Object[] parameters = new Object[1]; parameters[0] = new Bohne1("X"); String[] desiredReturns = {"Response 1", "Response 2", "Response 3"}; HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>(); myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", IN_SEQUENCE, parameters, desiredReturns); Assert.assertEquals(desiredReturns[0], mockedObject.returnOnObj(new Bohne1("ANY"))); Assert.assertEquals(desiredReturns[1], mockedObject.returnOnObj(new Bohne1("ANY"))); Assert.assertEquals(desiredReturns[2], mockedObject.returnOnObj(new Bohne1("ANY"))); } @Test public void mustReturnMockAnswersThatMatchTheGivenRequest(){ HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>(); HPService mockedObject = Mockito.mock(HPService.class); Object[] whenReqParams1 = new Object[1]; whenReqParams1[0] = new Bohne1("Request that must match 1"); String[] desiredReturns1 = {"Right Response 1"}; myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams1, desiredReturns1); Object[] whenReqParams2 = new Object[1]; whenReqParams2[0] = new Bohne1("Request that must match 2"); String[] desiredReturns2 = {"Right Response 2"}; myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams2, desiredReturns2); Assert.assertEquals(desiredReturns1[0], mockedObject.returnOnObj(new Bohne1("Request that must match 1"))); Assert.assertEquals(desiredReturns2[0], mockedObject.returnOnObj(new Bohne1("Request that must match 2"))); Assert.assertNull(mockedObject.returnOnObj(new Bohne1("ANY"))); } } class HPService { public String returnOnObj(Bohne1 in){return "not mocked";} } class Bohne1 { public String id; public Bohne1(String id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Bohne1)) return false; Bohne1 bohne1 = (Bohne1) o; return Objects.equals(id, bohne1.id); } @Override public int hashCode() { return Objects.hash(id); } }
... code ...
Mockito.when(null) – Return basierend auf equality zu gegebenem Input
Mockito.mock(null)
Weshalb sollte das sinn machen?
Mockito.when(null).thenReturn(meinObjekt)
bedeutet eigentlich: Liebes Mockito. Nimm den letzten Aufruf auf irgend einem Mock. Wenn irgendwann derselbe Aufruf mit denselben Parametern gemacht werden sollte, dann gib „meinObjekt“ zurück.
Dies wird unten demonstriert.
import org.mockito.Mockito; import java.util.Objects; /** * Demonstrates how Mockito.when(null) can be used to tell Mockito to * return a given value if objects equals to given ones are passed to the mocked method */ public class HPTest { public static void main(String[] args) { MyObj mockedObject = Mockito.mock(MyObj.class); /* Tell Mockito, that if get1() is called, then return "super cool string 1.1"*/ mockedObject.get1(); Mockito.when(null).thenReturn("super cool string 1.1"); mockedObject.get2(); Mockito.when(null).thenReturn("super cool string 2"); System.out.println(mockedObject.get1()); System.out.println(mockedObject.get1()); System.out.println(mockedObject.get2()); System.out.println(mockedObject.toString()); /* Tell Mockito, that if return1("1") is called, then return "Return 1" */ mockedObject.return1("1"); Mockito.when(null).thenReturn("Return 1"); /* but if return1("2") is called, then return "Return 2" */ mockedObject.return1("2"); Mockito.when(null).thenReturn("Return 2"); System.out.println(mockedObject.return1("1")); System.out.println(mockedObject.return1("2")); System.out.println(mockedObject.return1("2")); System.out.println(mockedObject.return1("1")); mockedObject.returnOnObj(new Bohne("A")); Mockito.when(null).thenReturn("On Obj A"); mockedObject.returnOnObj(new Bohne("B")); Mockito.when(null).thenReturn("On Obj B"); System.out.println(mockedObject.returnOnObj(new Bohne("A"))); System.out.println(mockedObject.returnOnObj(new Bohne("B"))); System.out.println(mockedObject.returnOnObj(new Bohne("B"))); System.out.println(mockedObject.returnOnObj(new Bohne("A"))); } } class MyObj { public String get1(){return "not mocked 1";} public String get2(){return "not mocked 2";} public String return1(String in){return "not mocked";} public String returnOnObj(Bohne in){return "not mocked";} } class Bohne { public String id; public Bohne(String id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Bohne)) return false; Bohne bohne = (Bohne) o; return Objects.equals(id, bohne.id); } @Override public int hashCode() { return Objects.hash(id); } } /* Output: super cool string 1.1 super cool string 1.1 super cool string 2 Mock for MyObj, hashCode: 650023597 Return 1 Return 2 Return 2 Return 1 On Obj A On Obj B On Obj B On Obj A Process finished with exit code 0 */
JavaScript in Java einlesen und ausführen
javax.script
Stream Reduce anwenden, um Auswertungen über Elemente zu machen
import static org.assertj.core.api.Assertions.assertThat; public class LerneReduce { public static void main(String[] args) { List<String> bros = TestDataProvider.getBrothersInArms(); assertThat( bros.stream().reduce((a, b) -> a + ", " + b).get() ).isEqualTo("Walther, Hans, Erich, Paul"); IntStream oneTo3 = IntStream.iterate(1, i -> ++i).limit(3); assertThat( oneTo3.reduce((i1, i2) -> i1 + i2).getAsInt() ).isEqualTo(6); } }
String-Joining mit Stream-API
import static org.assertj.core.api.Assertions.assertThat; public class LerneJoining { public static void main(String[] args) { List<String> bros = TestDataProvider.getBrothersInArms(); String brosString = bros.stream().collect(Collectors.joining(", ")); //Collectors.joining(...) assertThat(brosString).isEqualTo("Walther, Hans, Erich, Paul"); } }