226-2 Objektorientiert implementieren

  • 18 Mär. 2018
  • 10:05

    Unit-Tests einsetzt Einführung in Stub- und Mock-Klassen

    Objektorientiertes Software-Design steht für Wartbarkeit, Erweiterbarkeit und Robustheit. Mit Hilfe von Polymorphismus und Delegation kann dies erreicht werden. Im Modul 226-1 haben Sie bereits Unit-Tests mit JUnit implementiert. Sie haben dort gesehen, dass ein Unit-Test dazu dient eine einzelne Klasse isoliert zu testen.
    In der Einführung Stub- und Mock-Klassen wird nun gezeigt, dass es Fälle gibt, wo es schwierig ist, eine Klasse isoliert zu testen. In solchen Fällen arbeitet man mit sogenannten Stub- oder Mock-Objekten. Dazu gibt es Werkzeuge, wie JUnit, welche es erlauben, einen Unit-Test automatisiert und beliebig oft durchzuführen. Nach jeder Änderung an der Klasse kann der Test wiederholt werden. Damit ist sichergestellt, dass die Klasse alle bisherigen Anforderungen nach wie vor erfüllt. Man spricht von Regression-Tests. Oft ist es nicht möglich ein Klasse vollständig isoliert zu testen, weil sie von anderen Klassen abhängig ist. Dies ist der Fall, wenn eine Klasse Objekte anderer Klassen referenziert (über Instanzvariablen) oder wenn sie beim Aufruf ihrer Methoden Referenzen auf Objekte anderer Klassen empfängt (als Argument). In solchen Fällen müssen im Test zusätzliche Objekte bereitgestellt werden, welche nicht im Zentrum des Tests stehen. Man spricht hier von Kollaborationsobjekten (collaborator objects). Dies in Abgrenzung zum eigentlich zu testenden Objekt, welches oft als system under test (SUT) bezeichnet wird. Im einfachsten Fall spielen Kollaborationsobjekte im Test die Rolle von reinen Dummies. Das heisst, sie werden herumgereicht, aber nicht weiter bearbeitet. Oft werden Sie aber auch in den Test mit einbezogen, indem auch bei ihnen Bedingungen überprüft werden, welche durch das SUT eingehalten werden müssen. Schwieriger wird es, wenn das SUT darauf angewiesen ist, dass sich die Kollaborationsobjekte nach einem bestimmten Muster verhalten. Das heisst, dass die Kollaborationsobjekte beim Aufruf ihrer Methoden bestimmte, vordefinierte Resultate liefern. Das kann einiges Kopfzerbrechen geben, wenn sich hinter einem Kollaborationsobjekt ein ganzes Subsystem, wie ein Datenbank, oder ein GUI versteckt. In diesen Situationen arbeitet man dann mit sogenannten Stubs („Stummeln“) oder gar mit Mocks (von Mock-up, Attrappe). Dies sind Klassen, welche die gleiche Schnittstelle wie das Original anbieten, dahinter aber eine auf den Test zugeschnittene Funktion implementieren.

    Stub-Klassen

    Eine Stub-Klasse ist eine Klasse, welche die gleichen Methoden, wie die Originalklasse anbietet. Allerdings werden die Methoden nicht vollständig implementiert, sondern sie liefern beim Aufruf nur für den Test vorprogrammierte Resultate. Auf alles, was ausserhalb dieser Vorprogrammierung liegt kann eine Stub-Klasse nicht reagieren. Stub-Klassen können Informationen über die aufgerufenen Methoden sammeln. Diese Informationen können später im Test überprüft werden. Eine Stub-Klasse wird in der Regel von Hand für einen bestimmten Testfall programmiert und ist entsprechend eingeschränkt in ihren Möglichkeiten. Sie kann nicht ohne weiteres für andere Testfälle eingesetzt werden.

    zeichneTest mit Stub.mp4

    Mock-Klassen

    Eine Mock-Klasse ist eine Klasse, welche ebenfalls die gleichen Methoden, wie die Originalklasse anbietet. Sie ist aber viel flexibler als eine Stub-Klasse. Die Methoden können vor dem Test mit einem beliebigen Verhalten konfiguriert werden. Dazu werden je nach Werkzeug Regeln eingesetzt (wenn … dann …). Auch bei der Überprüfung bieten Mock-Klassen viel mehr Möglichkeiten. So kann direkt überprüft werden, ob eine bestimmte Methode wirklich mit den erwarteten Argumenten aufgerufen worden ist. Auch die Reihenfolge und Anzahl der Aufrufe kann überprüft werden. Für die Erstellung von Mock-Klassen setzt man normalerweise ein Werkzeug wie Mockito oder jMockit ein.

    zeichneTest mit Mock.mp4

    Zur Vertiefung wird folgender Artikel empfohlen:


    martinfowler.com Link: mocksArentStubs

    Mockito-Kurzanleitung

    Diese Anleitung bietet die wichtigsten Grundlagen zum Testen mit Mockito. Weitere Hilfe ist unter dem folgenden Link zu finden.

    Mockito in Testfall importieren

    import static org.mockito.Mockito.*;

    Ein Mock-Objekt erzeugen.

    Beispiel: Ein Mock-Objekt für eine Liste von Strings.

    List<String> liste = mock(List.class);

    Antwort für den Aufruf einer Methode beim Mock-Objekt konfigurieren.

    when(liste.get(0)).thenReturn("Meier");

    Beim Aufruf einer Methode des Mock-Objekts eine Exception werfen.

    when(liste.get(1)).thenThrow(new RuntimeException());

    Überprüfen, ob Methode beim Mock-Objekt mit den erwarteten Argumenten aufgerufen worden ist.

    Beispiel: Wurde die Methode get mit Argument 0 aufgerufen?

    verify(liste).get(0);

    Mit einem Argument-Matcher überprüfen, ob Methode beim Mock-Objekt mit irgendwelchen Argumenten aufgerufen worden ist.

    Mit einem Argument-Matcher überprüfen, ob Methode beim Mock-Objekt mit irgendwelchen Argumenten aufgerufen worden ist.

    verify(liste).get(anyInt());

    Überprüfen, ob Methode beim Mock-Objekt eine bestimmte Anzahl Male aufgerufen worden ist.

    verify(liste, times(3)).get(0);

    Reihenfolge der Methodenaufrufe beim Mock-Objekt überprüfen.

    InOrder order = inOrder(liste);

    order.verify(liste).get(0);

    order.verify(liste).get(1);

     

    mockito.org Link: Mockito
    Nachfolgend sind obige Anweisungen in einem Beispiel-Test zusammengefasst. In diesem Test wird für die List-Schnittstelle aus der Java-Bibliothek eine Mock-Klasse erstellt. In einem realen Test würde diese Mock-Klasse dann für den Test einer weiteren Klasse eingesetzt. Das ist hier nicht so, da es nur darum geht die verschiedenen Möglichkeiten von Mockito aufzuzeigen. Es gibt also keine weitere zu testende Klasse in diesem Beispiel.
    import static org.mockito.Mockito.*;
    import static org.junit.Assert.*;
    import org.junit.Test;
    import org.mockito.InOrder;
    
    import java.util.List;
    
    public class MockitoKurzanleitung {
    
      private List liste = mock(List.class);
    
      @Test(expected = RuntimeException.class)
      public void test() {
        InOrder order = inOrder(liste);
    
        when(liste.get(0)).thenReturn("Meier");
        when(liste.get(999)).thenThrow(new RuntimeException());
    
        assertEquals("Meier", liste.get(0));
        assertEquals(null, liste.get(1));
    
        verify(liste).get(0);
        verify(liste, times(1)).get(0);
        verify(liste, times(2)).get(anyInt());
    
        order.verify(liste).get(0);
        order.verify(liste).get(1);
    
        liste.get(999);
      }
    
    
                                                                

    person.mp4

  • 18 Mär. 2018
  • 10:05

    Einfürung in UML Sequenzdiagramm

    Objektorientiertes Software-Design steht für Wartbarkeit, Erweiterbarkeit und Robustheit. Mit Hilfe von Polymorphismus und Delegation kann dies erreicht werden.
    Wie Sie bereits aus dem Vorgängermodul wissen, sind Sequenzdiagramme das Mittel der Wahl, wenn es darum geht die Abläufe zur Laufzeit des Programmes zu dokumentieren. Ein Sequenzdiagramm zeigt einen Ausschnitt – einen Film – aus dem Leben des Programms. Es zeigt die während einer bestimmten Zeit vorhandenen Objekte und den Ablauf der Methodenaufrufe bei diesen Objekten. Letzteres wird auch als Abfolge von Meldungen bezeichnet, welche die Objekte einander senden.

    UML Tutorial #11 - Sequenzdiagramme


    Sequenzdiagramm.mp4


  • 18 Mär. 2018
  • 10:05

    Einfürung in UML Klassendiagramm

    Objektorientiertes Software-Design steht für Wartbarkeit, Erweiterbarkeit und Robustheit. Mit Hilfe von Polymorphismus und Delegation kann dies erreicht werden.
    Wie Sie bereits im Vorgängermodul gesehen haben, bieten Klassendiagramme eine gute Übersicht über die statische Struktur eines Programmes. Dies trifft genau gleich auch auf Programme zu, in denen mittels Vererbung übersichtlicher und redundanzfreier Programmiercode entwickelt wurde. Damit die Vererbung in Ihren Klassendiagrammen ersichtlich ist, sollten Sie die dazugehörigen UML-Darstellungsregeln anwenden bzw. lesen können.

    Klassendiagramme mit UML - Theoretische Objektorientierte Konzepte 1


    Klassendiagramme mit UML - Theoretische Objektorientierte Konzepte 2


    Klassendiagramme mit UML - Theoretische Objektorientierte Konzepte 3


    Klassendiagramme mit UML - Theoretische Objektorientierte Konzepte 4


    Die Ganze Reihe der TheSimpleInformatics ist nur empfhlenswert.
  • 18 Mär. 2018
  • 10:05

    Einfürung in Java Die Vorteile von Polymorphismus und Delegation verstehen

    Objektorientiertes Software-Design steht für Wartbarkeit, Erweiterbarkeit und Robustheit. Mit Hilfe von Polymorphismus und Delegation kann dies erreicht werden.
    Sie haben gesehen, wie mit Hilfe der Prinzipien Delegation und Methoden überschreiben (Polymorphismus) erweiterbarer Code geschrieben werden kann. Wobei „erweiterbarer Code“ hier zu verstehen ist, dass bei Erweiterungen bestehender Code nicht angetastet wird; sondern die Erweiterungen lediglich durch neue Klassen realisiert werden.
    Der nachfolgende Codeausschnitt zeigt die Klassen eines Bibliothekprogramms.
    o	Die Klasse Bibliothek enthält einen Katalog, in welchem alle verfügbaren Medien als Objekte gespeichert sind.
    o	Medien sind Bücher, CDs und DVDs.
    o	Jedes Medium verfügt über ein Attribut sample, das einen Ausschnitt des tatsächlichen Inhaltes birgt.
    o	Die Klasse Bibliothek hat eine Methode zur Ausgabe des Samples (showSample(Medium m)), wobei je nach Art des Mediums ein Player-Objekt verwendet wird, das in der Lage ist Sound und Video abzuspielen.
    
    public class Bibliothek {
    
      private Medium[] katalog;
    
      public void showSample(Medium m) {
        if (m instanceof Buch) {
          System.out.println(((Buch) m).sample);
        } else if (m instanceof CD) {
          Player p = new Player();
          p.playSound(((CD) m).sample);
        } else if (m instanceof DVD) {
          Player p = new Player();
          p.playVideo(((DVD) m).sample);
        }
      }
    }
    
    
    public abstract class Medium { }
    
    public class Buch extends Medium {
      String sample;
    }
    
    public class CD extends Medium {
      Sound sample;
    }
    
    public class DVD extends Medium {
      Video sample;
    }
    
    public class Sound { // ... }
    public class Video { // ... }
    
    public class Player {
      public void playVideo(Video v) {
        // Video anzeigen ...
      }
      public void playSound(Sound s) {
        // Sound spielen ...
      }
    }
    
                                                                

    Einführung Methoden überschreiben und Delegation.mp4

  • 18 Mär. 2018
  • 10:05

    Einfürung in Java Überschreiben und Überladen

    Der Unterschied zwischen Überschreiben und Überladen? Bei dieser Problematik unterscheiden wir zwei Fälle. Zum einen können wir mehrere gleichnamige Methoden innerhalb der selben Klasse haben. In diesem Fall sprechen wir vom Überladen einer Methode. Zum anderen, kann es in einer Vererbungshierarchie gleichnamige Methoden geben, da her eine Methode in einer Unterklasse hat den gleichen Namen wie die Methode einer Oberklasse. In diesem Fall sprechen wir vom Überschreiben einer Methode.
    Die Methode einer Subklasse überschreibt die Methode einer Superklasse, wenn Sie die gleiche Signatur wie die Methode einer Superklasse hat. Jetzt hat die Superklasse ihre Methode, und die Subklasse hat ihre eigene Methode mit der gleichen Signatur. (Denken Sie daran, dass die Signatur einer Methode der Name der Methode und ihre Parameterliste ist.) Ein Objekt vom Typ der Superklasse wird die Methode enthalten, die in der Definition der Superklasse gegeben ist. Ein Objekt vom Typ der Subklasse wird die Methode enthalten, die in der Definition der Subklasse gegeben ist.
                                                                ///Raum.java
    public class Raum {
    ///Mögliche unter Subklassen von Raum wären Kochraum, Putzraum und Wohnraum etc.
        //Attribute
        private String farbe;
    
        //default Konstruktor
        public Raum() {
        }
    
        //Methoden ausgabe der Raumanzahl
        public void printFarbe() {
            System.out.println(farbe);
        }
    }
    //Gebaude.java
    public class Gebaude {
        Gebaude g;
        //Attribute
        private int anzahlRaeume;
    
        //default Konstruktor
        public Gebaude() {
        }
    
        //Methoden ausgabe der Raumanzahl
        public void printRaeume() {
            System.out.println(anzahlRaeume);
        }
    }
                                                                

    Methoden überschreiben.mp4

  • 18 Mär. 2018
  • 10:05

    Einfürung in Java Komposition

    Obwohl sowohl die Vererbung als auch die Komposition Code-Wiederverwendbarkeit bieten, besteht der Hauptunterschied zwischen Komposition und Vererbung in Java darin, dass die Komposition die Wiederverwendung von Code erlaubt, ohne sie zu erweitern, aber für Vererbung muss die Klasse für die Wiederverwendung von Code oder Funktionalität erweitert werden. Ein weiterer Unterschied, der sich aus dieser Tatsache ergibt, ist, dass Sie durch Verwendung von Composition Code auch für die letzte Klasse wiederverwenden können, die nicht erweiterbar ist, aber Inheritance kann Code in solchen Fällen nicht wiederverwenden. Mithilfe von Composition können Sie auch Code aus vielen Klassen wiederverwenden, da sie nur als Membervariable deklariert sind, aber mit Vererbung können Sie Codeform nur in einer Klasse wiederverwenden, da Sie in Java nur eine Klasse erweitern können, da Mehrfachvererbung in Java nicht unterstützt wird. .. Sie können dies jedoch in C ++ tun, da dort eine Klasse mehr als eine Klasse erweitern kann. Übrigens sollten Sie in Java immer die Komposition über Vererbung bevorzugen , es ist nicht nur ich, sondern auch Joshua Bloch hat in seinem Buch vorgeschlagen.
    Gebaude <#>1--------------1,*Raum
                                                                ///Raum.java
    public class Raum {
    ///Mögliche unter Subklassen von Raum wären Kochraum, Putzraum und Wohnraum etc.
        //Attribute
        private String farbe;
    
        //default Konstruktor
        public Raum() {
        }
    
        //Methoden ausgabe der Raumanzahl
        public void printFarbe() {
            System.out.println(farbe);
        }
    }
    //Gebaude.java
    public class Gebaude {
        Gebaude g;
        //Attribute
        private int anzahlRaeume;
    
        //default Konstruktor
        public Gebaude() {
        }
    
        //Methoden ausgabe der Raumanzahl
        public void printRaeume() {
            System.out.println(anzahlRaeume);
        }
    }
                                                                

    gazica1: Java Einführung: Vererbung und Komposition

  • 18 Mär. 2018
  • 10:05

    Einfürung in Java Vererbung

    Bei der Definition von Klassen orientiert man sich oft an Begriffen aus der realen Welt. Attribute und Methoden sollen genau einmal deklariert und definiert werden und nicht in mehreren Klassen auftauchen. Ein Hilfsmittel hierzu sind Klassenhierarchien. Durch Vererbung können Attribute und Methoden in abgeleiteten Klassen verfügbar gemacht werden. Dieses Prinzip haben Sie kennengelernt, nun wenden Sie es auf Ebene Java-Code an.
    Konstruktoren sind spezielle „Methoden“, welche zur Initialisierung von Objekten aufgerufen werden. Wie alle Methoden, können auch Konstruktoren Parameter haben, über welche beim Aufruf Werte mitegegben werden können. Das wird hier ausgenützt, um die Instanzvariablen der Objekte bei der Erzeugung mit den gewünschten Werten initialisieren zu können.
    class Person {
      String name;
      String vorname;
      int personalNummer;
    }
    
    class Chef extends Person {
      String abteilung;
    }
    
    class Fachangestellter extends Person {
      Chef vorgesetzter;
    }
    
    public class Personen {
      public static void main (String args[]) {
    	  Fachangestellter personal1 = new Fachangestellter();
    	  Fachangestellter personal2 = new Fachangestellter();
    	  Chef personal3 = new Chef();
    	  Person personal4 = new Person();
    
    	  personal3.name="Sattler";
    	  personal3.vorname="Beatrice";
    	  personal3.abteilung="Verkauf";
    	  personal1.name="Klein";
    	  personal1.vorname="Thomas";
    
    	  personal1.vorgesetzter=personal3; //*1
    	  personal4 = personal1;    //*2
    	  ((Fachangestellter)personal4).vorgesetzter = personal3;   //*3
    	  personal1 = (Fachangestellter)personal4;  //*4
    	  personal1 = (Fachangestellter) personal4; //*5
    
      }
    }
                                                                

    Vererbung und Referenzen.mp4

    Vererbung und Konstruktoren.mp4

    Vererbung und Speicherdiagramm.mp4