SOLID Python

Princípios SOLID em projetos Python

Por Paula Grangeiro • PythonRio Junho 2018

Paula Grangeiro

#WebDeveloper #Senior #Python #Freelancer #Pyladies

#DesignPatterns #QualidadeCódigo

Primórdios da Programação

Eniac
Programmers

Programação em Batch

  • Instruções sequenciais
  • ø fallback
Deck of code cards
Fortran card Cartão de código Fortran
Fortran program Programa Fortran com subrotinas demarcadas

As linguagens de programação evoluíram de acordo com a interação homem x código.

Evolução dos Paradigmas

Estruturado

Procedural

Orientado à Objeto

Funcional

...

Fortran program

Orientação Objeto

Orientação Objeto

  • Reusabilidade de código
  • Conjunto de componentes que interagem (objetos)

- Acoplamento

+ Coesão

Encontrar Palíndromo (procedural)

Encontrar Palíndromo (oo)

O que é um palíndromo?

Código procedural em estrutura de classes

"(...) most of us (developers) use those languages (OO) without knowing why, and without knowing how to get the the most benefit out of them." - Robert C. Martin

Design Patterns

  • 23 padrões, divididos em 3 classificações
  • Conjunto de soluções para diferentes problemas de O.O.
    • Motivo, Aplicabilidade e Tradeoffs
  • Nem todos são aplicáveis em linguagens dinâmicas
  • Criação
    • Factory Method
    • Singleton
    • ...
  • Estruturais
    • Decorator
    • Proxy
    • ...
  • Comportamentais
    • Iterator
    • Command
    • ...

SOLID

Single Responsability Principle

Open/Closed Principle

Liskov Substituition Principle

Interface Segregation Principle

Dependency Inversion Principle

S.O.L.I.D.

Single Responsability

Uma classe deve ter um (e apenas um) único motivo para ser modificada


class Impressora:

    def __init__(self, cartuchos, folhas):
        self.cartuchos = cartuchos
        self.folhas = folhas

    def valida_cartucho(self):
        # verifica se cartucho tem tinta

    def valida_folha(self):
        # verifica se existem folhas

    def imprime(self, informacao):
        valida_cartucho()
        valida_folhas()

        for folha in folhas:
          # pruuu pri pripri pri
          # imprimindo informacoes
                  

class Cartucho:

    def __init__(self, **kwargs):
        # inicializa cartucho

    def validate(self):
        # valida estado do objeto cartucho


class Folha:

    def __init__(self, **kwargs):
        # inicializa objeto folha

    def validate(self):
        # valida estado do objeto folha


class Impressora:

    def __init__(self, cartuchos, folhas):
        # inicializa impressoa

    def imprime(self, informacao):
        # valida cartuchos e folhas e imprime
        for folha in folhas:
          # pruuu pri pripri pri
          # imprimindo informacoes
                  

Open/Closed

Você deve ser capaz de extender o comportamento de uma classe, sem modificá-lo.


class Impressora:

    def __init__(self, cartuchos, folhas):
        # inicializa impressoa

    def imprime(self, informacao):
        # valida cartuchos e folhas e imprime
        for folha in folhas:
          # pruuu pri pripri pri
          # imprimindo informacoes
                  

class BaseImpressora:

    def imprime(self, informacao):
        self.impressor.imprime(informacao)


class ImpressoraFolha(BaseImpressora):

    def __init__(self, cartuchos, folhas):
        self.impressor = ImpressorFolhas(cartucho, folhas)


class Impressora3D(BaseImpressora):

    def __init__(self, filamento):
        self.impressor = Impressor3D(filamento)


class ImpressorFolhas:

    def __init__(self, cartuchos, folhas):
        # inicializa

    def imprime(self, informacao):
        for folha in folhas:
            try:
                # imprime informacao
                # cabô espaço da folha
                # lança exceção
            except NaoTemMaisEspacoFolhaError:
                continue
            except NaoTemMaisTintaCartuchoError:
                raise CaboTintaError()


class Impressor3D:

    def __init__(self, filamento):
        # inicializa

    def imprime(self, informacao):
        while filamento.exist():
            filamento.imprime(informacao)
                  

Liskov

Classes derivadas devem ser facilmente substituídas por suas superclasses.


class BaseImpressora:

    def imprime(self, informacao):
        self.impressor.imprime(informacao)


class ImpressoraFolha(BaseImpressora):

    def __init__(self, cartuchos, folhas):
        self.impressor = ImpressorFolhas(cartucho, folhas)


class Impressora3D(BaseImpressora):

    def __init__(self, filamento):
        self.impressor = Impressor3D(filamento)
                  

class ImpressaoController:

    def __call__(self, informacao, cartuchos=None, folhas=None, filamento=None):
        if folhas:
            impressora = ImpressoraFolha(cartuchos, folhas)
        else:
            impressora = Impressora3D(filamento)
        impressora.imprime(informacao)
                  

class Impressora:

    def __init__(self, impressor):
        self.impressor = impressor

    def imprime(self, informacao):
        self.impressor.imprime(informacao)


class BaseImpressor:

    def imprime(self, informacao):
        raise NotImplementedError


class ImpressorFolhas(BaseImpressor):

    def __init__(self, cartuchos, folhas):
        # inicializa impressor

    def imprime(self, informacao):
        for folha in folhas:
            try:
                # imprime informacao
                # cabô espaço da folha
                # lança exceção
            except NaoTemMaisEspacoFolhaError:
                continue
            except NaoTemMaisTintaCartuchoError:
                raise CaboTintaError()


class Impressor3D(BaseImpressor):
    # implementa impressor 3d


class ImpressorXablau(BaseImpressor)
    # implementar impressor xablau
                  

class ImpressaoController:

    def __call__(self, impressora, impressor, informacao):
        impressora = Impressora(impressor)
        impressora.imprime(informacao)
                  

Interface Segregation

Interfaces devem ser refinadas e específicas por cliente


class Impressora:

    def __init__(self, impressor):
        self.impressor = impressor

    def imprime(self, informacao):
        self.impressor.imprime(informacao)
                  

class ScanMixin:

    def scan(self, informacao):
        # implementa scan


class Impressora:

    def __init__(self, impressor):
        self.impressor = impressor

    def imprime(self, informacao):
        self.impressor.imprime(informacao)


class Multifuncional(ScanMixin, Impressora):
    pass
                  

Dependency Inversion

Dependa de abstrações, não de concretos.


class ImpressoraFolha(BaseImpressora):

    def __init__(self, cartuchos, folhas):
        self.impressor = ImpressorFolhas(cartucho, folhas)


class ImpressorFolhas:

    def __init__(self, cartuchos, folhas):
        # inicializa

    def imprime(self, informacao):
        for folha in folhas:
            try:
                # imprime informacao
                # cabô espaço da folha
                # lança exceção
            except NaoTemMaisEspacoFolhaError:
                continue
            except NaoTemMaisTintaCartuchoError:
                raise CaboTintaError()

                  

class Impressora:

    def __init__(self, impressor):
        self.impressor = impressor

    def imprime(self, informacao):
        self.impressor.imprime(informacao)


class BaseImpressor:

    def imprime(self, informacao):
        raise NotImplementedError
                  

Bullets

  • Evite otimização precoce
    • Experimente os principios em partes do sistema que hoje estão ruins de manter.
  • Não seja xiita com princípios ou padrões de projetos
    • Foque nas vantagens e desvantagens da implementação seguindo os princípios SOLID.
    • Se vai facilitar demais por um lado e complicar demais do outro, não troque 6 por meia-dúzia
    • Se está ruim de testar, de repente não deu bom :/
  • Qualidade de código não se faz sozinho (a menos que só exista você no projeto)
    • Converse com o seu time e experimentem juntos ( ͡° ͜ʖ ͡°)

Dúvidas?

Obrigada

contato@paulagrangeiro.com.br

paulagrangeiro.com.br

@pgrangeiro

Fontes