Menu
CoddyTech

Thiết kế giao diện

Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 26 trên 64.

Giao diện định nghĩa một hợp đồng mà các lớp phải tuân theo. Trong Python, ta tạo giao diện bằng cách dùng các lớp cơ sở trừu tượng, trong đó tất cả phương thức đều là trừu tượng.

Nhập module abc:

from abc import ABC, abstractmethod

Tạo một giao diện chỉ có các phương thức trừu tượng:

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass
    @abstractmethod
    def resize(self, width, height):
        pass

Tất cả phương thức trong một interface nên là phương thức trừu tượng. Chúng xác định các lớp triển khai phải làm gì, chứ không phải làm như thế nào.

Triển khai giao diện trong một lớp cụ thể:

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}"

Tạo một lớp khác triển khai cùng giao diện:

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}"

Sử dụng giao diện (interface) theo cách đa hình:

shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
    print(shape.draw())
    print(shape.resize(10, 8))

Đầu ra:

Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8

Bạn cũng có thể dùng giao diện làm gợi ý kiểu dữ liệu:

def render_shape(drawable: Drawable):
    return drawable.draw()
circle = Circle(3)
print(render_shape(circle))

Đầu ra:

Drawing a circle

Điểm chính: Giao diện (interface) định nghĩa những việc các lớp phải làm, chứ không quy định cách chúng thực hiện. Hãy dùng các lớp cơ sở trừu tượng chỉ có phương thức trừu tượng để tạo ra những quy ước rõ ràng mà các lớp triển khai phải tuân theo. Điều này đảm bảo hành vi nhất quán giữa các cách triển khai khác nhau.

challenge icon

Thử thách

Trung bình

Trong thử thách này, bạn sẽ triển khai một hệ thống trình phát đa phương tiện bằng các giao diện.

Bạn cần chỉnh sửa các tệp sau:

  • playable.py - Triển khai các lớp cơ sở trừu tượng (giao diện)
  • song.py - Triển khai lớp Song
  • video.py - Triển khai lớp Video
  • mediaplayer.py - Triển khai lớp MediaPlayer

Mỗi tệp đều có các chú thích TODO chi tiết để hướng dẫn bạn triển khai. Hãy làm theo những chú thích này để tạo một hệ thống trình phát đa phương tiện hoàn chỉnh với tính kế thừa phù hợp và cách triển khai giao diện.

Tự làm thử

from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo

# Xử lý toàn diện các test case
test_case = input()

if test_case == "default_test":
    # Test case mặc định từ đề bài gốc
    song = Song("Bohemian Rhapsody", "Queen", 355)
    video = Video("Python Tutorial", "1080p", 1800)
    player = MediaPlayer()

    # Kiểm tra với bài hát
    player.set_media(song)
    print(player.current_media.get_info())
    print(player.play())
    print(player.pause())
    print(player.stop())
    print()  # Dòng trống giúp dễ đọc hơn

    # Kiểm tra với video
    player.set_media(video)
    print(player.current_media.get_info())
    print(player.play())
    print(player.pause())
    print(player.stop())

elif test_case == "empty_player":
    # Kiểm tra trình phát khi chưa thiết lập nội dung
    player = MediaPlayer()
    print(player.play())  # Sẽ in ra "No media set"
    print(player.pause())  # Sẽ in ra "No media set"
    print(player.stop())  # Sẽ in ra "No media set"

elif test_case == "time_formatting":
    # Kiểm tra cách định dạng thời gian trong phương thức (method) 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":
    # Kiểm tra Song và Video triển khai đúng các giao diện
    song = Song("Test Song", "Test Artist", 180)
    video = Video("Test Video", "480p", 240)
    
    # Kiểm tra việc triển khai giao diện
    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)}")
    
    # Kiểm tra tất cả phương thức của giao diện
    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":
    # Kiểm tra hành vi đa hình với danh sách (list) gồm nhiều loại nội dung khác nhau
    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":
    # Kiểm tra các trường hợp biên
    empty_song = Song("", "", 0)
    edge_video = Video("A" * 100, "", -10)  # Tiêu đề rất dài, độ phân giải trống, thời lượng âm
    
    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":
    # Triển khai kiểm thử tải với nhiều đối tượng đa phương tiện
    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()
    
    # Kiểm tra với tất cả bài hát
    for i, song in enumerate(songs):
        player.set_media(song)
        if i % 10 == 0:  # Chỉ in ra mỗi mục thứ 10 để tránh quá nhiều đầu ra
            print(f"Playing song {i+1}: {player.play()}")
    
    # Kiểm tra với tất cả video
    for i, video in enumerate(videos):
        player.set_media(video)
        if i % 10 == 0:  # Chỉ in ra mỗi mục thứ 10 để tránh quá nhiều đầu ra
            print(f"Playing video {i+1}: {player.play()}")
    
    print("Stress test completed successfully!")
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Python online