Conținut curs
Python Avansat
Despre lecție

Obiective

În această lecție, veți învăța:

  1. O introducere în arhitecturile de software
  2. Cele mai comune design patterns în Python
  3. Implementarea unor design patterns în Python

1. Introducere în arhitecturile de software

Arhitecturile de software reprezintă organizarea sistemelor de software și descriu modul în care componentele acestora interacționează. Ele sunt esențiale pentru a crea sisteme scalabile, maleabile și ușor de întreținut.

Un exemplu popular de arhitectură de software este Model-View-Controller (MVC), care separă componentele de afișare și control ale unei aplicații de logica de bază a acesteia. În Python, unele framework-uri web, cum ar fi Django, folosesc o abordare similară numită Model-View-Template (MVT).

2. Cele mai comune design patterns în Python

Design patterns sunt soluții generale reutilizabile la problemele comune întâlnite în proiectarea software-ului. Ele pot fi adaptate la nevoile specifice ale proiectului și pot ajuta la crearea unui cod mai eficient și ușor de întreținut. În continuare, vom explora câteva design patterns populare în Python.

2.1. Singleton

Singleton este un design pattern care asigură că o clasă are doar o singură instanță și oferă un punct de acces la aceasta. Acest lucru poate fi util pentru a coordona accesul la resurse partajate, cum ar fi conexiunile la baze de date.

python
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

class MyClass(Singleton):
    pass

obj1 = MyClass()
obj2 = MyClass()
assert obj1 is obj2

2.2. Factory Method

Factory Method este un design pattern care oferă o interfață pentru crearea de obiecte într-o superclasă, dar permite subclaselor să decidă ce obiecte să creeze. Acesta poate fi util pentru a separa logica de construcție de cea de utilizare.

python
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

class AnimalFactory:
    @staticmethod
    def create_animal(animal_type):
        if animal_type == "Dog":
            return Dog()
        elif animal_type == "Cat":
            return Cat()
        else:
            raise ValueError("Invalid animal type")

animal = AnimalFactory.create_animal("Dog")
animal.speak()  # Output: Woof!

2.3. Observer

Observer este un design pattern care oferă o interfață între obiecte, astfel încât când starea unui obiect se schimbă, toate obiectele dependente sunt notificate. Acesta poate fi util pentru a menține consistența între obiectele interconectate.

python
class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self):
        for observer in self._observers:
            observer.update(self)

class ConcreteSubject(Subject):
    def __init__(self):
        super().__init__()
        self._state = None

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value
        self.notify()

class Observer(ABC):
    @abstractmethod
    def update(self, subject):
        pass

class ConcreteObserver(Observer):
    def update(self, subject):
        print(f"Observer: Subject's state is now {subject.state}")

subject = ConcreteSubject()
observer = ConcreteObserver()
subject.attach(observer)
subject.state = "new state"  # Output: Observer: Subject's state is now new state

3. Implementarea unor designpatterns în Python

În această secțiune, vom implementa două design patterns suplimentare în Python: Decorator și Strategy.

3.1. Decorator

Decorator este un design pattern structural care permite adăugarea de funcționalități suplimentare unui obiect în mod dinamic, fără a afecta structura sa. Acesta poate fi util pentru a extinde comportamentul unor clase în mod flexibil și modular.

python
from abc import ABC, abstractmethod

class Component(ABC):
    @abstractmethod
    def operation(self):
        pass

class ConcreteComponent(Component):
    def operation(self):
        return "ConcreteComponent"

class Decorator(Component):
    def __init__(self, component):
        self._component = component

    def operation(self):
        return self._component.operation()

class ConcreteDecoratorA(Decorator):
    def operation(self):
        return f"ConcreteDecoratorA({self._component.operation()})"

class ConcreteDecoratorB(Decorator):
    def operation(self):
        return f"ConcreteDecoratorB({self._component.operation()})"
        
component = ConcreteComponent()
decoratorA = ConcreteDecoratorA(component)
decoratorB = ConcreteDecoratorB(decoratorA)
print(decoratorB.operation())  # Output: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))

3.2. Strategy

Strategy este un design pattern care permite schimbarea comportamentului (strategiei) unui obiect la runtime. Acesta poate fi util pentru a decupla algoritmii de structurile de date pe care le utilizează.

python
from abc import ABC, abstractmethod

class Strategy(ABC):
    @abstractmethod
    def execute(self, data):
        pass

class ConcreteStrategyA(Strategy):
    def execute(self, data):
        return sorted(data)

class ConcreteStrategyB(Strategy):
    def execute(self, data):
        return sorted(data, reverse=True)

class Context:
    def __init__(self, strategy):
        self._strategy = strategy

    def set_strategy(self, strategy):
        self._strategy = strategy

    def execute_strategy(self, data):
        return self._strategy.execute(data)

data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

context = Context(ConcreteStrategyA())
print(context.execute_strategy(data))  # Output: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

context.set_strategy(ConcreteStrategyB())
print(context.execute_strategy(data))  # Output: [9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]

Concluzie

În această lecție, am discutat despre arhitecturile de software și am explorat câteva design patterns comune în Python. Aceste concepte sunt importante pentru a crea software scalabil, modular și ușor de întreținut. Continuați să experimentați și să aplicați aceste design patterns în proiectele dvs. pentru a vă îmbunătăți abilitățile de programare în Python.