Skip to main content

Umgang mit bereichsübergreifenden Aspekten

Copilot Chat kann dir dabei helfen, Code zu vermeiden, der sich auf einen anderen Aspekt als den Hauptaspekt der Methode oder Funktion bezieht, in deren Kontext der Code verwendet wird.

Bereichsübergreifende Aspekte beziehen sich bei einem Programm auf mehrere Teile des Systems (z. B. Protokollierung, Sicherheit, Datenüberprüfung und Fehlerbehandlung). Sie können sich auf verschiedene Stellen in der Codebasis auswirken, was zu Problemen bei der Codeduplizierung und -wartung führt.

Copilot Chat kann beim Refactoring im Zusammenhang mit bereichsübergreifenden Aspekten helfen, indem die Implementierung von Methoden der aspektorientierten Programmierung (Aspect-Oriented Programming, AOP) oder die Verwendung von Decorator-Elementen und Middlewaremustern zum Zentralisieren dieser Aspekte auf modulare, nachvollziehbare Weise vorgeschlagen wird.

Beispielszenario

Stell dir vor, du verfügst über ein Python-Projekt, das mehrere Dienstdateien enthält, in denen eine Protokollierung erfolgt. Die protokollierten Informationen werden in den einzelnen Dienstdateien definiert. Wenn die Anwendung in Zukunft geändert oder erweitert wird, kann dieser Entwurf zu Inkonsistenzen beim Inhalt und Stil von Protokolleinträgen führen. Du kannst das Protokollierungsverhalten konsolidieren und zentralisieren, um zu vermeiden, dass sich diese Inkonsistenzen auf dein gesamtes Projekt auswirken.

Dies sind drei Dateien aus unserem Beispielprojekt: die Einstiegspunktdatei (main.py), die Konfigurationsdatei für Protokollnachrichten (logging_config.py) und eine der Dienstdateien (order_service.py). Die Beispieldienstdatei zeigt, wie Protokollinformationen zusammen mit der Geschäftslogik für einen bestimmten Teil der Anwendung definiert werden.

main.py

import logging
from logging_config import setup_logging
from payment_service import PaymentService
from order_service import OrderService
from shipping_service import ShippingService
from inventory_service import InventoryService
from notification_service import NotificationService

def main():
    setup_logging()
    payment_service = PaymentService()
    order_service = OrderService()
    shipping_service = ShippingService()
    inventory_service = InventoryService()
    notification_service = NotificationService()

    # Example usage
    payment_service.process_payment({"amount": 100, "currency": "USD"})
    order_service.place_order({"item": "Book", "quantity": 1})
    shipping_service.ship_order({"item": "Book", "quantity": 1})
    inventory_service.update_inventory("Book", -1)
    notification_service.send_notification("Order has been placed and shipped.")

if __name__ == "__main__":
    main()

logging_config.py

import logging

def setup_logging():
    logging.basicConfig(level=logging.INFO,
                  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

order_service.py

import logging

class OrderService:
    def __init__(self):
        self.logger = logging.getLogger(__name__)

    def place_order(self, order):
        self.logger.info("Entering place_order method.")
        try:
            # In here would be the business logic for placing an order
            # ...
            self.logger.info("Order placed successfully.")
        except Exception as e:
            self.logger.error(f"Error in place_order method: {e}")
            raise
        finally:
            self.logger.info("Exiting place_order method.")

Beispiel für Prompt

Öffne den Projektarbeitsbereich in VS Code oder Visual Studio, und gib den folgenden Prompt in der Ansicht Copilot Chat ein.

@workspace Refactor this project by using AOP to avoid logging that has cross-cutting concerns

Note

Der @workspace-Chatteilnehmer in VS Code und Visual Studio stellt Copilot Kontext für den gesamten Code in deinem Arbeitsbereich bereit. Du kannst @workspace verwenden, wenn Copilot die Struktur deines Projekts und die Art berücksichtigen soll, wie verschiedene Teile deines Codes interagieren.

Beispielantwort

Copilot antwortet wie folgt.

Note

Die folgende Antwort ist ein Beispiel. Da die Copilot Chat-Antworten nicht deterministisch sind, erhältst du möglicherweise eine andere Antwort als die hier beschriebene.

Für das Refactoring dieses Projekts mithilfe der aspektorientierten Programmierung (AOP) zum Behandeln der Protokollierung als bereichsübergreifenden Aspekt kannst du eine Bibliothek wie aspectlib verwenden. Hier erfährst du, wie du dies tun kannst:

  1. Installieren Sie aspectlib:

    pip install aspectlib

  2. Erstelle einen Aspekt für die Protokollierung in einer neuen Datei mit dem Namen logging_aspect.py:

    import aspectlib
    import logging
    
    @aspectlib.Aspect(bind=True)
    def log_method_call(cutpoint, *args, **kwargs):
        logger = logging.getLogger()
        method_name = cutpoint.__name__
        logger.info(f"Entering method: {method_name}")
        try:
            result = yield aspectlib.Proceed
            logger.info(f"Method {method_name} executed successfully.")
            return result
        except Exception as e:
            logger.error(f"Error in method {method_name}: {e}")
            raise
        finally:
            logger.info(f"Exiting method: {method_name}")
    
  3. Wende den Aspekt auf die Dienstmethoden in main.py an:

    import logging
    from logging_config import setup_logging
    from payment_service import PaymentService
    from order_service import OrderService
    from shipping_service import ShippingService
    from inventory_service import InventoryService
    from notification_service import NotificationService
    from logging_aspect import log_method_call
    import aspectlib
    
    def main():
        setup_logging()
    
        # Apply logging aspect to service methods
        aspectlib.weave(PaymentService.process_payment, log_method_call)
        aspectlib.weave(OrderService.place_order, log_method_call)
        aspectlib.weave(ShippingService.ship_order, log_method_call)
        aspectlib.weave(InventoryService.update_inventory, log_method_call)
        aspectlib.weave(NotificationService.send_notification, log_method_call)
    
        payment_service = PaymentService()
        order_service = OrderService()
        shipping_service = ShippingService()
        inventory_service = InventoryService()
        notification_service = NotificationService()
    
        # Example usage
        payment_service.process_payment({"amount": 100, "currency": "USD"})
        order_service.place_order({"item": "Book", "quantity": 1})
        shipping_service.ship_order({"item": "Book", "quantity": 1})
        inventory_service.update_inventory("Book", -1)
        notification_service.send_notification("Order has been placed and shipped.")
    
    if __name__ == "__main__":
        main()
    

Copilot weist dich dann an, den Protokollierungscode aus allen Dienstklassen zu entfernen, und stellt den Code für die überarbeiteten Dateien bereit. Dies ist beispielsweise die überarbeitete Version von order_service.py:

class OrderService:
    def place_order(self, order):
        # In here would be the business logic for placing an order
        # ...
        pass

In der umgestalteten Version des Projekts werden dieselben Protokollierungsvorgänge ausgeführt, aber der Protokollierungscode wird in einer einzelnen Datei zentralisiert.

Weiterführende Themen