Design Patterns em Scala – Parte 1: The Observer


Todo programador moderno, hype, antenado, ligado nas tendências, curte mesmo um Design Pattern.

Aliás tudo que tem Design no nome fica chique né? “Design de Sombrancelhas”, “Hair Designer”. Mas enfim…

Design Patterns é de fato um assunto bastante interessante, e o conhecimento de alguns desses padrões de design pode facilitar bastante o desenho de soluções.

Nesse post não pretendo ficar explicando a fundo os Patterns mais conhecidos no mundo Java, tem muita coisa já escrita por aí.

Como o foco principal desse blog é a linguagem Scala, vamos dar uma olhada em como alguns Patterns manjados em Java ficam ainda mais interessantes em Scala. Em seguida, vamos ver alguns Patterns novos para Scala.

Singleton

object MySingleton {
 // Código vai aqui
}

Singleton foi elevado ao cargo de construção de linguagem! Usando a palavra-chave “object” estamos criando uma instância única de uma classe.

No exemplo acima estamos ao mesmo tempo declarando uma classe e instanciando um singleton para ela.

Um singleton pode naturalmente estender classes e implementar Traits.

// Nem preciso de corpo se eu não quiser
object MySingleton extends MyClass with MyTrait 

O ministério da saúde dos programadores adverte: Singleton faz mal aos testes. Use com consciência.

Observer

Como vocês já sabem, nesse padrão temos dois personagens principais: o Observer, e o Subject.

O Observer é o objeto que está interessado no que o Subject está fazendo, e o Subject é gentil em notificar os Observers quando algo nele muda.

Primeiro, vejamos o jeito mais conhecido, java-like:

trait Subject {
  private var observers: List[Observer] = Nil

  def addObserver(observer: Observer) = observers = observer :: observers

  def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

trait Observer {
  def receiveUpdate(s:Subject)
}

// Olha mãe, sou um Singleton
object MyObserver extends Observer {
  def receiveUpdate(s:Subject) {
    println("O subject mudou!")
  }
}

// Olha mãe, eu nem preciso de um corpo
object MySubject extends Subject

// Testa tudo isso aí
MySubject.addObserver(MyObserver)
MySubject.notifyObservers

Criamos uma Trait para o Subject e o Observer e um exemplar concreto para cada um apenas para ver funcionando.

Uma Trait é como se fosse uma interface Java que também pode ter campos concretos. Uma classe consegue “estender” múltiplas Traits.

Só que em Scala eu consigo deixar as coisas mais interessantes. Vamos dizer que você não queira uma interface para os Observers. Que tal se qualquer classe que tiver o método receiveUpdate(s:Subject) pudesse ser um Observer?

Podemos usar Structural Typing para isso, e jogar a Trait Observer fora:

// Subject agora aceita qualquer observer que se "encaixa" no type definido
trait Subject {
  // Adicionei esse Type Alias para dar um nome a esse tipo estrutural.
  type Observer = { def receiveUpdate(s:Subject) }

  private var observers: List[Observer] = Nil
  def addObserver(observer: Observer) = observers = observer :: observers
  def notifyObservers() = observers.foreach(_.receiveUpdate(this))
}

// Não estende nada
object MyObserver {
  def receiveUpdate(s:Subject) {
    println("O subject mudou!")
  }
}

// O resto é igual
object MySubject extends Subject

MySubject.addObserver(MyObserver)
MySubject.notifyObservers

Structural Typing é parecido com Duck Typing, se o objeto tiver aquele método, ele é chamado. Por baixo dos panos o compilador resolve tudo usando Reflection, e por isso talvez tenha um impacto na performance do código.

Agora, uma instância de qualquer, QUALQUER classe que possua o método receiveUpdates(s:Subject) pode se adicionado como um Observer.

Legal, mas podemos ir além! E se eu não quiser que meu Observer seja uma instância de uma classe? Que tal se ele fosse apenas uma função?

Sinta o poder:

trait Subject {
  // Type Alias novamente. O tipo Observer é qualquer função que recebe Subject como parâmetro e não devolve nada (Unit é tipo void).
  type Observer = (Subject => Unit)

  private var observers: List[Observer] = Nil

  def addObserver(observer: Observer) = observers = observer :: observers

  // Mudei aqui para executar a função ( .apply aplica uma função )
  def notifyObservers() = observers.foreach(_.apply(this)) 
}

// Olha mãe, sou uma função!
val updateHandler = (s:Subject) => println("O subject mudou!")

object MySubject extends Subject

// Testa
MySubject.addObserver(updateHandler)
MySubject.notifyObservers

Type Alias é uma construção que permite que você defina um tipo. Isso mesmo, você declara um tipo e dá um nome para ele. Por exemplo, você pode dar um nome diferente para o tipo String:
type MinhaString = String.
Isso é bastante útil com Structural Typing.

Eu sei, eu sei, foi uma viagem e tanto. Se você nunca tinha experimentado nada do mundo funcional, esse aqui foi um pequeno exemplo.

Isso aí, vimos como em Scala podemos escrever o Observer de maneiras diferentes, tornando-o mais flexível.

Se o seu Observer não puder por algum motivo implementar uma interface, ou estender uma classe específica, podemos usar Structural Typing!

Se você não quer criar uma classe só para ser um Observer, e quiser só passar uma função de callback para seu Subject, agora você também pode de maneira simples e concisa. (em Java até daria para fazer uma classe anônima, mas convenhamos que assim é 3430943498 vezes mais bonito).

No próximo capítulo!

Arquitetura e Decoração com o Decorator

E não, não estamos falando sobre jardim, sofás, paredes e decoração de interiores…

Anúncios

2 comentários sobre “Design Patterns em Scala – Parte 1: The Observer

  1. Excelente post, como sempre.
    Apenas um comentario sobre Reflection. Ja fiz uns testes de desempenho em Java e percebi que Reflection é extremamente rapido. Existem tantos outros gargalos numa aplicaçao que da pra usar Reflextion sem dó.

Os comentários estão desativados.