跳至主要内容

处理横切关注点

Copilot Chat 可以帮助您避免编写与所在方法或函数核心关注点无关的代码。

横切关注点是程序中影响系统多个部分的方面,例如日志记录、安全、数据验证和错误处理。这些关注点可能散布在整个代码库中,导致代码重复和维护难题。

Copilot Chat 可以通过建议实现面向切面编程(AOP)实践,或使用装饰器和中间件模式,将这些横切关注点集中到模块化、易于维护的方式,从而帮助重构横切关注点。

示例情景

设想你有一个 Python 项目,其中包含多个执行日志记录的服务文件。日志信息在每个单独的服务文件中定义。如果将来对应用进行修改或扩展,这种设计可能导致日志条目的内容和风格不一致。你可以整合并集中日志行为,以避免这种分散在项目中的情况。

以下是我们示例项目的三个文件:入口文件 (main.py)、日志消息配置文件 (logging_config.py),以及其中一个服务文件 (order_service.py)。示例服务文件展示了日志信息如何与特定部分的业务逻辑一起定义。

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.")

示例提示

在 VS Code 或 Visual Studio 中打开项目工作区,并在 Copilot Chat 视图中输入以下提示。

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

注意

The @workspace 聊天参与者在 VS Code 和 Visual Studio 中为 Copilot 提供工作区内所有代码的上下文。当你希望 Copilot 考虑项目结构以及代码各部分之间的交互时,可以使用 @workspace。如果你使用 JetBrains IDE,请使用 @project 而不是 @workspace

示例回复

Copilot 的回复如下。

注意

以下响应为示例。Copilot Chat 的回复具有不确定性,您可能会得到与此处显示的不同的响应。

要使用面向切面编程(AOP)重构此项目,以将日志记录作为横切关注点处理,你可以使用类似 aspectlib 的库。以下是实现方法

  1. 安装 aspectlib

    pip install aspectlib

  2. 在名为 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. 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
    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 随后建议你从所有服务类中移除日志代码,并提供了修改后的文件代码。例如,这是修改后的 order_service.py 版本

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

在项目的重构版本中,执行了相同的日志操作,但日志代码已集中在单个文件中。

延伸阅读

© . This site is unofficial and not affiliated with GitHub, Inc.