Konstruktives Software Engineering - Exam.pdf

Konstruktives Software Engineering - Exam
Konstruktives Software Engineering - Exam Aufgabe 1) Du bist Projektmanager für ein Softwareentwicklungsprojekt und stehst vor der Entscheidung, entweder das Wasserfallmodell oder agile Methoden für dieses Projekt zu verwenden. Deine Entscheidung sollte auf der Art des Projekts, den Anforderungen und den Ressourcen basieren, die zur Verfügung stehen. a) Erkläre anhand eines Beispiels, welches Szen...

© StudySmarter 2024, all rights reserved.

Konstruktives Software Engineering - Exam

Aufgabe 1)

Du bist Projektmanager für ein Softwareentwicklungsprojekt und stehst vor der Entscheidung, entweder das Wasserfallmodell oder agile Methoden für dieses Projekt zu verwenden. Deine Entscheidung sollte auf der Art des Projekts, den Anforderungen und den Ressourcen basieren, die zur Verfügung stehen.

a)

Erkläre anhand eines Beispiels, welches Szenario am besten für das Wasserfallmodell geeignet ist. Gehe dabei auf mindestens drei Gründe ein, warum dieses Modell in diesem Fall besser geeignet ist als agile Methoden.

Lösung:

Beispiel-Szenario: Du arbeitest an der Entwicklung einer Software für ein medizinisches Gerät, das strengen regulatorischen Anforderungen unterliegt und eine gründliche Dokumentation erfordert.

  • Klar definierte Anforderungen: Im Wasserfallmodell werden die Anforderungen zu Beginn des Projekts vollständig erfasst und dokumentiert. Bei einem medizinischen Gerät sind die Anforderungen in der Regel genau definiert und ändern sich selten. Das Wasserfallmodell stellt sicher, dass alle Anforderungen von Anfang an klar sind, was für die Einhaltung von Vorschriften entscheidend ist.
  • Regulatorische Compliance: Medizinische Geräte unterliegen umfangreichen regulatorischen Prüfungen und Zertifizierungen. Das Wasserfallmodell unterstützt durch seine Phasenstruktur die Erstellung umfassender Dokumentationen, die für regulatorische Zwecke erforderlich sind. Jede Phase des Projekts wird abgeschlossen und dokumentiert, bevor die nächste beginnt.
  • Vorhersehbarkeit und Planung: Das Wasserfallmodell ermöglicht eine detaillierte Planung und Schätzung der Projektdauer und -kosten. Da die Anforderungen im Voraus festgelegt werden, können Zeitpläne und Budgets genauer eingehalten werden. Dies ist besonders wichtig bei Projekten, bei denen Abweichungen erhebliche Risiken darstellen könnten, wie z.B. bei der Entwicklung eines medizinischen Geräts.

b)

Eine andere Abteilung in Deinem Unternehmen möchte agile Methoden wie Scrum einführen. Erläutere die zentralen Elemente von Scrum und wie diese zur Steigerung der Flexibilität und Kundenorientierung beitragen. Außerdem, beschreibe, wie User Stories und Sprints in der Projektplanung und -durchführung verwendet werden.

Lösung:

Die zentralen Elemente von Scrum und deren Beitrag zur Flexibilität und Kundenorientierung:

  • Product Owner: Der Product Owner ist verantwortlich für die Definition der Produktanforderungen und sorgt dafür, dass das Entwicklungsteam sich auf die Funktionen konzentriert, die den größten Mehrwert für die Kunden bieten. Durch regelmäßiges Feedback und Priorisierungen kann der Product Owner sicherstellen, dass das Produkt die Bedürfnisse der Kunden optimal erfüllt.
  • Scrum Master: Der Scrum Master unterstützt das Team dabei, Scrum-Praktiken effektiv anzuwenden und beseitigt Hindernisse, die den Fortschritt des Teams behindern könnten. Dies trägt zur kontinuierlichen Verbesserung und hohen Produktivität des Teams bei, was letztendlich der Flexibilität zugutekommt.
  • Development Team: Das Entwicklungsteam besteht aus cross-funktionalen Mitgliedern, die die Arbeit eigenverantwortlich planen und durchführen. Durch die kollektive Verantwortung und Diversität im Team können schnellere Anpassungen und innovative Lösungen gefunden werden.
  • Sprints: Ein Sprint ist ein zeitlich festgelegtes Intervall (typischerweise 2-4 Wochen), in dem das Team eine bestimmte Menge Arbeit liefert. Am Ende jedes Sprints wird ein fertiges, potentiell auslieferungsfähiges Produktinkrement präsentiert. Dies ermöglicht eine schnelle Iteration und Anpassung an sich ändernde Anforderungen.
  • Daily Stand-ups: Tägliche kurze Meetings, in denen Teammitglieder ihren Fortschritt besprechen und Hindernisse identifizieren. Dies fördert die Transparenz und frühzeitige Problemlösung.
  • Sprint Review und Retrospektive: Am Ende jedes Sprints findet eine Sprint Review statt, bei der das Team die geleistete Arbeit mit dem Product Owner und anderen Stakeholdern überprüft. In der anschließenden Sprint Retrospektive reflektiert das Team über den vergangenen Sprint und identifiziert Verbesserungspotentiale. Beide Meetings stärken die Kundenorientierung und kontinuierliche Verbesserung.

User Stories und Sprints in der Projektplanung und -durchführung:

  • User Stories: User Stories sind kurze, einfache Beschreibungen einer Funktion, die aus der Perspektive des Endbenutzers geschrieben sind. Sie bestehen typischerweise aus einem Titel, einer Beschreibung und Akzeptanzkriterien. User Stories helfen dabei, die Anforderungen in kleinere, handhabbare Teile zu zerlegen, die nach Priorität geordnet werden können. Dies ermöglicht eine flexible Anpassung an sich ändernde Anforderungen und fokussiert auf die Bereitstellung von Mehrwert.
  • Sprints: Während der Sprint-Planung wählt das Team aus dem Product Backlog (der Sammlung aller User Stories) die Items aus, die es im nächsten Sprint umsetzen will. Diese Auswahl basiert auf der Priorität und der geschätzten Kapazität des Teams. Jeder Sprint endet mit einem fertigen Produktinkrement, was regelmäßiges Feedback und Anpassung ermöglicht. Durch diese iterative Prozessstruktur kann das Team schnell auf sich ändernde Anforderungen und Kundenbedürfnisse reagieren.

Aufgabe 2)

Unified Modeling Language (UML)UML ist eine standardisierte Modellierungssprache im Software Engineering zur Spezifikation, Konstruktion und Dokumentation von Software-Systemen.

  • Diagrammtypen umfassen: Anwendungsfalldiagramme, Klassendiagramme, Sequenzdiagramme, Aktivitätsdiagramme, Zustandsdiagramme
  • Nutzt grafische Notation zur Darstellung von objektorientierten Konzepten
  • Erlaubt abstrakte und detaillierte Modellierung
  • Unterstützt verschiedene SDLC-Phasen (Analyse, Design, Implementierung, Testing)
  • Integrationswerkzeuge (z.B. Rational Rose, MagicDraw)
  • Konzepte: Akteure, Use Cases, Klassen, Objekte, Beziehungen, Zustände, Aktivitäten
  • Industriestandard durch die Object Management Group (OMG)

a)

Erstelle ein Anwendungsfalldiagramm für ein Online-Banking-System. Das System soll folgende Funktionalitäten umfassen: Geld überweisen, Kontostand anzeigen, Kontoauszug herunterladen, und Benachrichtigungen erhalten. Identifiziere und beschreibe die relevanten Akteure und Use Cases. Achte darauf, dass die Beziehungen zwischen den Use Cases klar und verständlich dargestellt sind.

Lösung:

Unified Modeling Language (UML)UML ist eine standardisierte Modellierungssprache im Software Engineering zur Spezifikation, Konstruktion und Dokumentation von Software-Systemen.

  • Diagrammtypen umfassen: Anwendungsfalldiagramme, Klassendiagramme, Sequenzdiagramme, Aktivitätsdiagramme, Zustandsdiagramme
  • Nutzt grafische Notation zur Darstellung von objektorientierten Konzepten
  • Erlaubt abstrakte und detaillierte Modellierung
  • Unterstützt verschiedene SDLC-Phasen (Analyse, Design, Implementierung, Testing)
  • Integrationswerkzeuge (z.B. Rational Rose, MagicDraw)
  • Konzepte: Akteure, Use Cases, Klassen, Objekte, Beziehungen, Zustände, Aktivitäten
  • Industriestandard durch die Object Management Group (OMG)
Erstellen eines Anwendungsfalldiagramms für ein Online-Banking-SystemEin Anwendungsfalldiagramm dient zur Darstellung der Interaktionen zwischen verschiedenen Akteuren und dem System. Für unser Online-Banking-System umfassen die relevanten Akteure und Use Cases:
  • Akteure:
    • Kunde: Der Nutzer des Online-Banking-Systems.
    • Benachrichtigungssystem: Ein automatisiertes System, das Benachrichtigungen an den Kunden sendet.
  • Use Cases:
    • Geld überweisen: Der Kunde kann Geld an andere Konten überweisen.
    • Kontostand anzeigen: Der Kunde kann den aktuellen Kontostand einsehen.
    • Kontoauszug herunterladen: Der Kunde kann einen Kontoauszug als Dokument herunterladen.
    • Benachrichtigungen erhalten: Der Kunde kann Benachrichtigungen über Transaktionen oder Kontostandänderungen erhalten.
Beziehungen zwischen den Use Cases:
  • Extend: 'Benachrichtigungen erhalten' erstreckt sich über die anderen Use Cases, da der Kunde Benachrichtigungen erhalten kann, wenn er Geld überweist, den Kontostand anzeigt oder einen Kontoauszug herunterlädt.
  • Includes: 'Kontoauszug herunterladen' schließt 'Kontostand anzeigen' ein, da der aktuelle Kontostand auch im Kontoauszug dargestellt wird.
Anwendungsfalldiagramm:Hier ist eine visuelle Darstellung des Anwendungsfalldiagramms:Use Case Diagram for Online Banking
  • Der Akteur 'Kunde' interagiert mit allen Use Cases.
  • Die Use Cases 'Geld überweisen', 'Kontostand anzeigen', und 'Kontoauszug herunterladen' beinhalten jeweils 'Benachrichtigungen erhalten'.
  • Der Use Case 'Kontoauszug herunterladen' beinhaltet 'Kontostand anzeigen'.

b)

Entwerfe ein Klassendiagramm für das oben beschriebenen Online-Banking-System. Das Diagramm soll die folgenden Klassen umfassen: Benutzer, Konto, Transaktion, Benachrichtigung. Stelle die Beziehungen zwischen den Klassen dar und achte darauf, dass Eigenschaften und Methoden sinnvoll zugeordnet sind. Nutze korrekte Notation, um Assoziationen, Vererbungen und Aggregationen darzustellen.

Lösung:

Unified Modeling Language (UML)UML ist eine standardisierte Modellierungssprache im Software Engineering zur Spezifikation, Konstruktion und Dokumentation von Software-Systemen.

  • Diagrammtypen umfassen: Anwendungsfalldiagramme, Klassendiagramme, Sequenzdiagramme, Aktivitätsdiagramme, Zustandsdiagramme
  • Nutzt grafische Notation zur Darstellung von objektorientierten Konzepten
  • Erlaubt abstrakte und detaillierte Modellierung
  • Unterstützt verschiedene SDLC-Phasen (Analyse, Design, Implementierung, Testing)
  • Integrationswerkzeuge (z.B. Rational Rose, MagicDraw)
  • Konzepte: Akteure, Use Cases, Klassen, Objekte, Beziehungen, Zustände, Aktivitäten
  • Industriestandard durch die Object Management Group (OMG)
Entwerfen eines Klassendiagramms für ein Online-Banking-SystemEin Klassendiagramm zeigt die Struktur eines Systems durch die Darstellung der Klassen, ihrer Eigenschaften, Methoden und die Beziehungen zwischen den Klassen.Klassen und ihre Eigenschaften und Methoden:
  • Benutzer:
    • Eigenschaften: Benutzer-ID, Name, Email, Passwort
    • Methoden: anmelden(), abmelden(), profilAktualisieren()
  • Konto:
    • Eigenschaften: Kontonummer, Kontostand, Kontotyp
    • Methoden: einzahlen(betrag), abheben(betrag), kontostandAnzeigen()
  • Transaktion:
    • Eigenschaften: Transaktions-ID, Datum, Betrag, Beschreibung
    • Methoden: transaktionErstellen(), transaktionAnzeigen()
  • Benachrichtigung:
    • Eigenschaften: Benachrichtigungs-ID, Nachricht, Empfänger
    • Methoden: benachrichtigungSenden(), benachrichtigungAnzeigen()
Beziehungen zwischen den Klassen:
  • Benutzer besitzt Konto: Ein Benutzer kann mehrere Konten besitzen.
  • Konto führt Transaktion: Ein Konto kann mehrere Transaktionen durchführen.
  • Benutzer erhält Benachrichtigung: Ein Benutzer kann mehrere Benachrichtigungen erhalten.
Notation:
  • Assoziation: Ein einfacher Verbindungspfad, z.B. Benutzer → Konto, Konto → Transaktion.
  • Aggregation: Ein leerer Diamantkopf zeigt Aggregation an, z.B. Konto →◇→ Transaktion.
  • Vererbung: Ein Dreieckskopf zeigt Vererbung an, diese Beziehungen sind bei diesem Szenario jedoch nicht nötig.
Klassendiagramm:Hier ist eine visuelle Darstellung des Klassendiagramms:Class Diagram for Online Banking
  • Benutzer -[1..*]-- besitzt ---[1..*]--Konto
  • Konto -[1..*]-- führt durch ---[1..*]--Transaktion
  • Benutzer -[1..*]-- erhält ---[1..*]--Benachrichtigung

c)

Erstelle ein Sequenzdiagramm, das den Ablauf der Geldüberweisung in dem Online-Banking-System darstellt. Zeige den Interaktionsprozess zwischen dem Benutzer, dem System und relevanten Objekten. Der Prozess beginnt damit, dass der Benutzer die Überweisungsfunktion auswählt und endet mit einer Bestätigung der erfolgreichen Transaktion.

Lösung:

Unified Modeling Language (UML)UML ist eine standardisierte Modellierungssprache im Software Engineering zur Spezifikation, Konstruktion und Dokumentation von Software-Systemen.

  • Diagrammtypen umfassen: Anwendungsfalldiagramme, Klassendiagramme, Sequenzdiagramme, Aktivitätsdiagramme, Zustandsdiagramme
  • Nutzt grafische Notation zur Darstellung von objektorientierten Konzepten
  • Erlaubt abstrakte und detaillierte Modellierung
  • Unterstützt verschiedene SDLC-Phasen (Analyse, Design, Implementierung, Testing)
  • Integrationswerkzeuge (z.B. Rational Rose, MagicDraw)
  • Konzepte: Akteure, Use Cases, Klassen, Objekte, Beziehungen, Zustände, Aktivitäten
  • Industriestandard durch die Object Management Group (OMG)
Erstellen eines Sequenzdiagramms für die Geldüberweisung im Online-Banking-SystemEin Sequenzdiagramm zeigt die Interaktionen und die Reihenfolge der Nachrichten zwischen verschiedenen Systemkomponenten.Sequenzdiagramm für die Geldüberweisung:Akteure und Objekte:
  • Benutzer: Der Kunde, der die Überweisung initiiert.
  • Online-Banking-System: Das Hauptsystem, welches die Anfragen verarbeitet.
  • Kontoverwaltung: Ein Subsystem oder eine Komponente, die für die Handhabung von Konten zuständig ist.
  • Transaktionsverwaltung: Ein Subsystem oder eine Komponente, die für die Durchführung und Überprüfung von Transaktionen zuständig ist.
  • Benachrichtigungssystem: Ein System, das Benachrichtigungen an den Benutzer sendet.
Ablauf der Geldüberweisung:
  1. Benutzer: Wählt die Funktion „Geld überweisen“ im Online-Banking-System.
  2. Online-Banking-System: Zeigt ein Formular zur Eingabe von Überweisungsdetails an.
  3. Benutzer: Gibt die Überweisungsdetails ein und bestätigt die Überweisung.
  4. Online-Banking-System: Validiert die Eingabedaten.
  5. Kontoverwaltung: Überprüft den Kontostand und andere relevante Konto Details.
  6. Transaktionsverwaltung: Führt die Transaktion aus und aktualisiert die Kontensalden.
  7. Transaktionsverwaltung: Sendet eine Bestätigung der erfolgreichen Transaktion an das Online-Banking-System.
  8. Online-Banking-System: Zeigt die Bestätigung der erfolgreichen Transaktion an den Benutzer.
  9. Benachrichtigungssystem: Sendet eine Benachrichtigung über die Transaktion an den Benutzer.
Visuelle Darstellung des Sequenzdiagramms:Hier ist eine visuelle Darstellung des Sequenzdiagramms:Sequence Diagram for Money Transfer
  • Benutzer --(Wählt Überweisung)--> Online-Banking-System
  • Online-Banking-System --(Zeigt Formular an)--> Benutzer
  • Benutzer --(Bestätigt Überweisung)--> Online-Banking-System
  • Online-Banking-System --(Validiert Daten)--> Kontoverwaltung
  • Kontoverwaltung --(Überprüft Kontostand)--> Transaktionsverwaltung
  • Transaktionsverwaltung --(Führt Transaktion aus)--> Kontoverwaltung
  • Transaktionsverwaltung --(Sendet Bestätigung)--> Online-Banking-System
  • Online-Banking-System --(Zeigt Bestätigung an)--> Benutzer
  • Online-Banking-System --(Sendet Benachrichtigung)--> Benachrichtigungssystem
  • Benachrichtigungssystem --(Sendet Benachrichtigung)--> Benutzer

d)

Beschreibe ein Zustandsdiagramm für die Klasse Konto im Online-Banking-System. Das Diagramm soll die verschiedenen Zustände eines Kontos (z.B. Aktiv, Inaktiv, Überzogen) und die Ereignisse, die zu einem Zustandswechsel führen, darstellen. Achte darauf, dass alle möglichen Zustandsübergänge vollständig und korrekt beschrieben sind.

Lösung:

Unified Modeling Language (UML)UML ist eine standardisierte Modellierungssprache im Software Engineering zur Spezifikation, Konstruktion und Dokumentation von Software-Systemen.

  • Diagrammtypen umfassen: Anwendungsfalldiagramme, Klassendiagramme, Sequenzdiagramme, Aktivitätsdiagramme, Zustandsdiagramme
  • Nutzt grafische Notation zur Darstellung von objektorientierten Konzepten
  • Erlaubt abstrakte und detaillierte Modellierung
  • Unterstützt verschiedene SDLC-Phasen (Analyse, Design, Implementierung, Testing)
  • Integrationswerkzeuge (z.B. Rational Rose, MagicDraw)
  • Konzepte: Akteure, Use Cases, Klassen, Objekte, Beziehungen, Zustände, Aktivitäten
  • Industriestandard durch die Object Management Group (OMG)
Erstellen eines Zustandsdiagramms für die Klasse Konto im Online-Banking-SystemEin Zustandsdiagramm zeigt die verschiedenen Zustände einer Klasse und die Ereignisse, die Zustandsänderungen verursachen.Zustände der Klasse Konto:
  • Aktiv: Das Konto ist in vollem Umfang aktiv und funktionsfähig.
  • Inaktiv: Das Konto ist momentan nicht aktiv und kann nicht für Transaktionen genutzt werden.
  • Überzogen: Das Konto hat ein negatives Guthaben und ist über den Kreditrahmen hinaus belastet.
Ereignisse und Übergänge:
  • Einzahlung: Ein positiver Geldbetrag wird auf das Konto eingezahlt. Dieser Zustand kann von jeder Situation ausgehen und zu „Aktiv“ führen.
  • Abhebung: Ein Betrag wird vom Konto abgehoben, kann den Zustand beibehalten oder das Konto überziehen und zu „Überzogen“ ändern.
  • Konto deaktivieren: Das Konto wird inaktiv gesetzt, kann von „Aktiv” oder „Überzogen“ zu „Inaktiv“ führen.
  • Konto reaktivieren: Das Konto wird wieder aktiviert, wechselt von „Inaktiv“ zu „Aktiv“.
  • Überziehung decken: Das negative Guthaben wird gedeckt, wechselt von „Überzogen“ zu „Aktiv“.
Zustandsdiagramm:Hier ist eine visuelle Darstellung des Zustandsdiagramms:State Diagram for Account
  • Anfangszustand: Aktiv
  • Aktiv:
    • Übergang zu „Überzogen“ bei Überziehung.
    • Übergang zu „Inaktiv“ bei Deaktivierung.
    • Bleibt „Aktiv“ bei Transaktionen, die den Kontostand nicht negativ machen.
  • Überzogen:
    • Übergang zu „Aktiv“ bei Einzahlen oder Überziehung abdecken.
    • Übergang zu „Inaktiv“ bei Deaktivierung.
  • Inaktiv:
    • Übergang zu „Aktiv“ bei Reaktivierung.
  • Übergangsereignisse:
    • Einzahlung
    • Abhebung
    • Konto deaktivieren
    • Konto reaktivieren
    • Überziehung decken

Aufgabe 3)

Du bist Teil eines Softwareentwicklungsteams, das für eine Firma arbeitet, die eine neue Softwarelösung entwickeln soll. Um die Entwicklungszeit zu verkürzen und flexible, wartbare Software zu schaffen, schlägt dein Teamleiter vor, Design Patterns einzusetzen. Du sollst diesbezüglich zwei spezifische Anforderungen bearbeiten.

a)

Beschreibe das Singleton Design Pattern und erkläre, warum und wann es sinnvoll ist, dieses anzuwenden. Gehe dabei auf die Problemstellungen ein, die durch das Singleton Design Pattern gelöst werden können. Implementiere anschließend das Singleton Design Pattern in Java und erläutere den Code.

Lösung:

Singleton Design Pattern

  • Beschreibung: Das Singleton Design Pattern stellt sicher, dass eine Klasse nur eine Instanz hat und bietet einen globalen Zugriffspunkt auf diese Instanz. Es verhindert somit die mehrfache Erstellung von Instanzen einer Klasse.
  • Warum und wann es sinnvoll ist:
    • Es ist nützlich in Situationen, in denen eine einzige Instanz einer Klasse eine zentrale Rolle spielt und diese einzige Instanz für die gesamte Steuerung verantwortlich ist. Beispiele hierfür sind Konfigurationsklassen, Logger-Klassen, Verbindungsmanager zu Datenbanken oder Ressourcenkontroller.
    • Das Singleton Design Pattern ist sinnvoll, um Ressourcenkonflikte zu vermeiden, indem sichergestellt wird, dass nur eine Instanz einer bestimmten Klasse existiert. Dies kann die Speicherverwaltung und die Synchronisation verbessern.
  • Lösbare Problemstellungen: Das Singleton Design Pattern kann die folgenden Probleme lösen:
    • Mehrfachinstanzierung einer Klasse, die zu Inkonsistenzen und erhöhtem Ressourcenverbrauch führen kann.
    • Bereitstellung von globaler Zugriffsmöglichkeit auf eine spezielle Instanz.
    • Notwendigkeit der Steuerung über einen zentralen Punkt, z.B. Konfigurationsdaten oder Logging.
  • Implementierung des Singleton Design Patterns in Java: Untenstehend ist der implementierte Code sowie eine Erläuterung:
     public class Singleton {  // Statische Variable, die die einzige Instanz der Klasse hält.  private static Singleton uniqueInstance;   // Privater Konstruktor, verhindert die Instanzierung durch andere Klassen.  private Singleton() { }   // Öffentliche Methode, die den Zugriffspunkt zur einzigen Instanz der Klasse bietet.  public static synchronized Singleton getInstance() {  if (uniqueInstance == null) {  uniqueInstance = new Singleton();  }  return uniqueInstance;  }   // Beispielmethode, die die Funktionalität der Klasse repräsentiert.  public void showMessage() {  System.out.println('Hello World from Singleton!');  }  } 

Erläuterung des Codes:

  • Statische Variable: Die statische Variable uniqueInstance hält die einzige Instanz der Klasse Singleton. Diese Variable wird nur einmal initialisiert.
  • Privater Konstruktor: Der Konstruktor ist privat, um die Erstellung von Instanzen anderer Klassen zu verhindern.
  • Methode getInstance(): Diese Methode bietet den globalen Zugriffspunkt. Sie prüft, ob die Instanz bereits existiert. Wenn nicht, erstellt sie eine neue Instanz und gibt diese zurück. Die Methode ist synchronisiert, um Thread-Sicherheit zu gewährleisten.
  • Beispielmethode: Die Methode showMessage() ist ein Beispiel, das zeigt, wie Methoden genutzt werden können, die in der Singleton-Klasse definiert sind.

b)

Ein Teil der neuen Software benötigt eine Möglichkeit, mehrere verschiedene Objekte eines bestimmten Typs zu erzeugen, ohne deren konkrete Klasse genau spezifizieren zu müssen. Welches Design Pattern würdest Du anwenden und warum? Erkläre das Pattern ausführlich und implementiere es in Java. Gehe dabei auf die Alternativen ein, die ohne dieses Design Pattern genutzt werden könnten, und beschreibe deren Nachteile.

Lösung:

Factory Design Pattern

  • Beschreibung: Das Factory Design Pattern ermöglicht die Erstellung von Objekten, ohne die exakte Klasse der zu erzeugenden Objekte angeben zu müssen. Es definiert eine Schnittstelle oder eine abstrakte Klasse zur Erstellung eines Objekts, lässt aber die Unterklassen entscheiden, welche Klasse instanziiert werden soll.
  • Warum es sinnvoll ist:
    • Das Factory Pattern fördert die Entkopplung des Codes, indem es den Code, der Objekte erstellt, von dem Code trennt, der diese Objekte verwendet. Dadurch wird der Code flexibler und einfacher zu warten.
    • Es erleichtert die Erweiterbarkeit, da neue Objekttypen erstellt werden können, ohne den bestehenden Code zu ändern.
  • Alternativen und ihre Nachteile:
    • Direkte Instanziierung: Wenn Objekte direkt mit dem new-Operator erstellt werden, ist die Klasse eng mit dem Code verbunden, was zu geringer Flexibilität und schwieriger Wartbarkeit führt.
    • Simple Factory: Eine einfache Fabrik kann verwendet werden, um die Erstellungslogik zu kapseln. Jedoch kann dies die Erweiterbarkeit einschränken, da Änderungen an neuen Produktklassen auch Änderungen an der Fabrik erfordern.
  • Implementierung des Factory Design Patterns in Java:
     // Schritt 1: Erstellen Sie eine Schnittstelle oder eine abstrakte Klasse für die Produkte  public interface Product {  void use();  }   // Schritt 2: Erstellen Sie konkrete Produktklassen, die die Schnittstelle implementieren  public class ProductA implements Product {  @Override  public void use() {  System.out.println('ProductA is used');  }  }   public class ProductB implements Product {  @Override  public void use() {  System.out.println('ProductB is used');  }  }   // Schritt 3: Erstellen Sie eine Factory-Klasse, die die Objekte erstellt  public class ProductFactory {  public static Product createProduct(String type) {  switch (type) {  case 'A':  return new ProductA();  case 'B':  return new ProductB();  default:  throw new IllegalArgumentException('Unknown product type');  }  }  }   // Schritt 4: Verwenden Sie die Factory in der Anwendung  public class Main {  public static void main(String[] args) {  Product productA = ProductFactory.createProduct('A');  productA.use();   Product productB = ProductFactory.createProduct('B');  productB.use();  }  } 
  • Erläuterung des Codes:
    • Schritt 1 (Schnittstelle): Die Schnittstelle Product definiert die Methode use(), die von allen konkreten Produkten implementiert werden muss.
    • Schritt 2 (Konkrete Produkte): Die Klassen ProductA und ProductB implementieren die Schnittstelle Product und überschreiben die Methode use().
    • Schritt 3 (Factory-Klasse): Die Klasse ProductFactory enthält die Methode createProduct(), die basierend auf dem übergebenen Parameter den entsprechenden Produkttyp erstellt.
    • Schritt 4 (Anwendung): In der Main-Klasse wird die Factory verwendet, um Objekte der gewünschten Typen zu erstellen und deren Methoden aufzurufen.

Aufgabe 4)

Test-driven Development (TDD)Test-driven Development (TDD) ist ein Ansatz zur schrittweisen Softwareentwicklung, bei dem Tests geschrieben werden, bevor der eigentliche Code implementiert wird. Der iterative Zyklus des TDD besteht aus drei Schritten:

  • Test schreiben
  • Code implementieren
  • Test ausführen
Dieses Vorgehen gewährleistet, dass der geschriebene Code die vorab definierten Spezifikationen erfüllt. TDD betont die Wichtigkeit kleiner, isolierter Testfälle (Unit Tests), um die Robustheit und Wartbarkeit des Codes sicherzustellen. Ein typischer TDD-Zyklus besteht aus den Phasen Red (Test schlägt fehl), Green (Code schreiben bis Test erfolgreich) und Refactor (Code optimieren).Gegeben sei eine Funktion increment(x), die eine Zahl um eins erhöht. Schreibe und implementiere Testfälle für diese Funktion in einem TDD-Zyklus, um die Funktionsweise von TDD besser zu verstehen.

a)

1. Schreibe einen initialen Fehlschlagtest für die Funktion increment(x), der überprüft, ob die Funktion die Zahl x um eins erhöht. Du darfst davon ausgehen, dass die Funktion increment(x) noch nicht implementiert ist. Verwende hierfür eine Programmiersprache Deiner Wahl.

Lösung:

Initialer Fehlschlagtest für die Funktion increment(x)

Um einen initialen Fehlschlagtest für die Funktion increment(x) zu schreiben, gehen wir davon aus, dass die Funktion noch nicht implementiert ist. Der Test soll sicherstellen, dass die Funktion die Eingabezahl um eins erhöht. Hier verwenden wir Python und das Unit-Testing-Framework unittest.

Folgende Schritte sind notwendig:

  • Erstelle eine neue Testdatei.
  • Definiere eine Testklasse, die von unittest.TestCase erbt.
  • Schreibe einen Testfall, der die Funktion increment(x) aufruft und das Ergebnis überprüft.
import unittestclass TestIncrementFunction(unittest.TestCase):    def test_increment(self):        result = increment(1)        self.assertEqual(result, 2)if __name__ == '__main__':    unittest.main()

Dieser Test ruft die Funktion increment(1) auf und erwartet, dass das Ergebnis 2 ist. Da die Funktion increment(x) noch nicht implementiert ist, wird der Test fehlschlagen, wenn wir ihn ausführen. Dies ist der Beginn des Red-Zyklus des TDD-Prozesses.

b)

2. Implementiere die Funktion increment(x) so, dass der vorher geschriebene Test erfolgreich ist. Beschreibe die Schritte, die Du bei der Implementierung gegangen bist und erläutere, wie Du sicherstellst, dass die Implementierung korrekt ist.

Lösung:

Implementierung der Funktion increment(x)

Nachdem wir den initialen Fehlschlagtest für die Funktion increment(x) geschrieben und ausgeführt haben, stellen wir sicher, dass der Test fehlschlägt. Der nächste Schritt im TDD-Zyklus ist die Implementierung der Funktion, sodass der Test erfolgreich ist.

Schritte zur Implementierung

  1. Erstellen der Funktion: Implementiere eine einfache Funktion increment(x), die den Wert von x um eins erhöht.
  2. Testen der Funktion: Führe die bereits geschriebenen Tests erneut aus, um sicherzustellen, dass sie erfolgreich sind.

Implementierung

def increment(x):    return x + 1

Nun führen wir die Tests erneut aus, um sicherzustellen, dass sie erfolgreich sind.

Sicherstellen der Korrektheit

Um sicherzustellen, dass die Implementierung korrekt ist, sollte der vorher geschriebene Test keine Fehler mehr ausgeben, wenn wir ihn ausführen. Dies zeigt, dass die Funktion increment(x) korrekt implementiert wurde und die erwarteten Ergebnisse liefert.

Ausführung der Tests

import unittestclass TestIncrementFunction(unittest.TestCase):    def test_increment(self):        result = increment(1)        self.assertEqual(result, 2)if __name__ == '__main__':    unittest.main()

Wenn die Testausführung erfolgreich ist, bedeutet dies, dass die Implementierung der Funktion increment(x) korrekt ist und der Testfall bestanden wird.

c)

3. Füge zusätzliche Testfälle hinzu, um sicherzugehen, dass die Funktion increment(x) robust gegenüber verschiedenen Eingangswerten ist. Berücksichtige dabei beispielsweise negative Werte, Null und große Zahlen. Implementiere die Tests und beschreibe, welche weiteren Anpassungen am Code nötig waren, um alle Tests zum Erfolg zu führen.

Lösung:

Zusätzliche Testfälle für die Funktion increment(x)

Um sicherzustellen, dass die Funktion increment(x) robust gegenüber verschiedenen Arten von Eingabewerten ist, fügen wir zusätzliche Testfälle hinzu. Diese Testfälle sollen negative Werte, Null und große Zahlen berücksichtigen. Wir verwenden weiterhin Python und das unittest-Framework.

Schritte zur Implementierung der zusätzlichen Tests

  1. Erweitern der bestehenden Testklasse, um zusätzliche Testfälle zu enthalten.
  2. Ausführen der Tests, um sicherzustellen, dass die Funktion korrekt arbeitet.

Zusätzliche Testfälle

import unittestdef increment(x):    return x + 1class TestIncrementFunction(unittest.TestCase):    def test_increment(self):        self.assertEqual(increment(1), 2)    def test_increment_zero(self):        self.assertEqual(increment(0), 1)    def test_increment_negative(self):        self.assertEqual(increment(-1), 0)    def test_increment_large_number(self):        self.assertEqual(increment(1000000), 1000001)if __name__ == '__main__':    unittest.main()

Diese weiteren Tests prüfen:

  • Erhöhung von Null (test_increment_zero())
  • Erhöhung einer negativen Zahl (test_increment_negative())
  • Erhöhung einer großen Zahl (test_increment_large_number())

Da unsere Implementierung der Funktion increment(x) sehr einfach ist und nur den Wert um eins erhöht, sollten keine weiteren Anpassungen nötig sein. Wir führen die erweiterten Tests aus, um sicherzustellen, dass alle erfolgreich sind und die Funktion in allen Szenarien korrekt arbeitet.

Ausführen der Tests

Durch das Ausführen der erweiterten Tests mittels unittest.main() stellen wir sicher, dass die Funktion increment(x) wie erwartet arbeitet. Wenn alle Tests erfolgreich sind, ist die Implementierung robust gegenüber den getesteten Eingabewerten.

d)

4. Nachdem alle Tests erfolgreich sind, refactoriere Deinen Code, um ihn zu optimieren und zu verbessern. Beschreibe die vorgenommenen Änderungen und erläutere, welche Vorteile die Verbesserungen mit sich bringen. Achte dabei darauf, dass alle Tests nach dem Refactoring immer noch erfolgreich ausgeführt werden.

Lösung:

Code-Refactoring der Funktion increment(x)

Nachdem wir alle Tests erfolgreich ausgeführt haben, ist es Zeit, den Code zu optimieren und zu verbessern. Dieser Schritt gehört zur Refactor-Phase des TDD-Zyklus. Beim Refactoring geht es darum, den bestehenden Code zu verbessern, ohne dabei dessen Funktionalität zu ändern.

Schritte zum Refactoring

  1. Analyse des aktuellen Codes, um Verbesserungspotential zu identifizieren.
  2. Implementierung der Verbesserungen.
  3. Ausführung der Tests, um sicherzustellen, dass sie nach dem Refactoring immer noch erfolgreich sind.

Aktueller Code

def increment(x):    return x + 1

Da die Funktion increment(x) bereits sehr einfach und klar ist, gibt es wenig Raum für Verbesserungen innerhalb der Funktion selbst. Stattdessen können wir unser Test-Setup verbessern, um es wartbarer und erweiterbarer zu machen.

Verbesserung des Test-Setups

Wir können den Code weiter optimieren, indem wir die Testfälle konsolidieren, um Redundanzen zu vermeiden. Statt für jede Testbedingung eine separate Methode zu definieren, können wir eine Testmethode mit Parameterisierung verwenden.

Optimiertes Test-Setup

import unittestfrom parameterized import parameterizeddef increment(x):    return x + 1class TestIncrementFunction(unittest.TestCase):    @parameterized.expand([        (1, 2),        (0, 1),        (-1, 0),        (1000000, 1000001)    ])    def test_increment(self, input_value, expected_output):        self.assertEqual(increment(input_value), expected_output)if __name__ == '__main__':    unittest.main()

Hier verwenden wir das parameterized-Modul, um die Testfälle innerhalb einer Methode zu konsolidieren. Dies bringt mehrere Vorteile:

  • Wartbarkeit: Das Hinzufügen neuer Testfälle erfordert weniger Duplikation.
  • Übersichtlichkeit: Der Testcode ist kürzer und übersichtlicher.
  • Erweiterbarkeit: Neue Testdaten können leicht hinzugefügt werden, ohne die Teststruktur zu ändern.

Führe nach dem Refactoring die Tests erneut aus, um sicherzustellen, dass alle erfolgreich sind:

Tests Ausführen

if __name__ == '__main__':    unittest.main()

Alle Tests sollten auch nach dem Refactoring weiterhin erfolgreich ausgeführt werden. Das zeigt, dass die Verbesserungen keine unerwünschten Nebenwirkungen hatten und die Funktionalität unverändert ist.

Sign Up

Melde dich kostenlos an, um Zugriff auf das vollständige Dokument zu erhalten

Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.

Kostenloses Konto erstellen

Du hast bereits ein Konto? Anmelden