Instantly share code, notes, and snippets.

Embed
What would you like to do?

Ruby e Princípios SOLID

Criado por Robert Martin no início dos anos 2000. Cinco princípios que auxiliam na arquitetura de software com o propósito de criar software que atende a três critérios:

  • Tolerância a alterações
  • Facilidade de entendimento
  • Criação de componentes que servem de base para múltiplos softwares

1. O que é Arquitetura?

1.1. Arquitetura ou Ferramentas?

Arquitetura não é qual framework, linguagem, infraestrutura ou banco de dados que uma aplicação usa. Todas essas coisas são tecnologias, apenas ferramentas.

1.2. Arquitetura em construções

Comparamos com arquiteturas de edifícios. Esses detalhes seriam apenas os tijolos, vigas, tubulações etc. A arquitetura trata da organização da estrutura.

1.3. Arquitetura em Software

É possível identificar uma igreja observando a sua planta, por exemplo. Da mesma forma, deve ser possível identificar o domínio de um software olhando sua estrutura.

2. SOLID

2.1 Single Responsibility Principle

Não deve existir mais de um motivo para uma classe ser alterada.

  • Classes definidas com propósito único

Múltiplas responsabilidades em uma classe

class ShoppingItem
  def save(shopping_item)
    # saves shopping_item
  end

  def calculate_item_price
    # calculates shopping item price
  end
end

Uma classe responsável pela interação com o banco de dados e outra pelas regras de negócio

class ShoppingItemDataBase
  def save(shopping_item)
    # saves shopping_item
  end
end

class ShoppingItemEntity
  def calculate_item_price
    # calculates shopping item price
  end
end

2.2 Open Closed Principle

"Classes precisam ser abertas para extensão e fechadas para alterações."

Código precisa ser facilmente alterável. E para isso:

"[...] they must be designed to allow the behavior of those systems to be changed by adding new code, rather than changing existing code." -- Uncle Bob

Quando os requisitos são estendidos e uma mudança enorme é necessária, os desenvolvedores erraram. Esse princípio diminui os riscos da introdução de bugs limitando as alterações ao código existente.

Violação do OCP

class Rectangle
  def initialize(width, height)
    @width = width
    @height = height
  end
end

class Square
  def initialize(size)
    @size = size
  end
end

class ShapePrinter
  def draw_shape(shape)
    if shape.is_a? Square
      # draw square
    elsif shape.is_a? Rectangle
      # draw rectangle
    end
  end
end

De acordo com OCP

class Rectangle
  def initialize(width, height)
    @width = width
    @height = height
  end

  def draw
    # draw rectangle
  end
end

class Square
  def initialize(size)
    @size = size
  end

  def draw
    # draw square
  end
end

class ShapePrinter
  def draw_shape(shape)
    shape.draw
    # [...] code specific to ShapePrinter
  end
end

2.3 Liskov Substitution Principle

Definição de subtipos de Barbara Liskov. Uma função que recebe um tipo A deve funcionar corretamente se receber o tipo B subtipo de A, sem alterações ao sistema.

Para construir sistemas com partes trocáveis, essas partes precisam aderir a um contrato que permita a substituição dessas partes.

class DoPayment
  def pay(billing_information, billing_calculator)
    cost = billing_calculator.calculate_cost(billing_information)
    PaymentStorage.save_payment(cost)
  end
end

2.4 Interface Segregation Principle

Basicamente, uma classe não deve implementar uma interface que não vai utilizar um método.

  • Redeploy
  • Recompilação das dependências de código

Apesar de Ruby ser dinamicamente tipada e sem uma implementação (padrão) de interfaces, não quer dizer que não seja possível retirar algo desse princípio.

2.5 Dependency Inversion Principle

É um dos princípios mais referenciados e fáceis de observar, que é a base também de outros princípios, como o SRP. Em resumo:

  1. Módulos de alto nível não devem depender de módulos de baixo nível.
  2. Abstrações não dependem de detalhes.

Não se deve:

  • referenciar classes concretas voláteis
  • criar "herdeiros" de classes concretas voláteis
  • sobrescrever métodos concretos
class DoPayment
  def initialize(payment_storage)
    @payment_storage = payment_storage
  end

  def pay(billing_information, billing_calculator)
    cost = billing_calculator.calculate_cost(billing_information)
    @payment_storage.save_payment(cost)
  end
end

class PaymentController
  def create()
    payment_storage = PaymentStorage.new()
    DoPayment.new(payment_storage).pay(billing_information, billing_calculator)
  end
end

Rspec.describe DoPayment
  let(:payment_storage_mock) { PaymentStorageMock.new() }
  let(:do_payment) { DoPayment.new(payment_storage_mock) }
end

Conclusão

Esses princípios que baseiam um software robusto, manutenível e testável, podem ser implementados de formas diferentes. A Clean Architecture é uma delas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment