Skip to main content

处理横切关注点

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 视图中输入以下提示。

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

Note

VS Code 和 Visual Studio 中的 @workspace 聊天参与者为 Copilot 提供工作区中所有代码的上下文。 当希望 Copilot 考虑项目的结构以及代码的不同部分如何进行交互时,可以使用 @workspace

示例响应

Copilot 会做出如下响应。

Note

以下响应是示例。 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

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

其他阅读材料