横断的関心事は、ログ、セキュリティ、データ検証、エラー処理など、システムの複数の部分に影響を与えるプログラムの側面です。 それらは、コードベース全体にちらばっていて、コードの重複やメンテナンスの課題の原因になる可能性があります。
Copilot Chat は、アスペクト指向プログラミング (AOP) プラクティスの実装を提案したり、デコレーターやミドルウェア パターンを使ってモジュール形式の保守可能な方法でこれらの関心事を一本化したりして、横断的関心事のリファクタリングに役立ちます。
シナリオ例
ログが行われる複数のサービス ファイルを含む Python プロジェクトがあるとします。 ログされる情報は、個々のサービス ファイル内で定義されています。 将来、アプリケーションが変更または拡張される場合、この設計のため、ログ エントリの内容とスタイルに不整合が生じる可能性があります。 ログの動作を統合して一元化し、これがプロジェクト全体に広がるのを防ぐことができます。
このプロジェクト例には、エントリ ポイント ファイル (main.py
)、ログ メッセージ構成ファイル (logging_config.py
)、サービス ファイル (order_service.py
) という 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
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
などのライブラリを使用できます。 これを行う方法を次に示します。
aspectlib
をインストールします。
pip install aspectlib
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}")
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
リファクタリング後のバージョンのプロジェクトでは、同じログ操作が実行されますが、ログのコードは 1 つのファイルに一元化されています。