Softwareentwicklungspraktikum Lehramt - Exam.pdf

Softwareentwicklungspraktikum Lehramt - Exam
Softwareentwicklungspraktikum Lehramt - Exam Aufgabe 1) Agile Projektmanagement Du arbeitest in einem Softwareentwicklungsteam, das sich entschieden hat, agile Methoden zur Prozessoptimierung zu verwenden. Dein Team verwendet eine Kombination aus Scrum und Kanban, um den Workflow zu verwalten und die Projektleistung zu maximieren. Das Team möchte die Vorteile beider Methoden verstehen und anwenden...

© StudySmarter 2024, all rights reserved.

Softwareentwicklungspraktikum Lehramt - Exam

Aufgabe 1)

Agile Projektmanagement Du arbeitest in einem Softwareentwicklungsteam, das sich entschieden hat, agile Methoden zur Prozessoptimierung zu verwenden. Dein Team verwendet eine Kombination aus Scrum und Kanban, um den Workflow zu verwalten und die Projektleistung zu maximieren. Das Team möchte die Vorteile beider Methoden verstehen und anwenden können, um die Effizienz zu steigern.

a)

Als Product Owner bist Du verantwortlich für die Verwaltung des Product Backlogs. Erläutere den Prozess der Erstellung und Verwaltung eines Product Backlogs in Scrum und wie dieser Prozess zur Priorisierung und Lieferung von wichtigen Features führt. Diskutiere auch mindestens zwei verschiedene Techniken, die Du anwenden könntest, um die Einträge im Product Backlog zu priorisieren. Berücksichtige dabei sowohl funktionale als auch nicht-funktionale Anforderungen.

Lösung:

  • Erstellung und Verwaltung des Product Backlogs Als Product Owner bist Du dafür verantwortlich, das Product Backlog zu erstellen und zu pflegen. Der Prozess umfasst die folgenden Schritte:
    • 1. Anforderungen Sammeln: Sammle alle Benutzeranforderungen, Geschäftsziele und Stakeholder-Anfragen in Form von User Stories, Epics oder technischen Aufgaben.
    • 2. Einträge Hinzufügen: Füge die gesammelten Anforderungen in das Product Backlog ein. Jeder Eintrag sollte eine klare Beschreibung, Akzeptanzkriterien und eine geschätzte Größe bzw. Komplexität haben.
    • 3. Priorisieren: Bestimme die Wichtigkeit der Einträge und ordne sie entsprechend. Bevorzugt werden Einträge, die den größten Wert für das Produkt und die Endbenutzer liefern.
    • 4. Pflegen: Überprüfe regelmäßig das Product Backlog und passe es bei Bedarf an, indem Du neue Einträge hinzufügst, bestehende ändern oder entfernen.
    • 5. Verfeinern: Arbeite mit dem Entwicklungsteam zusammen, um die Backlog-Einträge in kleinere, besser verständliche und besser überprüfbare Stücke zu zerlegen.
  • Priorisierungstechniken:
    • 1. MoSCoW-Methode: Diese Methode unterteilt die Einträge in Must-haves (müssen unbedingt umgesetzt werden), Should-haves (sollten umgesetzt werden), Could-haves (könnten umgesetzt werden) und Won't-haves (werden in diesem Zyklus nicht umgesetzt). Dies hilft, sich auf die wichtigsten Features zu konzentrieren.
    • 2. Value vs. Effort: Um die Einträge im Product Backlog zu priorisieren, kannst Du diese Methode verwenden. Sie bewertet den Mehrwert eines Features gegen den Aufwand, es umzusetzen. Einträge mit hohem Wert und niedrigem Aufwand werden bevorzugt.
    • Funktionale und nicht-funktionale Anforderungen: Es ist wichtig, sowohl funktionale (z.B. neue Features) als auch nicht-funktionale Anforderungen (z.B. Performance, Sicherheit) zu berücksichtigen. Nutze Techniken wie die oben genannten, um eine umfassende Priorisierung zu gewährleisten.

b)

In einem aktuellen Projekt hast Du festgestellt, dass die Teams ihre Workflows unterschiedlich managen. Ein Team verwendet ein reines Kanban-Board, während ein anderes Team den Workflow mit Scrum-Methoden organisiert. Analysiere die Unterschiede zwischen diesen beiden Ansätzen hinsichtlich der Verwaltung laufender Arbeiten (WIP - Work In Progress) und der kontinuierlichen Verbesserung. Gehe dabei auf die Struktur der Meetings und die Rollen innerhalb des Teams ein und wie diese Aspekte zur Effizienz bzw. Flexibilität der Arbeitsweise beitragen können.

Lösung:

  • Verwaltung laufender Arbeiten (WIP) und kontinuierliche Verbesserung
    • 1. Kanban:
    • WIP: Bei Kanban wird der Fokus auf die Begrenzung der laufenden Arbeiten (Work In Progress) gelegt. Das bedeutet, dass zu jedem Zeitpunkt nur eine begrenzte Anzahl von Aufgaben in jedem Stadium des Workflows bearbeitet werden darf. Dies hilft, Engpässe zu verringern und die Durchlaufzeiten zu verbessern.
    • Kontinuierliche Verbesserung: Kanban fördert eine kontinuierliche Verbesserung durch regelmäßige Überprüfung und Anpassung des Workflows. Teams analysieren ihre Prozesse und suchen nach Möglichkeiten zur Optimierung, indem sie Metriken wie Durchsatz und Zykluszeiten überwachen.
    • Meetings: Es gibt weniger strukturierte Meetings in Kanban im Vergleich zu Scrum. Statt festgelegter Sprints und zugehöriger Meetings gibt es häufige, informelle Stand-Up-Meetings und Retrospektiven, um den Workflow kontinuierlich zu prüfen und anzupassen.
    • Rollen: In Kanban gibt es keine fest definierten Rollen wie in Scrum. Die Teammitglieder arbeiten kollaborativ und übernehmen flexibel verschiedene Aufgaben.
    • 2. Scrum:
    • WIP: In Scrum wird die Anzahl der laufenden Arbeiten durch die Planung in Sprints begrenzt. Ein Sprint ist eine festgelegte Zeitspanne (normalerweise 2-4 Wochen), in der ein definiertes Set von Aufgaben bearbeitet wird. Dies hilft dem Team, sich auf eine bestimmte Menge an Arbeit zu konzentrieren und sicherzustellen, dass diese innerhalb des Sprints abgeschlossen wird.
    • Kontinuierliche Verbesserung: Scrum beinhaltet regelmäßige Rituale wie Sprint Reviews, Sprint Retrospectives und Daily Stand-Ups, die die kontinuierliche Verbesserung fördern. Das Team reflektiert seine Arbeitsweise und identifiziert Bereiche für Verbesserungen nach jedem Sprint.
    • Meetings: Scrum hat eine klar strukturierte Meeting-Struktur, einschließlich Sprint Planning, Daily Stand-Ups, Sprint Reviews und Sprint Retrospectives. Diese wiederkehrenden Meetings helfen dem Team, organisiert zu bleiben und sich ständig zu verbessern.
    • Rollen: In Scrum gibt es spezifische Rollen: Product Owner, Scrum Master und das Entwicklungsteam. Jede Rolle hat klare Verantwortlichkeiten. Der Product Owner priorisiert das Backlog, der Scrum Master unterstützt das Team, Hindernisse zu überwinden und das Entwicklungsteam führt die Arbeiten aus.
  • Vergleich von Kanban und Scrum hinsichtlich Effizienz und Flexibilität:
    • Effizienz: Kanban bietet Effizienz durch einen stetigen Fluss und die Begrenzung der WIP. Es vermeidet die Unterbrechungen und Neustartkosten, die bei Sprints in Scrum auftreten können. Scrum bietet Effizienz durch klare Struktur und Zielvorgaben innerhalb eines Sprints.
    • Flexibilität: Kanban ist flexibler, da Änderungen jederzeit vorgenommen werden können und es keine festen Rollen oder Zeiträume gibt. Scrum ist weniger flexibel aufgrund der festen Sprint-Zeiten und klar definierten Rollen, bietet aber klare Strukturen und regelmäßige Reviews, um Anpassungen vorzunehmen.

Aufgabe 2)

Du arbeitest an einem Projekt zur Implementierung einer einfachen Taschenrechner-Anwendung, die grundlegende mathematische Operationen wie Addition, Subtraktion, Multiplikation und Division unterstützt. Du möchtest den Testgetriebe-Entwicklung (TDD) Ansatz, insbesondere den Red-Green-Refactor-Zyklus, anwenden, um sicherzustellen, dass dein Code fehlerfrei und gut strukturiert ist.

a)

Schreibe einen Test für die Addition von zwei Zahlen. Der Test sollte fehlschlagen, da die Funktionalität zur Durchführung der Addition noch nicht implementiert ist. Verwende eine Programmiersprache deiner Wahl und formatiere den Test als Beispiel klar und verständlich.

Lösung:

Um den Red-Green-Refactor-Zyklus zu starten, müssen wir zunächst einen fehlgeschlagenen Test schreiben, der die Addition von zwei Zahlen überprüft. Nehmen wir an, wir verwenden Python und das unittest Modul, um den Test zu schreiben.

Hier ist ein Beispiel für einen solchen Test:

 import unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = add(2, 3)        self.assertEqual(result, 5)if __name__ == '__main__':    unittest.main()
  • import unittest: Importiert das unittest-Modul, das die Tools für das Testen bereitstellt.
  • class TestCalculator(unittest.TestCase): Definiert eine Testklasse, die von unittest.TestCase erbt.
  • def test_addition(self): Definiert eine Testmethode innerhalb der Testklasse, die die Addition zweier Zahlen überprüft.
  • result = add(2, 3): Ruft die noch zu implementierende add-Funktion auf.
  • self.assertEqual(result, 5): Überprüft, ob das Ergebnis der Addition korrekt ist.
  • if __name__ == '__main__': Stellt sicher, dass die Tests ausgeführt werden, wenn das Skript direkt ausgeführt wird.

Da die add-Funktion noch nicht implementiert ist, wird dieser Test fehlschlagen. Dies ist der beabsichtigte erste Schritt im TDD-Prozess. Sobald der Test fehlschlägt, können wir die add-Funktion implementieren und den Test erneut ausführen, um sicherzustellen, dass er besteht.

b)

Implementiere die minimale Funktionalität, um den Additionstest bestehen zu lassen. Zeige den Code, der nun den Test bestehen lässt. Beschreibe auch, welche Änderungen vorgenommen wurden, um diesen Schritt zu erreichen.

Lösung:

Nachdem wir den fehlgeschlagenen Test für die Addition geschrieben haben, ist der nächste Schritt im TDD-Zyklus die Implementierung der minimalen Funktionalität, um diesen Test bestehen zu lassen. Das bedeutet, wir müssen eine add-Funktion hinzufügen, die die Addition von zwei Zahlen durchführt.

Hier ist der Code, der die add-Funktion implementiert und den Test bestehen lässt:

 # Hauptcode des Taschenrechnersdef add(a, b):    return a + b# Testcodeimport unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = add(2, 3)        self.assertEqual(result, 5)if __name__ == '__main__':    unittest.main()
  • def add(a, b): Definiert eine Funktion, die zwei Zahlen addiert und das Ergebnis zurückgibt.
  • return a + b: Führt die Addition der beiden übergebenen Parameter durch und gibt das Ergebnis zurück.

Durch diese Änderungen wird der Test für die Addition jetzt erfolgreich bestehen. Hier ist eine Schritt-für-Schritt-Beschreibung der Änderungen:

  • Definition der add-Funktion: Wir haben eine neue Funktion add definiert, die zwei Argumente (a und b) annimmt und deren Addition zurückgibt.
  • Testausführung: Der bestehende Test ruft die add-Funktion mit den Argumenten 2 und 3 auf und überprüft, ob das Ergebnis 5 ist. Da die add-Funktion nun korrekt implementiert ist, wird der Test erfolgreich sein.

Damit haben wir den grünen Schritt im TDD-Zyklus erreicht, bei dem der Test besteht. Der nächste Schritt wäre das Refactoring, falls notwendig, um den Code zu verbessern, ohne dass die Funktionalität verändert wird.

c)

Überarbeite den Code (Refactor), um ihn sauberer und effizienter zu gestalten, ohne dass die Funktionalität sich ändert. Stelle sicher, dass alle Tests, einschließlich des Tests für die Addition, weiterhin bestehen. Beschreibe die vorgenommenen Refactoring-Schritte.

Lösung:

Das Ziel des Refactor-Schritts im TDD-Zyklus ist es, den Code sauberer und effizienter zu gestalten, ohne die Funktionalität zu verändern. Wir werden den bestehenden Code überprüfen und gegebenenfalls Verbesserungen vornehmen, um die Lesbarkeit und Wartbarkeit zu erhöhen.

Hier ist der überarbeitete Code:

 # Rechenoperationsmodulclass Calculator:    @staticmethod    def add(a, b):        return a + b# Testcodeimport unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = Calculator.add(2, 3)        self.assertEqual(result, 5)if __name__ == '__main__':    unittest.main()
  • Umstrukturierung in eine Klasse: Wir haben die add-Funktion in eine Klasse namens Calculator verschoben. Dies verbessert die Struktur und Skalierbarkeit des Codes, insbesondere wenn wir in Zukunft weitere Operationen hinzufügen möchten.
  • Verwendung einer statischen Methode: Wir haben die add-Funktion als statische Methode innerhalb der Calculator-Klasse definiert. Dadurch können wir die Methode aufrufen, ohne eine Instanz der Klasse zu erstellen, was für eine Taschenrechner-Anwendung sinnvoll ist.
  • Verbesserung der Lesbarkeit: Indem wir den Funktionsaufruf zu Calculator.add ändern, wird klarer, dass wir eine Methode der Calculator-Klasse aufrufen. Dies verbessert die Lesbarkeit und Verständlichkeit des Codes.

Hier ist eine Schritt-für-Schritt-Beschreibung der vorgenommenen Refactoring-Schritte:

  • Erstellung der Calculator-Klasse: Wir haben eine neue Klasse Calculator erstellt, die die add-Methode enthält.
  • Verschieben der add-Methode: Wir haben die add-Methode in die Calculator-Klasse verschoben und als statische Methode definiert, indem wir @staticmethod verwendet haben.
  • Anpassung des Tests: Wir haben den Test so angepasst, dass er die statische Methode Calculator.add aufruft.
  • Überprüfung der Funktionalität: Wir haben sichergestellt, dass alle Tests, einschließlich des Tests für die Addition, weiterhin bestehen.

Mit diesen Refactoring-Schritten haben wir den Code besser organisiert und die Grundlage für zukünftige Erweiterungen gelegt, ohne die ursprüngliche Funktionalität zu verändern.

d)

Erstelle Tests und implementiere die Funktionalität für die verbleibenden grundlegenden mathematischen Operationen (Subtraktion, Multiplikation, und Division) gemäß dem TDD-Ansatz. Stelle sicher, dass alle Tests am Ende bestehen und erkläre kurz, wie der Red-Green-Refactor-Zyklus für jede Operation angewendet wurde.

Lösung:

Um die verbleibenden grundlegenden mathematischen Operationen (Subtraktion, Multiplikation und Division) gemäß dem Testgetriebe-Entwicklungsansatz (TDD) zu implementieren, folgen wir dem Red-Green-Refactor-Zyklus für jede Operation. Zuerst schreiben wir die fehlgeschlagenen Tests, implementieren dann die minimale Funktionalität, um diese Tests zu bestehen, und schließlich refaktorieren wir den Code.

Hier sind die einzelnen Schritte:

1. Tests für alle Operationen schreiben (Red)

 import unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = Calculator.add(2, 3)        self.assertEqual(result, 5)    def test_subtraction(self):        result = Calculator.subtract(5, 3)        self.assertEqual(result, 2)    def test_multiplication(self):        result = Calculator.multiply(2, 3)        self.assertEqual(result, 6)    def test_division(self):        result = Calculator.divide(6, 3)        self.assertEqual(result, 2)if __name__ == '__main__':    unittest.main()

Diese Tests werden fehlschlagen, da die Methoden subtract, multiply und divide noch nicht implementiert sind.

2. Minimalen Code implementieren, um die Tests bestehen zu lassen (Green)

 # Rechenoperationsmodulclass Calculator:    @staticmethod    def add(a, b):        return a + b    @staticmethod    def subtract(a, b):        return a - b    @staticmethod    def multiply(a, b):        return a * b    @staticmethod    def divide(a, b):        if b == 0:            raise ValueError('Division durch Null ist nicht erlaubt')        return a / b# Testcodeimport unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = Calculator.add(2, 3)        self.assertEqual(result, 5)    def test_subtraction(self):        result = Calculator.subtract(5, 3)        self.assertEqual(result, 2)    def test_multiplication(self):        result = Calculator.multiply(2, 3)        self.assertEqual(result, 6)    def test_division(self):        result = Calculator.divide(6, 3)        self.assertEqual(result, 2)    def test_divide_by_zero(self):        with self.assertRaises(ValueError):            Calculator.divide(6, 0)if __name__ == '__main__':    unittest.main()
  • subtract: Subtrahiert b von a.
  • multiply: Multipliziert a und b.
  • divide: Dividiert a durch b und gibt das Ergebnis zurück. Wenn b = 0 ist, wird ein ValueError ausgelöst.

3. Refactoring (Refactor)

Da unser Code bereits sauber organisiert ist, ist kein weiteres Refactoring notwendig. Alle Tests sollten jetzt bestehen:

 class Calculator:    @staticmethod    def add(a, b):        return a + b    @staticmethod    def subtract(a, b):        return a - b    @staticmethod    def multiply(a, b):        return a * b    @staticmethod    def divide(a, b):        if b == 0:            raise ValueError('Division durch Null ist nicht erlaubt')        return a / bimport unittestclass TestCalculator(unittest.TestCase):    def test_addition(self):        result = Calculator.add(2, 3)        self.assertEqual(result, 5)    def test_subtraction(self):        result = Calculator.subtract(5, 3)        self.assertEqual(result, 2)    def test_multiplication(self):        result = Calculator.multiply(2, 3)        self.assertEqual(result, 6)    def test_division(self):        result = Calculator.divide(6, 3)        self.assertEqual(result, 2)    def test_divide_by_zero(self):        with self.assertRaises(ValueError):            Calculator.divide(6, 0)if __name__ == '__main__':    unittest.main()

Zusammengefasst wurde der Red-Green-Refactor-Zyklus wie folgt angewendet:

  • Red: Fehlende Tests für Subtraktion, Multiplikation und Division wurden geschrieben.
  • Green: Minimale Implementierung der subtract, multiply und divide Methoden, um die Tests bestehen zu lassen.
  • Refactor: Überprüfung und Sicherstellung, dass der Code ordentlich strukturiert ist. Alle Tests bestehen.

Unser Grundlagen-Taschenrechner ist nun vollständig implementiert mit einer sauberen und gut strukturierten Codebasis.

Aufgabe 3)

Stell Dir ein Schulsystem vor, das verschiedene Arten von Benachrichtigungen an Schüler und Lehrer sendet, Funktionen zur Erstellung von Nutzern bietet und sicherstellt, dass das System nur eine einzige Instanz hat, um unkontrollierten Zugriff zu verhindern. Benachrichtigungen sind ein zentrales Element dieses Systems.

a)

a) Implementiere das Singleton-Muster für das Schulsystem. Beachte dabei, dass nur eine einzige Instanz des Schulsystems existiert und global verfügbar ist. Erkläre in eigenen Worten, warum das Singleton-Muster in diesem Fall sinnvoll ist.

class SchoolSystem:    _instance = None    def __new__(cls, *args, **kwargs):        if cls._instance is None:            cls._instance = super(SchoolSystem, cls).__new__(cls, *args, **kwargs)        return cls._instance    def __init__(self):        self._observers = []    def attach(self, observer):        if observer not in self._observers:            self._observers.append(observer)    def detach(self, observer):        try:            self._observers.remove(observer)        except ValueError:            pass    def notify(self):        for observer in self._observers:            observer.update(self)

Lösung:

Lösung zur Teilaufgabe a)

Das Singleton-Muster stellt sicher, dass nur eine einzige Instanz einer Klasse existiert und diese global verfügbar ist. Dies wird erreicht, indem der Konstruktor der Klasse modifiziert wird, sodass er nur eine Instanz erstellt und dieselbe Instanz bei jedem weiteren Aufruf zurückgibt. Im Kontext eines Schulsystems ist das Singleton-Muster sinnvoll, da es sicherstellt, dass alle Benachrichtigungen und Benutzererstellungen über eine zentrale Instanz verwaltet werden. Dies verhindert unkontrollierten Zugriff und ermöglicht eine kohärente Verwaltung des Systems.

 class SchoolSystem:      _instance = None      def __new__(cls, *args, **kwargs):          if cls._instance is None:              cls._instance = super(SchoolSystem, cls).__new__(cls, *args, **kwargs)          return cls._instance      def __init__(self):          self._observers = []      def attach(self, observer):          if observer not in self._observers:              self._observers.append(observer)      def detach(self, observer):          try:              self._observers.remove(observer)          except ValueError:              pass      def notify(self):          for observer in self._observers:              observer.update(self)  

Erklärung:

  • Die Klasse SchoolSystem besitzt ein Attribut _instance, das anfänglich auf None gesetzt ist.
  • Die Methode __new__ stellt sicher, dass nur eine Instanz der Klasse erstellt wird. Wenn _instance noch None ist, wird eine neue Instanz erzeugt. Andernfalls wird die existierende Instanz zurückgegeben.
  • Die Methode __init__ initialisiert die Liste der Beobachter (Observers), die benachrichtigt werden sollen.
  • Mit der Methode attach können neue Beobachter zur Liste hinzugefügt werden, sofern sie noch nicht vorhanden sind.
  • Mit der Methode detach können Beobachter aus der Liste entfernt werden, falls sie existieren. Falls der Beobachter nicht in der Liste ist, passiert nichts.
  • Die Methode notify ruft die update-Methode aller Beobachter auf und benachrichtigt sie über Änderungen.

b)

b) Benutzer in diesem Schulsystem können Schüler oder Lehrer sein. Diese Benutzer müssen auf Benachrichtigungen reagieren. Implementiere das Observer-Muster, um sicherzustellen, dass alle registrierten Benutzer benachrichtigt werden, wenn das Schulsystem eine Benachrichtigung sendet.

class User:    def update(self, subject):        passclass Student(User):    def update(self, subject):        print('Student received a notification')class Teacher(User):    def update(self, subject):        print('Teacher received a notification')# Beispielhafte Nutzungschool_system = SchoolSystem()student = Student()teacher = Teacher()school_system.attach(student)school_system.attach(teacher)school_system.notify()

Lösung:

Lösung zur Teilaufgabe b)

Das Observer-Muster ermöglicht es, dass ein Objekt (das Subjekt) eine Liste von Beobachtern führt und diese benachrichtigt, wenn sich der Zustand des Subjekts ändert. Im Kontext des Schulsystems stellt das Observer-Muster sicher, dass alle registrierten Benutzer (Schüler und Lehrer) benachrichtigt werden, wenn das System eine Benachrichtigung sendet.

 class User:    def update(self, subject):        passclass Student(User):    def update(self, subject):        print('Student received a notification')class Teacher(User):    def update(self, subject):        print('Teacher received a notification')# Beispielhafte Nutzungschool_system = SchoolSystem()student = Student()teacher = Teacher()school_system.attach(student)school_system.attach(teacher)school_system.notify() 

Erklärung:

  • Die Klasse User dient als Basisklasse für alle Benutzer im Schulsystem. Sie definiert die Methode update, die von allen Unterklassen überschrieben werden kann.
  • Die Klasse Student ist eine Unterklasse von User. Sie überschreibt die Methode update und gibt eine Meldung aus, wenn sie eine Benachrichtigung erhält.
  • Die Klasse Teacher ist ebenfalls eine Unterklasse von User. Auch sie überschreibt die Methode update und gibt eine Meldung aus, wenn sie eine Benachrichtigung erhält.
  • Ein Objekt der Klasse SchoolSystem wird erstellt.
  • Ein Student und ein Teacher werden als Beobachter registriert, indem sie an das Schulsystem angehängt werden.
  • Die Methode notify des Schulsystems wird aufgerufen, um alle registrierten Benutzer zu benachrichtigen. Dies führt dazu, dass die update-Methode der registrierten Benutzer aufgerufen wird und sie die Benachrichtigung erhalten.

c)

c) Implementiere das Factory-Muster für die Erstellung von Nutzern in diesem Schulsystem. Diese Factory wird genutzt, um entweder Schüler oder Lehrer zu instanziieren, ohne die speziellen Klassen explizit anzugeben. Zeige zudem ein Beispiel, wie man die Factory nutzt, um eine Menge von Schülern und Lehrern zu erstellen.

class UserFactory:    @staticmethod    def create_user(user_type):        if user_type == 'student':            return Student()        elif user_type == 'teacher':            return Teacher()        else:            raise ValueError('Unknown user type.')# Beispielhafte Nutzungusers = []for i in range(5):    if i % 2 == 0:        users.append(UserFactory.create_user('student'))    else:        users.append(UserFactory.create_user('teacher'))# Teste ob die Nutzer korrekt erstellt wurdenfor user in users:    user.update(school_system)

Lösung:

Lösung zur Teilaufgabe c)

Das Factory-Muster verschafft eine Möglichkeit, Objekte zu erstellen, ohne die spezifischen Klassen explizit anzugeben. Im Kontext des Schulsystems ermöglicht die Factory die Instanziierung von Schülern oder Lehrern basierend auf einem Typ-Parameter. Dies vereinfacht die Erstellung neuer Benutzer und macht den Code flexibler und erweiterbarer.

 class UserFactory:    @staticmethod    def create_user(user_type):        if user_type == 'student':            return Student()        elif user_type == 'teacher':            return Teacher()        else:            raise ValueError('Unknown user type.')# Beispielhafte Nutzungusers = []for i in range(5):    if i % 2 == 0:        users.append(UserFactory.create_user('student'))    else:        users.append(UserFactory.create_user('teacher'))# Teste ob die Nutzer korrekt erstellt wurdenschool_system = SchoolSystem()for user in users:    school_system.attach(user)school_system.notify() 

Erklärung:

  • Die UserFactory Klasse besitzt eine statische Methode create_user, die einen Benutzertyp als Parameter akzeptiert und basierend darauf entweder einen Schüler oder einen Lehrer erstellt.
  • Im Beispiel werden fünf Benutzer erstellt und einer Liste users hinzugefügt. Jeder ungerade Index erzeugt einen Lehrer und jeder gerade Index erzeugt einen Schüler.
  • Das school_system wird initialisiert und die erstellten Benutzer werden ihm als Beobachter hinzugefügt.
  • Durch den Aufruf der Methode notify des school_system wird überprüft, ob die Benutzer korrekt erstellt und benachrichtigt wurden.

Aufgabe 4)

In einem agilen Softwareentwicklungsprojekt stehen Sprint Planning und Retrospektiven im Mittelpunkt der iterativen und inkrementellen Arbeitsweise. Das Team setzt sich zusammen aus dem Product Owner, Scrum Master und den Entwicklungsteammitgliedern. Während des Sprint Plannings werden die Ziele für den bevorstehenden Sprint festgelegt und Aufgaben in kleinere, umsetzbare Einheiten aufgeteilt. Dies erfolgt unter Berücksichtigung der 'Definition of Done' (DoD). Nach Abschluss eines Sprints findet eine Retrospektive statt, um die Zusammenarbeit und Prozesse des vergangenen Sprints zu analysieren und Verbesserungsmöglichkeiten zu identifizieren.

a)

Beschreibe den Ablauf eines Sprint Planning Meetings in einer agilen Softwareentwicklungsumgebung. In Deiner Beschreibung sollten die folgenden Punkte enthalten sein:

  • Die Rolle des Product Owners, Scrum Masters und des Teams
  • Die Festlegung der 'Definition of Done' und deren Bedeutung
  • Der Prozess der Aufteilung der Aufgaben in kleinere Einheiten
  • Wie Burndown Charts in diesem Kontext verwendet werden

Lösung:

Beschreibung des Ablaufs eines Sprint Planning Meetings in einer agilen Softwareentwicklungsumgebung:Ein Sprint Planning Meeting ist ein wesentlicher Bestandteil des agilen Entwicklungsprozesses. Im Folgenden wird der Ablauf detailliert beschrieben, wobei die angegebenen Punkte berücksichtigt werden:

  • Die Rolle des Product Owners, Scrum Masters und des Teams:• Der Product Owner ist dafür verantwortlich, die Anforderungen und Prioritäten für den kommenden Sprint zu kommunizieren. Er präsentiert das Product Backlog und diskutiert die wichtigsten User Stories mit dem Team.• Der Scrum Master moderiert das Meeting, sorgt für einen reibungslosen Ablauf und unterstützt das Team bei der Vermeidung von Hindernissen. Er stellt sicher, dass alle Beteiligten die agile Methodik einhalten.• Das Entwicklungsteam analysiert die vom Product Owner präsentierten User Stories, gibt Einschätzungen ab und plant, wie die Arbeit im kommenden Sprint erledigt werden kann. Das Team entscheidet, welche Aufgaben realistisch bewältigt werden können.
  • Die Festlegung der 'Definition of Done' und deren Bedeutung:Die 'Definition of Done' (DoD) ist ein zentraler Aspekt eines jeden Sprint Plannings. Sie legt fest, welche Kriterien erfüllt sein müssen, damit eine User Story als 'fertig' betrachtet wird. Dies kann beispielsweise beinhalten, dass der Code getestet, dokumentiert und in das Hauptrepository integriert ist. Die DoD sorgt für Klarheit und ein gemeinsames Verständnis im Team darüber, was von einer abgeschlossenen Aufgabe erwartet wird.
  • Der Prozess der Aufteilung der Aufgaben in kleinere Einheiten:Nach der Präsentation und Diskussion der User Stories zerlegt das Entwicklungsteam diese in kleinere, handhabbare Aufgaben. Dies wird als Task Breakdown bezeichnet. Jede Aufgabe wird detailliert beschrieben und mit einem geschätzten Aufwand versehen. Dadurch wird sichergestellt, dass die Arbeitsschritte klar definiert und besser planbar sind.
  • Wie Burndown Charts in diesem Kontext verwendet werden:Burndown Charts sind ein wichtiges Werkzeug im agilen Projektmanagement. Sie visualisieren den Fortschritt des Teams während des Sprints. Ein Burndown Chart zeigt die verbleibende Arbeit im Vergleich zur verstrichenen Zeit. Es hilft dem Team, den Überblick zu behalten und rechtzeitig zu erkennen, ob die geplanten Aufgaben im Sprint abgeschlossen werden können oder ob Anpassungen notwendig sind.

Zusammengefasst bietet das Sprint Planning Meeting den Rahmen für eine strukturierte und effiziente Planung, bei der alle Teammitglieder aktiv einbezogen werden und die Grundlage für einen erfolgreichen Sprint legen.

b)

Angenommen, ein Team hat während der Retrospektive folgende Punkte identifiziert:

  • Unzureichende Kommunikation innerhalb des Teams
  • Unklare Aufgabenverteilung
  • Unzureichende Tests vor der Abgabe
Formuliere drei konkrete Aktionspunkte, die das Team im nächsten Sprint umsetzen sollte, um diese Probleme zu adressieren. Begründe die Wahl jedes Aktionspunkts.

Lösung:

Drei konkrete Aktionspunkte zur Adressierung der identifizierten Probleme:Während der Retrospektive wurden vom Team folgende Probleme festgestellt: unzureichende Kommunikation, unklare Aufgabenverteilung und unzureichende Tests vor der Abgabe. Hier sind konkrete Aktionspunkte, um diese Probleme im nächsten Sprint zu lösen, inklusive Begründung:

  • Implementierung regelmäßiger Stand-Up Meetings:Begründung: Durch tägliche kurze Stand-Up Meetings stellen die Teammitglieder sicher, dass alle über den aktuellen Stand der jeweiligen Aufgaben informiert sind. Dies fördert die Transparenz und verbessert die Kommunikation innerhalb des Teams. Jeder teilt seine Fortschritte, Hindernisse und Pläne, was zur proaktiven Problemlösung beiträgt.
  • Einführung von klar zugeordneten Aufgaben mit Verantwortlichkeiten:Begründung: Um die unklare Aufgabenverteilung zu adressieren, sollte das Team jeder Aufgabe einen oder mehrere Verantwortliche zuordnen. Dies schafft Klarheit darüber, wer welche Aufgaben übernimmt und mindert Verwirrung. Verantwortlichkeiten können in einem Aufgabenboard, wie etwa einem Kanban-Board, visualisiert werden.
  • Definition und Implementierung von Test-Standards als Teil der 'Definition of Done':Begründung: Unzureichende Tests vor der Abgabe können durch detaillierte Test-Standards in der 'Definition of Done' (DoD) behoben werden. Jeder abgeschlossene Task muss die definierten Tests bestehen, bevor er als 'fertig' betrachtet wird. Dies verbessert die Codequalität und reduziert die Anzahl später entdeckter Fehler. Automatisierte Tests können zusätzlich integriert werden, um eine kontinuierliche Testabdeckung zu gewährleisten.

Durch die Umsetzung dieser Aktionspunkte sollte das Team in der Lage sein, die identifizierten Probleme effizient zu lösen und die Qualität sowie die Zusammenarbeit im nächsten Sprint zu verbessern.

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