アダプターパターン
CoddyのPythonジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 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 Patternは、既存のクラスを新しいインターフェースでラップすることで、互換性のないインターフェース同士を連携させます。アダプターは、期待されるインターフェースへの呼び出しを、ラップされたオブジェクトの実際のインターフェースへの呼び出しに変換します。これは、既存のコードを変更せずにレガシーコードやサードパーティライブラリを統合する場合に役立ちます。
チャレンジ
中級このチャレンジでは、レガシーシステムを最新のアプリケーションアーキテクチャに統合するために、Adapterデザインパターンを実装します。Adapterパターンは、一方のインターフェースを別のインターフェースに変換するラッパー(アダプター)を作成することで、互換性のないインターフェースを持つオブジェクト同士を連携させます。
あなたの会社には、新しい最新の分析プラットフォームと統合する必要がある、レガシーのデータ分析および可視化システムがあります。レガシーコンポーネントのインターフェースは、新しいシステムと互換性がありません。レガシーコードを書き直す代わりに(それにはリスクと高いコストが伴います)、これらのコンポーネントを新しいシステムで動作させるためのアダプターを作成することがあなたの課題です。
次のことを行う必要があります:
interfaces.pyで定義されている最新のインターフェースを理解する(このファイルは変更できません)legacy_system.pyにあるレガシーコンポーネントを調べる(このファイルは変更できません)- レガシーコンポーネントを最新のインターフェースと互換性のあるものにするアダプタークラスを
adapters.pyに実装する - これらのインターフェースを使用する最新のシステムを
modern_system.pyに作成する
自分で試してみよう
# 必要なクラスをすべてインポートする
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":
# Test validation errors
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":
# Test analysis summary
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":
# Test complete workflow with multiple operations
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())このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Pythonオンラインコンパイラ