인터페이스 설계
Coddy Python 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 64개 중 26번째.
인터페이스는 클래스가 따라야 하는 계약을 정의합니다. Python에서 인터페이스는 모든 메서드가 추상 메서드인 추상 베이스 클래스를 사용하여 만듭니다.
abc 모듈을 임포트합니다:
from abc import ABC, abstractmethod추상 메서드만 포함된 인터페이스를 생성합니다:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
pass인터페이스의 모든 메서드는 추상적이어야 합니다. 즉, 구현 클래스가 무엇을 해야 하는지를 정의할 뿐, 어떻게 수행할지를 정의하지는 않습니다.
인터페이스를 구체 클래스에서 구현합니다:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"동일한 인터페이스를 구현하는 또 다른 클래스를 생성합니다:
class Rectangle(Drawable):
def __init__(self, width, height):
self.width = width
self.height = height
def draw(self):
return "Drawing a rectangle"
def resize(self, width, height):
self.width = width
self.height = height
return f"Resized rectangle to {width}x{height}"인터페이스를 다형적으로 사용하세요:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))출력:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8인터페이스를 타입 힌트로 사용할 수도 있습니다:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))출력:
Drawing a circle핵심 포인트: 인터페이스는 클래스가 어떻게 수행하는지가 아니라 무엇을 해야 하는지를 정의합니다. 구현 클래스가 따라야 하는 명확한 계약을 만들기 위해 추상 메서드만 포함된 추상 기본 클래스를 사용하세요. 이는 서로 다른 구현 간에 일관된 동작을 보장합니다.
챌린지
중급이 챌린지에서는 인터페이스를 사용한 미디어 플레이어 시스템을 구현합니다.
다음 파일들을 수정해야 합니다:
playable.py- 추상 베이스 클래스(인터페이스) 구현song.py- Song 클래스 구현video.py- Video 클래스 구현mediaplayer.py- MediaPlayer 클래스 구현
각 파일에는 구현을 안내하는 상세한 TODO 주석이 포함되어 있습니다. 이 주석들을 따라 적절한 상속과 인터페이스 구현을 갖춘 완전한 미디어 플레이어 시스템을 만드세요.
치트 시트
abc 모듈을 사용하여 추상 베이스 클래스로 인터페이스를 만듭니다:
from abc import ABC, abstractmethod추상 메서드만 포함된 인터페이스를 정의합니다:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
pass구체 클래스(concrete classes)에서 인터페이스를 구현합니다:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"인터페이스를 다형성 있게 사용합니다:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))인터페이스를 타입 힌트로 사용합니다:
def render_shape(drawable: Drawable):
return drawable.draw()인터페이스는 클래스가 어떻게 수행하는지가 아니라 무엇을 해야 하는지를 정의합니다. 인터페이스의 모든 메서드는 구현 클래스를 위한 명확한 규약(contract)을 만들기 위해 추상 메서드여야 합니다.
직접 해보기
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# 종합 테스트 케이스 핸들러
test_case = input()
if test_case == "default_test":
# 원래 문제의 기본 테스트 케이스
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# 노래로 테스트
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # 가독성을 위한 빈 줄
# 비디오로 테스트
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# 미디어가 설정되지 않은 상태에서 미디어 플레이어 테스트
player = MediaPlayer()
print(player.play()) # "No media set"이 출력되어야 함
print(player.pause()) # "No media set"이 출력되어야 함
print(player.stop()) # "No media set"이 출력되어야 함
elif test_case == "time_formatting":
# get_info() 메서드의 시간 형식 지정 테스트
song1 = Song("Short Song", "Artist A", 65) # 1:05
song2 = Song("Long Song", "Artist B", 3661) # 61:01
video1 = Video("Hour Video", "720p", 3600) # 60:00
print(song1.get_info())
print(song2.get_info())
print(video1.get_info())
elif test_case == "interface_compliance":
# Song과 Video가 인터페이스를 올바르게 구현하는지 테스트
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# 인터페이스 구현 확인
print(f"Song implements Playable: {isinstance(song, Playable)}")
print(f"Song implements MediaInfo: {isinstance(song, MediaInfo)}")
print(f"Video implements Playable: {isinstance(video, Playable)}")
print(f"Video implements MediaInfo: {isinstance(video, MediaInfo)}")
# 모든 인터페이스 메서드 테스트
print(song.play())
print(song.pause())
print(song.stop())
print(song.get_title())
print(song.get_duration())
print(song.get_info())
print(video.play())
print(video.pause())
print(video.stop())
print(video.get_title())
print(video.get_duration())
print(video.get_info())
elif test_case == "polymorphism":
# 다양한 미디어 유형 리스트를 사용한 다형성 동작 테스트
media_list = [
Song("Song 1", "Artist 1", 180),
Video("Video 1", "720p", 300),
Song("Song 2", "Artist 2", 240),
Video("Video 2", "1080p", 420)
]
player = MediaPlayer()
for media in media_list:
player.set_media(media)
print(f"Media: {media.get_title()}")
print(f"Info: {media.get_info()}")
print(f"Play: {player.play()}")
print()
elif test_case == "edge_cases":
# 엣지 케이스 테스트
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # 매우 긴 제목, 빈 해상도, 음수 재생 시간
print(f"Empty song info: {empty_song.get_info()}")
print(f"Edge video info: {edge_video.get_info()}")
player = MediaPlayer()
player.set_media(empty_song)
print(player.play())
player.set_media(edge_video)
print(player.play())
elif test_case == "stress_test":
# 많은 미디어 객체를 사용한 부하 테스트 구현
songs = [Song(f"Song {i}", f"Artist {i}", i * 30) for i in range(1, 101)]
videos = [Video(f"Video {i}", f"{i*10}p", i * 60) for i in range(1, 101)]
player = MediaPlayer()
# 모든 노래로 테스트
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # 출력이 너무 많지 않도록 10번째마다만 출력
print(f"Playing song {i+1}: {player.play()}")
# 모든 비디오로 테스트
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # 출력이 너무 많지 않도록 10번째마다만 출력
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.