Advanced Design and Programming - Exam.pdf

Advanced Design and Programming - Exam
Advanced Design and Programming - Exam Aufgabe 1) Gegeben ist eine Anwendung, die verschiedene Arten von Datenbankverbindungen verwaltet. Es stehen zwei Typen von Datenbanken zur Verfügung: MySQL und PostgreSQL . Du sollst Erzeugungsmuster anwenden, um die Instanziierung und Verwaltung dieser Verbindungen zu optimieren. a) Implementiere eine Singleton-Klasse DatabaseConfig , die sicherstellt, dass...

© StudySmarter 2024, all rights reserved.

Advanced Design and Programming - Exam

Aufgabe 1)

Gegeben ist eine Anwendung, die verschiedene Arten von Datenbankverbindungen verwaltet. Es stehen zwei Typen von Datenbanken zur Verfügung: MySQL und PostgreSQL. Du sollst Erzeugungsmuster anwenden, um die Instanziierung und Verwaltung dieser Verbindungen zu optimieren.

a)

Implementiere eine Singleton-Klasse DatabaseConfig, die sicherstellt, dass die Konfiguration für die Datenbankverbindungen nur einmalig instanziiert wird. Diese Klasse soll Informationen wie Hostname, Port und Datenbankname enthalten. Schreibe den Code in Java oder Python.

class DatabaseConfig:    __instance = None    def __new__(cls):        if cls.__instance is None:            cls.__instance = super(DatabaseConfig, cls).__new__(cls)            # Initialisiere hier die Konfigurationswerte            cls.__instance.hostname = 'localhost'            cls.__instance.port = 5432            cls.__instance.dbname = 'example_db'        return cls.__instance# Beispielnutzungconfig1 = DatabaseConfig()config2 = DatabaseConfig()print(config1 is config2)  # Sollte True ausgeben

Lösung:

Um sicherzustellen, dass die Konfiguration für die Datenbankverbindungen nur einmalig instanziiert wird, implementieren wir das Singleton-Muster in einer DatabaseConfig-Klasse in Python. Hier ist die Lösung:

class DatabaseConfig:    __instance = None    def __new__(cls):        if cls.__instance is None:            cls.__instance = super(DatabaseConfig, cls).__new__(cls)            # Initialisiere hier die Konfigurationswerte            cls.__instance.hostname = 'localhost'            cls.__instance.port = 5432            cls.__instance.dbname = 'example_db'        return cls.__instance# Beispielnutzungconfig1 = DatabaseConfig()config2 = DatabaseConfig()print(config1 is config2)  # Sollte True ausgeben
  • Die Klasse DatabaseConfig hat ein statisches Attribut __instance, das auf None gesetzt ist.
  • Die Methode __new__(cls) wird verwendet, um die Instanz zu erstellen. Wenn __instance None ist, wird eine neue Instanz erstellt und die Konfigurationswerte werden initialisiert.
  • Wenn __instance bereits gesetzt ist, wird die gleiche Instanz bei weiteren Aufrufen von __new__ zurückgegeben.
  • Am Ende wird bestätigt, dass beide Konfigurationsobjekte config1 und config2 tatsächlich die gleiche Instanz sind.

b)

Erstelle eine Factory Method namens DatabaseFactory, die basierend auf angegebenen Parametern MySQL- oder PostgreSQL-Verbindungen erzeugt. Implementiere dies in Java oder Python und zeige Beispiele für die Erzeugung jeder Verbindung.

class DatabaseFactory:    @staticmethod    def create_connection(db_type):        if db_type == 'MySQL':            return MySQLConnection()        elif db_type == 'PostgreSQL':            return PostgreSQLConnection()        else:            raise ValueError(f'Unbekannter Datenbanktyp: {db_type}')# Beispielnutzungmysql_conn = DatabaseFactory.create_connection('MySQL')postgres_conn = DatabaseFactory.create_connection('PostgreSQL')

Lösung:

Um eine Factory Method zu implementieren, die basierend auf angegebenen Parametern MySQL- oder PostgreSQL-Verbindungen erzeugt, kannst Du die folgende Python-Implementierung verwenden:

class MySQLConnection:    def connect(self):        print('Verbindung zu MySQL-Datenbank hergestellt.')class PostgreSQLConnection:    def connect(self):        print('Verbindung zu PostgreSQL-Datenbank hergestellt.')class DatabaseFactory:    @staticmethod    def create_connection(db_type):        if db_type == 'MySQL':            return MySQLConnection()        elif db_type == 'PostgreSQL':            return PostgreSQLConnection()        else:            raise ValueError(f'Unbekannter Datenbanktyp: {db_type}')# Beispielnutzungmysql_conn = DatabaseFactory.create_connection('MySQL')mysql_conn.connect()  # Ausgabe: Verbindung zu MySQL-Datenbank hergestellt.postgres_conn = DatabaseFactory.create_connection('PostgreSQL')postgres_conn.connect()  # Ausgabe: Verbindung zu PostgreSQL-Datenbank hergestellt.
  • Es wurden zwei Klassen MySQLConnection und PostgreSQLConnection erstellt, die jeweils die Methode connect() haben, um eine Verbindung herzustellen.
  • Die DatabaseFactory Klasse hat eine statische Methode create_connection(db_type), die je nach Parameter db_type entweder eine MySQL- oder PostgreSQL-Verbindung zurückgibt.
  • Eine Fehlermeldung wird ausgelöst, wenn ein unbekannter Datenbanktyp angegeben wird.
  • Zum Schluss zeigen die Beispiele, wie Verbindungen zu beiden Datenbanktypen erstellt und genutzt werden können.

c)

Erweitere die DatabaseFactory um eine Abstract Factory AbstractDatabaseFactory, die eine Schnittstelle zur Erstellung verwandter Objekte bieten soll, ohne deren konkrete Klassen zu spezifizieren. Implementiere dies in Java oder Python sowie eine konkrete Factory für MySQL und PostgreSQL.

class AbstractDatabaseFactory:    def create_connection(self):        raise NotImplementedErrorclass MySQLFactory(AbstractDatabaseFactory):    def create_connection(self):        return MySQLConnection()class PostgreSQLFactory(AbstractDatabaseFactory):    def create_connection(self):        return PostgreSQLConnection()# Beispielnutzungmysql_factory = MySQLFactory()mysql_conn = mysql_factory.create_connection()postgres_factory = PostgreSQLFactory()postgres_conn = postgres_factory.create_connection()

Lösung:

Um die DatabaseFactory um eine Abstract Factory namens AbstractDatabaseFactory zu erweitern, kannst Du die folgende Implementierung in Python verwenden:

class MySQLConnection:    def connect(self):        print('Verbindung zu MySQL-Datenbank hergestellt.')class PostgreSQLConnection:    def connect(self):        print('Verbindung zu PostgreSQL-Datenbank hergestellt.')class AbstractDatabaseFactory:    def create_connection(self):        raise NotImplementedErrorclass MySQLFactory(AbstractDatabaseFactory):    def create_connection(self):        return MySQLConnection()class PostgreSQLFactory(AbstractDatabaseFactory):    def create_connection(self):        return PostgreSQLConnection()# Beispielnutzungmysql_factory = MySQLFactory()mysql_conn = mysql_factory.create_connection()mysql_conn.connect()  # Ausgabe: Verbindung zu MySQL-Datenbank hergestellt.postgres_factory = PostgreSQLFactory()postgres_conn = postgres_factory.create_connection()postgres_conn.connect()  # Ausgabe: Verbindung zu PostgreSQL-Datenbank hergestellt.
  • Die Klasse MySQLConnection und die Klasse PostgreSQLConnection haben jeweils eine connect()-Methode, um eine Verbindung herzustellen.
  • Die AbstractDatabaseFactory-Klasse definiert eine Schnittstelle mit der Methode create_connection(), die eine Ausnahme auslöst, wenn sie nicht überschrieben wird.
  • Die MySQLFactory- und die PostgreSQLFactory-Klassen sind konkrete Implementierungen der AbstractDatabaseFactory-Schnittstelle, die jeweils eine Verbindung für ihre spezifische Datenbank zurückgeben.
  • Zum Schluss zeigen die Beispielverwendungen, wie die konkreten Fabriken MySQLFactory und PostgreSQLFactory verwendet werden, um Verbindungen zu erstellen und diese zu nutzen.

d)

Diskutiere die Vor- und Nachteile der verwendeten Erzeugungsmuster in diesem Kontext. Gehe dabei auf Aspekte wie Modularität, Wartbarkeit und Komplexität des Codes ein.

  • Vorteile: Die Erzeugungsmuster erhöhen die Modularität, indem sie die Instanziierung von Objekten von ihrer Verwendung trennen. Dadurch wird der Code flexibler und die Klassen sind leichter austauschbar.
  • Ein weiterer Vorteil ist die Erhöhung der Wartbarkeit. Änderungen an der Instanziierungslogik müssen nur an einer Stelle erfolgen und nicht im gesamten Code verteilt werden.
  • Nachteile: Einer der Nachteile ist die erhöhte Komplexität des Codes, insbesondere für Entwickler, die nicht vertraut mit Design Patterns sind. Die Instanziierung von Objekten ist nicht mehr offensichtlich und kann das Verständnis des Codes erschweren.
  • Ein zweiter Nachteil ist, dass die Muster bei unsachgemäßer Verwendung zu schwer verständlichem und schwer nachvollziehbarem Code führen können. Diese Komplexität kann sich insbesondere in großen Projekten als hinderlich erweisen.

Lösung:

Die Anwendung von Erzeugungsmustern in der Verwaltung von Datenbankverbindungen bringt sowohl Vor- als auch Nachteile mit sich. Hier sind einige wichtige Punkte, die dabei berücksichtigt werden sollten:

  • Vorteile:
  • Erhöhte Modularität: Durch die Trennung der Instanziierung von Objekten von ihrer Verwendung wird der Code modularer. Die Klassen für MySQL- und PostgreSQL-Verbindungen sind leichter austauschbar, ohne dass Änderungen im gesamten Code vorgenommen werden müssen.
  • Verbesserte Wartbarkeit: Änderungen an der Instanziierungslogik müssen nur an einer zentralen Stelle durchgeführt werden. Dies reduziert den Aufwand für Anpassungen und verringert die Wahrscheinlichkeit von Fehlern, die durch redundante Änderungen entstehen könnten.
  • Flexibilität: Die Verwendung von Fabrikmethoden und abstrakten Fabriken ermöglicht es, neue Datenbanktypen hinzuzufügen, ohne bestehenden Code zu ändern. Dies erhöht die Flexibilität und Skalierbarkeit der Anwendung.
  • Nachteile:
  • Erhöhte Komplexität: Die Einführung von Erzeugungsmustern führt zu einer höheren Komplexität des Codes. Dies kann Entwickler, die nicht mit diesen Design Patterns vertraut sind, vor Herausforderungen stellen. Die Instanziierung von Objekten wird weniger offensichtlich, was das Verständnis des Codes erschwert.
  • Potenziale für Missbrauch: Bei unsachgemäßer Verwendung können diese Muster zu schwer verständlichem und schwer nachvollziehbarem Code führen. Die Komplexität kann sich insbesondere in großen Projekten als hinderlich erweisen und die Wartbarkeit tatsächlich beeinträchtigen, wenn sie nicht richtig implementiert wird.
  • Zusätzlicher Overhead: Das Implementieren und Verwalten dieser Muster kann zusätzlichen Overhead verursachen, sowohl in Bezug auf Entwicklungszeit als auch auf Ressourcen.

Zusammengefasst bieten Erzeugungsmuster in der Verwaltung von Datenbankverbindungen erhebliche Vorteile in Bezug auf Modularität und Wartbarkeit, bringen jedoch auch eine erhöhte Komplexität und potenzielle Herausforderungen mit sich. Ein sorgfältiges Abwägen dieser Vor- und Nachteile ist entscheidend, um die richtige Entscheidung für das jeweilige Projekt zu treffen.

Aufgabe 2)

Observer-Muster: Ein Verhaltensmuster, bei dem ein Objekt (Subject) eine Liste von abhängigen Objekten (Observer) verwaltet und diese über Zustandsänderungen benachrichtigt.

  • Subject: Verwaltet Observer und benachrichtigt sie bei Zustandsänderungen.
  • Observer: Wird über Zustandsänderungen im Subject informiert und reagiert darauf.
  • Methoden:
    attach(), detach(), notify()
  • Schnittstellen:
    Subject, Observer
  • Vorteile: Entkopplung von Subject und Observer, einfache Erweiterbarkeit.
  • Nachteile: Komplexität kann bei vielen Observern steigen, Performance-Overhead möglich.

a)

Implementiere das Observer-Muster in Java. Erstelle eine Klasse

Subject
mit Methoden
attach(), detach(),notify()
und eine Schnittstelle
Observer
mit einer Methode
update()
. Implementiere zwei konkrete Observer-Klassen, die unterschiedliche Reaktionen auf Zustandsänderungen zeigen und zeige anhand eines Beispiels die Interaktion zwischen Subject und Observer.

Lösung:

Um das Observer-Muster in Java zu implementieren, werden wir zuerst die Schnittstelle Observer und die Klasse Subject definieren. Danach erstellen wir konkrete Observer-Klassen, die unterschiedliche Reaktionen auf Zustandsänderungen zeigen. Schließlich demonstrieren wir die Interaktion zwischen Subject und Observer. Lass uns Schritt für Schritt vorgehen.

b)

Angenommen, das

Subject
benachrichtigt 100
Observer
, nachdem sein Zustand geändert wurde. Berechne den Zeitaufwand, wenn jede Benachrichtigung 1 ms dauert. Wie würde sich die Performance verändern, wenn die Anzahl der Observer auf 1.000 erhöht wird? Diskutiere die potenziellen Performance-Probleme und wie sie mithilfe von Designänderungen oder Optimierungen gelöst werden könnten.

Lösung:

Lass uns die Aufgabenstellung Schritt für Schritt durchgehen und die benötigten Berechnungen sowie mögliche Performance-Probleme und Optimierungen diskutieren.

Aufgabe 3)

Kontext: In einem Softwareprojekt wird ein Bibliothekssystem entwickelt, in dem Bücher, CDs und Zeitschriften verwaltet werden. Dein Team entschied sich für einen objektorientierten Ansatz. Unter Verwendung fortgeschrittener OOP-Konzepte sollen verschiedene Anforderungen implementiert und optimiert werden.

a)

  • Implementiere eine Klassenhierarchie unter Verwendung von Vererbung. Definiere eine abstrakte Klasse Medien, die allgemeine Eigenschaften und Methoden wie titel und leihen() enthält. Erstelle dann spezifische Klassen Buch, CD und Zeitschrift, die von der Klasse Medien erben und spezifische Attribute bzw. Funktionen hinzufügen.
  • Implementiere später ein Interface DigitaleMedien mit einer Methode download(). Alle digitalen Medien wie E-Books und digitale Zeitschriften sollen dieses Interface implementieren. Zeige in diesem Zusammenhang den Unterschied und die Komplementarität zwischen der Verwendung von abstrakten Klassen und Interfaces.

Lösung:

Kontext: In einem Softwareprojekt wird ein Bibliothekssystem entwickelt, in dem Bücher, CDs und Zeitschriften verwaltet werden. Dein Team entschied sich für einen objektorientierten Ansatz. Unter Verwendung fortgeschrittener OOP-Konzepte sollen verschiedene Anforderungen implementiert und optimiert werden.

  • Unteraufgabe: Implementiere eine Klassenhierarchie unter Verwendung von Vererbung. Definiere eine abstrakte Klasse Medien, die allgemeine Eigenschaften und Methoden wie titel und leihen() enthält. Erstelle dann spezifische Klassen Buch, CD und Zeitschrift, die von der Klasse Medien erben und spezifische Attribute bzw. Funktionen hinzufügen.
  • Implementiere später ein Interface DigitaleMedien mit einer Methode download(). Alle digitalen Medien wie E-Books und digitale Zeitschriften sollen dieses Interface implementieren. Zeige in diesem Zusammenhang den Unterschied und die Komplementarität zwischen der Verwendung von abstrakten Klassen und Interfaces.
Lösung: Implementiere die Klassenhierarchie mit Vererbung:
import abcdclass (Medien(abc.asbstractbase):    titel:str      @abc.abstractmethod    defleihen(self):        passclass Buch(Medien):    def __init__ (self) )
Implementiere das Interface DigitaleMedien:
import abcclass Medien(metaclass=abc.>@abc.abstractmethod 'defleue: ,
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