アダプターパターン
CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 50/64。
アダプターパターンは、互換性のないインターフェースを持つオブジェクト同士を連携させるためのパターンです。既存のクラスをクライアントが期待する新しいインターフェースでラップすることで、架け橋として機能します。
互換性のないインターフェースを持つ2つのシステムがあります:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"古いプリンターは old_print() を使用し、新しいプリンターは print() を使用します。
古いプリンターを新しいインターフェースで動作させるためのアダプターを作成します:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# 古いインターフェースを新しいインターフェースに適合させる
return self.old_printer.old_print(text)アダプターは古いプリンターをラップし、期待されるインターフェースを提供します。
同じクライアントコードで両方のプリンターを使用します:
def print_document(printer, text):
return printer.print(text) # print() メソッドを期待します
# 新しいプリンターを直接使用する
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# アダプターを介して古いプリンターを使用する
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))メディアプレーヤーを使用した別の例を作成します:
class Mp3Player:
def play_mp3(self, filename):
return f"Playing MP3: {filename}"
class Mp4Player:
def play_mp4(self, filename):
return f"Playing MP4: {filename}"
class MediaAdapter:
def __init__(self, player, audio_type):
self.player = player
self.audio_type = audio_type
def play(self, filename):
if self.audio_type == "mp3":
return self.player.play_mp3(filename)
elif self.audio_type == "mp4":
return self.player.play_mp4(filename)class AudioPlayer:
def play(self, audio_type, filename):
if audio_type == "mp3":
return Mp3Player().play_mp3(filename)
else:
adapter = MediaAdapter(Mp4Player(), audio_type)
return adapter.play(filename)
player = AudioPlayer()
print(player.play("mp3", "song.mp3"))
print(player.play("mp4", "video.mp4"))出力:
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4重要なポイント:アダプターパターンは、既存のクラスを新しいインターフェースでラップすることにより、互換性のないインターフェースを連携させます。アダプターは、期待されるインターフェースからの呼び出しを、ラップされたオブジェクトの実際のインターフェースへと変換します。これは、既存のコードを修正することなく、レガシーコードやサードパーティライブラリを統合するのに役立ちます。
チャレンジ
中級このチャレンジでは、アダプター(Adapter)デザインパターンを実装して、レガシーシステムを最新のアプリケーションアーキテクチャに統合します。アダプターパターンは、一方のインターフェースを別のインターフェースに変換するラッパー(アダプター)を作成することで、互換性のないインターフェースを持つオブジェクト同士を連携させることができます。
あなたの会社には、新しい最新の分析プラットフォームと統合する必要があるレガシーなデータ分析および可視化システムがあります。レガシーコンポーネントは、新しいシステムと互換性のないインターフェースを持っています。レガシーコードを書き直す(リスクが高くコストもかかる)代わりに、これらのコンポーネントを新しいシステムで動作させるためのアダプターを作成するタスクが課せられました。
以下の作業が必要です:
interfaces.pyで定義されている最新のインターフェースを理解する(このファイルは変更できません)legacy_system.pyにあるレガシーコンポーネントを調査する(このファイルは変更できません)adapters.pyに、レガシーコンポーネントを最新のインターフェースと互換性を持たせるアダプタークラスを実装するmodern_system.pyで、これらのインターフェースを使用する最新のシステムを作成する
チートシート
アダプターパターン(Adapter Pattern)は、既存のクラスをクライアントが期待する新しいインターフェースでラップすることにより、互換性のないインターフェースを持つオブジェクト同士を連携させることができます。
基本的なアダプターの構造:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# 古いインターフェースを新しいインターフェースに適合させる
return self.old_printer.old_print(text)互換性のないプリンターインターフェースの例:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"
# クライアントコードは print() メソッドを期待する
def print_document(printer, text):
return printer.print(text)
# アダプターを介して古いプリンターを使用する
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello")) # OLD: Helloメディアプレーヤーのアダプターの例:
class MediaAdapter:
def __init__(self, player, audio_type):
self.player = player
self.audio_type = audio_type
def play(self, filename):
if self.audio_type == "mp3":
return self.player.play_mp3(filename)
elif self.audio_type == "mp4":
return self.player.play_mp4(filename)アダプターは、期待されるインターフェースからの呼び出しを、ラップされたオブジェクトの実際のインターフェースに変換し、既存のコードを修正することなくレガシーコードの統合を可能にします。
自分で試してみよう
# 必要なクラスをすべてインポート
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# 総合的なテストケースハンドラー
test_case = input()
if test_case == "basic_adapter_test":
# アダプターの基本機能をテスト
processor = LegacyDataAnalyzerAdapter()
data = [1, 2, 3, 4, 5]
processor.process_data(data)
results = processor.get_results()
print(f"Results: {results}")
elif test_case == "visualizer_adapter_test":
# ビジュアライザーアダプターをテスト
visualizer = LegacyChartGeneratorAdapter("bar")
data = [10, 20, 30, 40, 50]
success = visualizer.visualize(data)
if success:
export_success = visualizer.export_visualization("test_chart.png")
print(f"Visualization exported: {export_success}")
elif test_case == "analytics_system_test":
# 分析システム全体をテスト
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter("line")
system = AnalyticsSystem(processor, visualizer)
data = [15, 25, 35, 45, 55]
result = system.analyze_and_visualize(data, "analytics_output.png")
print(f"System result: {result}")
elif test_case == "validation_error_test":
# バリデーションエラーをテスト
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data("invalid_data")
print("Validation failed - should have raised ValueError")
except ValueError as e:
print(f"Validation error caught: {e}")
elif test_case == "empty_data_test":
# 空のデータでテスト
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data([])
results = processor.get_results()
print(f"Empty data results: {results}")
except Exception as e:
print(f"Empty data error: {e}")
elif test_case == "chart_types_test":
# 異なるチャートタイプをテスト
chart_types = ["bar", "line", "pie"]
data = [5, 15, 25, 35]
for chart_type in chart_types:
visualizer = LegacyChartGeneratorAdapter(chart_type)
success = visualizer.visualize(data)
print(f"{chart_type} chart created: {success}")
elif test_case == "file_extension_test":
# ファイル拡張子の処理をテスト
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# 様々なファイル名形式をテスト
filenames = ["test", "test.jpg", "test.pdf", "test.png"]
for filename in filenames:
success = visualizer.export_visualization(filename)
print(f"Export '{filename}': {success}")
elif test_case == "summary_test":
# 分析サマリーをテスト
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter()
system = AnalyticsSystem(processor, visualizer)
data = [10, 20, 30, 40, 50, 60]
system.analyze_and_visualize(data)
summary = system.get_analysis_summary()
print("Analysis Summary:")
print(summary)
elif test_case == "mixed_data_test":
# 混合数値データでテスト
processor = LegacyDataAnalyzerAdapter()
data = [1.5, 2, 3.7, 4, 5.2]
processor.process_data(data)
results = processor.get_results()
print(f"Mixed data results: {results}")
elif test_case == "invalid_numeric_test":
# 無効な数値データでテスト
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data([1, 2, "three", 4, 5])
print("Validation failed - should have raised ValueError")
except ValueError as e:
print(f"Invalid numeric data error: {e}")
elif test_case == "no_results_summary_test":
# 結果がない場合のサマリーをテスト
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter()
system = AnalyticsSystem(processor, visualizer)
summary = system.get_analysis_summary()
print(f"No results summary: {summary}")
elif test_case == "large_dataset_test":
# 大規模データセットでテスト
processor = LegacyDataAnalyzerAdapter()
data = list(range(1, 101)) # 1から100まで
processor.process_data(data)
results = processor.get_results()
print(f"Large dataset - Mean: {results.get('mean', 'N/A'):.2f}")
print(f"Large dataset - Min: {results.get('min', 'N/A')}")
print(f"Large dataset - Max: {results.get('max', 'N/A')}")
elif test_case == "complete_workflow_test":
# 複数の操作を含む完全なワークフローをテスト
processor = LegacyDataAnalyzerAdapter()
bar_visualizer = LegacyChartGeneratorAdapter("bar")
line_visualizer = LegacyChartGeneratorAdapter("line")
data = [12, 18, 25, 32, 28, 35, 42]
# 異なるビジュアライザーを使用して分析システムを作成
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# 分析を実行
bar_result = bar_system.analyze_and_visualize(data, "bar_chart.png")
line_result = line_system.analyze_and_visualize(data, "line_chart.png")
print("Complete workflow test completed")
print(f"Bar chart result: {bar_result['visualization_file']}")
print(f"Line chart result: {line_result['visualization_file']}")
# サマリーを取得
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。