Menu
Coddy logo textTech

Kompozit Kalıbı

Coddy'nin Python Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 54 / 64.

Kompozit Deseni (Composite Pattern), tekil nesneleri ve nesne gruplarını tek tipte ele alır. Hem tekil öğelerin hem de öğe koleksiyonlarının aynı arayüzü paylaştığı ağaç yapıları oluşturur.

İşte bir dosya sistemi için basit bileşenler:

class File:
    def __init__(self, name, size):
        self.name = name
        self.size = size
    
    def get_size(self):
        return self.size
    
    def display(self):
        return f"File: {self.name} ({self.size}KB)"

class Folder:
    def __init__(self, name):
        self.name = name
        self.children = []
    
    def add(self, item):
        self.children.append(item)
    
    def get_size(self):
        total = 0
        for child in self.children:
            total += child.get_size()
        return total
    
    def display(self):
        result = f"Folder: {self.name}"
        for child in self.children:
            result += f"\n  {child.display()}"
        return result

Hem dosyalar hem de klasörler aynı metotlara (get_size() ve display()) sahiptir, bu nedenle tek tip olarak ele alınabilirler.

Bir dosya sistemi yapısı oluşturun:

# Dosyaları oluştur
file1 = File("document.txt", 10)
file2 = File("image.jpg", 50)
file3 = File("video.mp4", 200)

# Klasörleri oluştur
documents = Folder("Documents")
media = Folder("Media")
root = Folder("Root")

# Ağaç yapısını oluştur
documents.add(file1)
media.add(file2)
media.add(file3)
root.add(documents)
root.add(media)

Kompozit yapıyı kullanın:

print(f"Root size: {root.get_size()}KB")
print(root.display())

Menü sistemi içeren başka bir örnek oluşturalım:

class MenuItem:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def get_price(self):
        return self.price
    
    def show(self):
        return f"{self.name}: ${self.price}"

class Menu:
    def __init__(self, name):
        self.name = name
        self.items = []
    
    def add(self, item):
        self.items.append(item)
    
    def get_price(self):
        total = 0
        for item in self.items:
            total += item.get_price()
        return total
    
    def show(self):
        result = f"{self.name} Menu:"
        for item in self.items:
            result += f"\n  {item.show()}"
        return result

combo = Menu("Combo")
combo.add(MenuItem("Burger", 8))
combo.add(MenuItem("Fries", 3))
combo.add(MenuItem("Drink", 2))

print(f"Combo price: ${combo.get_price()}")
print(combo.show())

Çıktı:

Root size: 260KB
Folder: Root
  Folder: Documents
    File: document.txt (10KB)
  Folder: Media
    File: image.jpg (50KB)
    File: video.mp4 (200KB)
Combo price: $13
Combo Menu:
  Burger: $8
  Fries: $3
  Drink: $2

Önemli Nokta: Composite Pattern (Bileşik Deseni), tekil nesnelere ve nesne koleksiyonlarına aynı şekilde davranmanıza olanak tanır. Hem yapraklar (tekil öğeler) hem de bileşikler (gruplar) aynı arayüzü uygular; bu da dosya sistemleri, menüler veya organizasyon şemaları gibi ağaç yapılarıyla çalışmayı kolaylaştırır.

challenge icon

Görev

Orta

Bu meydan okumada, Composite tasarım desenini kullanarak bir dosya sistemi yapısı uygulayacaksınız. Composite deseni, nesneleri parça-bütün hiyerarşilerini temsil edecek şekilde ağaç yapılarında birleştirmenize olanak tanır ve bireysel nesneler ile nesne bileşimlerine tek tip bir şekilde davranılmasını sağlar.

Composite deseni şunlardan oluşur:

  • Component: Tüm somut sınıflar için ortak arayüzü tanımlayan soyut bir sınıf
  • Leaf: Alt öğesi olmayan, bir bileşimin uç nesnelerini temsil eder
  • Composite: Çocukları olan bileşenler için davranışı tanımlar ve çocuk bileşenleri saklar

Şunları içeren bir dosya sistemi uygulayacaksınız:

  • Soyut bir FileSystemComponent sınıfı (Component)
  • Bir File sınıfı (Leaf)
  • Bir Directory sınıfı (Composite)
  • Genel yapıyı yönetmek için bir FileSystem sınıfı
  1. Uygun soyut metotlarla soyut temel sınıfı uygulayın
  2. Dosyalar ve dizinler için somut uygulamalar oluşturun
  3. Dizinlerin hem dosyaları hem de diğer dizinleri içerebildiğinden emin olun
  4. Boyut hesaplama ve görüntüleme gibi özyinelemeli (recursive) işlemleri uygulayın
  5. Bileşen ekleme, kaldırma ve bulma için yol tabanlı (path-based) işlemler ekleyin
  6. Hata durumlarını uygun şekilde ele alın
  7. Bileşen özelliklerinin düzgün bir şekilde kapsüllendiğinden (encapsulation) emin olun

Kopya kağıdı

Composite Deseni (Composite Pattern), hem tekil öğelerin hem de koleksiyonların aynı arayüzü paylaştığı ağaç yapıları oluşturarak, tekil nesnelere ve nesne gruplarına tek tip bir şekilde yaklaşır.

Temel Dosya Sistemi Örneği:

class File:
    def __init__(self, name, size):
        self.name = name
        self.size = size
    
    def get_size(self):
        return self.size
    
    def display(self):
        return f"File: {self.name} ({self.size}KB)"

class Folder:
    def __init__(self, name):
        self.name = name
        self.children = []
    
    def add(self, item):
        self.children.append(item)
    
    def get_size(self):
        total = 0
        for child in self.children:
            total += child.get_size()
        return total
    
    def display(self):
        result = f"Folder: {self.name}"
        for child in self.children:
            result += f"\n  {child.display()}"
        return result

Yapıyı Oluşturma:

# Dosyaları oluştur
file1 = File("document.txt", 10)
file2 = File("image.jpg", 50)

# Klasörleri oluştur
documents = Folder("Documents")
root = Folder("Root")

# Ağaç yapısını oluştur
documents.add(file1)
root.add(documents)

# Tek tip olarak kullan
print(f"Root size: {root.get_size()}KB")
print(root.display())

Menü Sistemi Örneği:

class MenuItem:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def get_price(self):
        return self.price
    
    def show(self):
        return f"{self.name}: ${self.price}"

class Menu:
    def __init__(self, name):
        self.name = name
        self.items = []
    
    def add(self, item):
        self.items.append(item)
    
    def get_price(self):
        total = 0
        for item in self.items:
            total += item.get_price()
        return total
    
    def show(self):
        result = f"{self.name} Menu:"
        for item in self.items:
            result += f"\n  {item.show()}"
        return result

Desen Bileşenleri:

  • Bileşen (Component): Ortak arayüzü tanımlayan soyut sınıf
  • Yaprak (Leaf): Alt öğesi olmayan uç nesneler (File, MenuItem)
  • Bileşik (Composite): Aynı arayüzü uygulayan alt öğelere sahip nesneler (Folder, Menu)

Temel Avantajlar: Tekil nesnelere ve koleksiyonlara tek tip bir şekilde yaklaşarak dosya sistemleri, menüler veya organizasyon şemaları gibi ağaç yapılarıyla çalışmayı kolaylaştırır.

Kendin dene

# Gerekli tüm sınıfları içe aktar
from file_system import FileSystem
from directory import Directory
from file import File

# Kapsamlı test durumu işleyicisi
test_case = input()

if test_case == "basic_file_test":
    file = File("test.txt", 100)
    print(f"Name: {file.name}")
    print(f"Size: {file.get_size()} KB")
    print(file.display())

elif test_case == "basic_directory_test":
    documents = Directory("Documents")
    file1 = File("resume.pdf", 250)
    file2 = File("cover_letter.doc", 180)
    documents.add(file1)
    documents.add(file2)
    print(f"Total size: {documents.get_size()} KB")
    print(documents.display())

elif test_case == "file_system_basic_test":
    fs = FileSystem()
    readme = File("README.md", 50)
    fs.add_to_path("/", readme)
    print(fs.display())
    print(f"Total system size: {fs.get_total_size()} KB")

elif test_case == "nested_directory_test":
    fs = FileSystem()
    docs = Directory("Documents")
    projects = Directory("Projects")
    
    fs.add_to_path("/", docs)
    fs.add_to_path("/Documents", projects)
    
    project_file = File("main.py", 300)
    readme = File("README.md", 75)
    
    fs.add_to_path("/Documents/Projects", project_file)
    fs.add_to_path("/Documents/Projects", readme)
    
    print(fs.display())

elif test_case == "path_operations_test":
    fs = FileSystem()
    
    # Dizin yapısını oluştur
    docs = Directory("Documents")
    projects = Directory("Projects")
    fs.add_to_path("/", docs)
    fs.add_to_path("/Documents", projects)
    
    # Dosyaları ekle
    file1 = File("notes.txt", 120)
    file2 = File("project1.py", 450)
    fs.add_to_path("/Documents", file1)
    fs.add_to_path("/Documents/Projects", file2)
    
    # Yol işlemlerini test et
    retrieved_docs = fs.get_from_path("/Documents")
    retrieved_file = fs.get_from_path("/Documents/Projects/project1.py")
    
    print(f"Documents directory size: {retrieved_docs.get_size()} KB")
    print(f"Retrieved file: {retrieved_file.name} ({retrieved_file.get_size()} KB)")

elif test_case == "file_validation_test":
    try:
        invalid_file = File("negative.txt", -50)
        print("Validation failed - should have raised ValueError")
    except ValueError as e:
        print(f"Caught expected error: {e}")
    
    # NotImplementedError işlemlerini test et
    valid_file = File("test.txt", 100)
    try:
        valid_file.add(File("other.txt", 50))
    except NotImplementedError as e:
        print(f"Add operation error: {e}")
    
    try:
        valid_file.get_component("nonexistent")
    except NotImplementedError as e:
        print(f"Get component error: {e}")

elif test_case == "directory_duplicate_test":
    directory = Directory("TestDir")
    file1 = File("duplicate.txt", 100)
    file2 = File("duplicate.txt", 200)
    
    directory.add(file1)
    print("First file added successfully")
    
    try:
        directory.add(file2)
        print("Duplicate check failed")
    except ValueError as e:
        print(f"Caught expected duplicate error: {e}")

elif test_case == "component_removal_test":
    directory = Directory("TestDir")
    file1 = File("file1.txt", 100)
    file2 = File("file2.txt", 150)
    file3 = File("file3.txt", 200)
    
    directory.add(file1)
    directory.add(file2)
    directory.add(file3)
    
    print("Initial state:")
    print(directory.display())
    
    directory.remove(file2)
    print("\nAfter removing file2.txt:")
    print(directory.display())
    
    try:
        nonexistent = File("ghost.txt", 50)
        directory.remove(nonexistent)
    except ValueError as e:
        print(f"\nRemoval error: {e}")

elif test_case == "recursive_search_test":
    root_dir = Directory("root")
    subdir1 = Directory("subdir1")
    subdir2 = Directory("subdir2")
    
    file1 = File("target.txt", 100)
    file2 = File("other.txt", 150)
    file3 = File("deep.txt", 200)
    
    root_dir.add(subdir1)
    subdir1.add(subdir2)
    subdir1.add(file1)
    subdir2.add(file3)
    root_dir.add(file2)
    
    # Mevcut dosyaları ara
    found1 = root_dir.find_component_recursive("target.txt")
    found2 = root_dir.find_component_recursive("deep.txt")
    not_found = root_dir.find_component_recursive("missing.txt")
    
    print(f"Found target.txt: {found1.name if found1 else 'Not found'}")
    print(f"Found deep.txt: {found2.name if found2 else 'Not found'}")
    print(f"Found missing.txt: {not_found.name if not_found else 'Not found'}")

elif test_case == "size_calculation_test":
    fs = FileSystem()
    
    # Karmaşık yapı oluştur
    docs = Directory("Documents")
    images = Directory("Images")
    
    fs.add_to_path("/", docs)
    fs.add_to_path("/", images)
    
    # Bilinen boyutlarda dosyalar ekle
    doc1 = File("doc1.txt", 100)
    doc2 = File("doc2.txt", 200)
    img1 = File("img1.jpg", 500)
    img2 = File("img2.png", 300)
    
    fs.add_to_path("/Documents", doc1)
    fs.add_to_path("/Documents", doc2)
    fs.add_to_path("/Images", img1)
    fs.add_to_path("/Images", img2)
    
    # Boyutları hesapla
    docs_size = fs.get_from_path("/Documents").get_size()
    images_size = fs.get_from_path("/Images").get_size()
    total_size = fs.get_total_size()
    
    print(f"Documents size: {docs_size} KB")
    print(f"Images size: {images_size} KB")
    print(f"Total size: {total_size} KB")
    print(f"Sum verification: {docs_size + images_size == total_size}")

elif test_case == "display_formatting_test":
    root = Directory("root")
    level1 = Directory("level1")
    level2 = Directory("level2")
    
    file1 = File("root_file.txt", 100)
    file2 = File("level1_file.txt", 200)
    file3 = File("level2_file.txt", 300)
    
    root.add(file1)
    root.add(level1)
    level1.add(file2)
    level1.add(level2)
    level2.add(file3)
    
    print("Formatted directory structure:")
    print(root.display())

elif test_case == "file_system_path_test":
    fs = FileSystem()
    
    try:
        # Yapıyı oluştur
        fs.add_to_path("/", Directory("home"))
        fs.add_to_path("/home", Directory("user"))
        fs.add_to_path("/home/user", File("profile.txt", 150))
        
        # Erişimi test et
        user_dir = fs.get_from_path("/home/user")
        profile = fs.get_from_path("/home/user/profile.txt")
        
        print(f"User directory: {user_dir.name}")
        print(f"Profile file: {profile.name}")
        
        # Silme işlemini test et
        fs.remove_from_path("/home/user/profile.txt")
        print("Profile removed successfully")
        
        # Silinen dosyaya erişmeyi dene
        try:
            fs.get_from_path("/home/user/profile.txt")
        except ValueError as e:
            print(f"Expected error accessing removed file: {e}")
            
    except ValueError as e:
        print(f"Path operation error: {e}")

elif test_case == "empty_directory_test":
    empty_dir = Directory("Empty")
    
    print(f"Empty directory size: {empty_dir.get_size()} KB")
    print("Empty directory display:")
    print(empty_dir.display())
    
    result = empty_dir.get_component("nonexistent")
    print(f"Get nonexistent component: {result}")

elif test_case == "name_property_test":
    file = File("original.txt", 100)
    directory = Directory("OriginalDir")
    
    print(f"Original file name: {file.name}")
    print(f"Original directory name: {directory.name}")
    
    # Geçerli isim atamayı test et
    file.name = "renamed.txt"
    directory.name = "RenamedDir"
    
    print(f"Renamed file: {file.name}")
    print(f"Renamed directory: {directory.name}")
    
    # Boş isim atamayı test et
    try:
        file.name = ""
        print("Empty name validation failed")
    except ValueError as e:
        print(f"Empty name error: {e}")

elif test_case == "large_file_system_test":
    fs = FileSystem()
    
    # Birden fazla dizin ve dosya oluştur
    directories = ["Documents", "Images", "Videos", "Music"]
    file_counts = [5, 3, 2, 4]
    base_sizes = [100, 500, 1000, 200]
    
    total_files = 0
    total_directories = len(directories)
    
    for i, dir_name in enumerate(directories):
        fs.add_to_path("/", Directory(dir_name))
        
        for j in range(file_counts[i]):
            file_name = f"file_{j+1}.ext"
            file_size = base_sizes[i] + (j * 50)
            fs.add_to_path(f"/{dir_name}", File(file_name, file_size))
            total_files += 1
    
    print(f"Created {total_directories} directories")
    print(f"Created {total_files} files")
    print(f"Total system size: {fs.get_total_size()} KB")
    print("\nSystem structure:")
    print(fs.display())

elif test_case == "edge_cases_test":
    # Boyutu 0 olan dosyayı test et
    zero_file = File("empty.txt", 0)
    print(f"Zero size file: {zero_file.get_size()} KB")
    
    # Çok uzun isimli dizini test et
    long_name = "A" * 100
    long_dir = Directory(long_name)
    print(f"Long directory name length: {len(long_dir.name)}")
    
    # Derin iç içe geçmiş yapıyı test et
    current = Directory("level0")
    root = current
    
    for i in range(1, 6):
        next_level = Directory(f"level{i}")
        current.add(next_level)
        current = next_level
    
    # En derin seviyeye dosya ekle
    deep_file = File("deep.txt", 100)
    current.add(deep_file)
    
    print("Deep nesting test:")
    print(root.display())
    
    # Boş yapılar üzerindeki işlemleri test et
    empty = Directory("Empty")
    try:
        empty.remove(File("ghost.txt", 50))
    except ValueError as e:
        print(f"Empty structure removal error: {e}")
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Object Oriented Programming bölümündeki tüm dersler