Fork me on GitHub

Keep Learning Conhecimento nunca é o bastante

iPhone/iPad: minhas aplicações favoritas

Simples e rápido, minhas aplicações favoritas para iOS:

  • Camera+: aplicativo para tirar fotos com opções adicionais (como controle de exposição separado do foco) e efeitos de pós-processamento.
  • Hipstamatic: provavelmente a app que lançou a moda de efeitos vintage em fotos. A mais utilizada hoje é o Instagram, mas ainda prefiro o Hipstamatic.
  • 1Password: pra quem utiliza a versão desktop, é uma grande mão na roda pra lembrar senhas quando o celular é tudo que temos à mão.
  • Dropbox: ótimo pra visualizar documentos e fotos.
  • Convertbot: sou fanboy da Tapbots e uso todas as aplicações dos caras. O Convertbot é um conversor de unidades com muitas opções disponíveis e uma UI fantástica.
  • Pastebot: múltiplas áreas de transferência no iPhone, com direito à operações sobre o conteúdo copiado (como efeitos sobre imagens ou tradução de textos). Além disso, com o Pastebot Sync, é possível copiar e colar conteúdos entre Mac e iPhone e vice-versa.
  • Tweetbot: cliente de Twitter da Tapbots. UI sensacional, gestures, filtros, possibilidade de usar listas como timeline principal e várias outras opções.
  • Weightbot: aplicação para acompanhamento de peso, com estabelecimento de metas, cálculo de IMC e gráficos de projeções (pra você ver se está no ritmo certo pra chegar lá).
  • Shazam: está num café e começa a tocar uma música boa, mas você não sabe o nome e/ou artista? Use o Shazam pra identificá-la. Uso bastante com trilhas sonoras de filmes também. :)
  • Kindle: leitor de e-books, se integra muito bem com o Kindle for Mac.
  • Reeder: ótimo leitor de feeds.

Hora dos jogos:

  • Angry Birds e Angry Birds Seasons: clássico. Ótimo passatempo pra longas esperas.
  • Cut the Rope e Cut the Rope: experiments: ótimo jogo que combina elementos de física e habilidade de raciocínio pra resolver os problemas.
  • Contre Jour: uma mistura de Angry Birds, Cut the Rope, Portal e outros jogos, com uma trilha sonora e visuais muito bons.
  • Rage HD: prévia do próximo jogo para consoles da id Software, um shooter muito bem feito.
  • Infinity Blade: provavelmente o jogo com gráficos mais poderosos no iOS, leva o device (e a bateria) ao limite. O jogo em si não é uma obra-prima, mas vale pelos gráficos.
  • Death Rally: corrida + destruição, power ups, jogo em estilo bem clássico com ótimos gráficos e controles.
  • Fruit Ninja: mais um jogo clássico e ótimo pra passar o tempo.
  • Civilization Revolutions: pra quem é fã da franquia, uma versão simplificada do Civ, dá pra fazer um jogo completo em pouco mais de uma hora.

É isso! :)


Gems “locais”, irb e Bundler

Gosto de algumas gems como o wirble, que adicionam algumas funcionalidades ao irb (e, por consequência, ao console do Rails). O problema é que, em projetos que utilizam o Bundler, fica complicado adicionar esse tipo de gem ao Gemfile, pois elas não são realmente dependências do projeto e nem todos gostariam de utilizá-las.

Como programadores costumam ser bem radicais (ainda mais com essa mania de ser “opinionado”), isso sempre gera discussões e atritos. Eu já penso que é mais fácil simplesmente buscar uma solução, e eis aqui uma: https://gist.github.com/1096494.

Colocando esse snippet no seu arquivo .irbrc, ao abrir o irb (ou console do Rails), todas as gems do gemset global do RVM serão colocadas no load path, podendo assim serem requeridas na sequência. Eu não costumo usar gemsets por projeto (uso apenas o Bundler, da maneira explicada nesse post), mas deixo o gemset global com algumas gems para esse fim. Meu arquivo .irbrc está disponível no GitHub.

Essa solução não é lá muito elegante, mas funciona muito bem para gems utilizadas no irb. Há algum tempo propus uma solução na lista do Bundler e recebi algumas sugestões, porém nenhuma me agradou (como, por exemplo, obrigar todo mundo que não quer as gems a usar a flag –without a cada bundle install). A discussão pode ser vista aqui. Até cheguei a fazer um fork do Bundler para implementar algum tipo de metadado para controlar isso, mas o código é bem complicado e acoplado, então deixei pra lá. ;)


SOLID Ruby: Liskov Substitution Principle e Interface Segregation Principle

Para fechar os cinco princípios do SOLID, vamos falar sobre os dois princípios restantes: Liskov Substitution Principle (LSP) e Interface Segregation Principle (ISP).

Como já foi falado anteriormente, esses princípios foram formulados com linguagens estáticas em mente e, por essa razão, precisam ser “adaptados” para que sejam aplicados em linguagens dinâmicas. Note que, em suas formas originais, esses princípios em geral recorrem a técnicas como herança para contornar as “amarras” do sistema estático de tipos.

No caso do LSP temos mais um exemplo disso. Em sua forma original ele é definido da seguinte forma:

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

O que foi “traduzido” para o OOP da seguinte forma:

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

Em Ruby isso não é muito problemático pois, como sabemos, não estamos presos à tipos. O importante, como já vimos com o OCP, é manter a mesma interface, de forma que se precisarmos modificar alguma entidade, as demais entidades que dela dependem não precisem ser modificadas.

Além da interface, também precisamos prestar atenção ao comportamento. Modificações no comportamento podem fazer com que clientes da entidade sofram consequências inesperadas.

Já o ISP é definido da seguinte forma:

Clients should not be forced to depend upon interfaces that they do not use.

Isso quer dizer que um cliente (entidade que depende de alguma outra) não deve depender de interfaces não utilizadas por ele pois, em caso de modificação nessa interface, mesmo que o cliente em questão não a utilize, também terá que ser modificado.

Em linguagens estáticas há uma série de técnicas e artifícios para atingir isso. Em Ruby, o Duck Typing nos entrega esse princípio “de graça”, já que cada entidade depende apenas da interface que utiliza, independente do restante (e de tipos). Apesar disso, devemos sempre buscar entidades com interfaces coesas e bem delimitadas, evitando que sejam muito extensas e genéricas (o SRP se aplica aqui também). Aplicar o design pattern Adapter é uma boa forma de respeitar isso.

Bem, é fácil perceber que os últimos três princípios discutidos (OCP, LSP e ISP) lidam diretamente com formas de garantir interfaces estáveis para atingirmos o nosso objetivo de evitar que mudanças no código gerem um “efeito dominó” e façam com que tenhamos que alterar várias partes do software.

Mas, já que não temos estruturas como Interfaces e classes abstratas para garantir que estamos respeitando a interface definida, como garantir a aplicação desses princípios? Bom, há algumas formas para fazer isso. Você pode “emular” uma interface da seguinte forma:

class MyInterface
  def method_1
    raise "abstract method called"
  end
 
  def method_2
    raise "abstract method called"
  end
 
  # and so on...
end
 
class MyClass < MyInterface
  def method_1
    # do something
  end
 
  def method_2
    # do something
  end
end

Obs: também pode ser feito através de módulos e mixins ao invés de herança.

Isso funciona, mas não é muito o estilo Ruby, certo? A maneira que prefiro fazer é através de specs. Sempre que vou criar um wrapper/adapter ou qualquer estrutura que precise de uma interface estável mesmo quando o código encapsulado for modificado, crio um grupo de “specs de interface” e uso ela para toda estrutura que precise implementar a mesma.

Por exemplo, poderíamos escrever um cliente para o Twitter e escrever nosso código de modo que a gem que consome a API do serviço possa ser trocada sem maiores consequências:

 
# app/adapters/twitter_gem_adapter.rb
class TwitterGemAdapter
  def self.timeline(page=1, per_page=5, since_id = nil)
    options = {:page => page, :count => per_page}
    options[:since_id] = since_id if since_id
 
    Twitter.home_timeline(options)
  end
 
  def self.update(text)
    Twitter.update(text)
  end
 
  def self.reply(in_reply_to_status_id, text)
    Twitter.update(text, :in_reply_to_status_id => in_reply_to_status_id)
  end
 
  def self.retweet(tweet_id)
    Twitter.retweet(tweet_id)
  end
 
  def self.favorite(tweet_id)
    Twitter.favorite_create(tweet_id)
  end
end
 
# spec/support/shared_examples/twitter_adapter_examples.rb
module TwitterAdapterExamples
  shared_examples_for "any adapter for a twitter api gem" do
    it { should respond_to(:timeline)}
    it { should respond_to(:update)}
    it { should respond_to(:reply)}
    it { should respond_to(:retweet)}
    it { should respond_to(:favorite)}
  end
end
 
# spec/adapters/twitter_gem_adapter_spec.rb
describe TwitterGemAdapter do
  context "API contract" do
    subject { TwitterGemAdapter }
 
    it_behaves_like "any adapter for a twitter api gem"
  end
 
  # other specs ...
end

Aqui utilizamos a feature de “exemplos compartilhados” do RSpec, mas isso pode ser feito facilmente em outros frameworks. O importante é que, através dessas specs, garantimos que qualquer adapter implemente a mesma interface. Nesse caso cuidamos apenas disso, mas qualquer comportamento comum também pode ser especificado da mesma forma.

Material recomendado:


SOLID Ruby: Open-Closed Principle

Muitos dos princípios da programação orientada a objetos foram criados com linguagens estáticas em mente. Esse é o caso do Open-Closed Principle, enunciado da seguinte maneira:

Software entities (classes, modules, functions, etc) should be open for extension, but closed for modification.

Originalmente, a ideia é que, uma fez finalizada, uma entidade só poderia ser modificada para correções. Qualquer nova “feature” deveria ser implementada em uma nova entidade, que aproveitaria o código da primeira principalmente através de herança. Isso resulta em reaproveitamento da implementação, mas não garante a consistência da interface.

Um pouco depois, com a introdução e popularização de classes abstratas e recursos de linguagem como Interfaces, o princípio evoluiu e passou a ser aplicado em conjunto com o DIP, fazendo com que o código sempre dependesse de interfaces ao invés de implementações. Com isso, era possível reaproveitar uma interface (closed) e modificar a implementação (open).

Mas e nas linguagens como Ruby, em que abrir uma classe é tão simples como definí-la e pode ser feito a qualquer momento? Será que esse princípio se aplica? Isso sempre é tema de muita discussão e aqui vai minha visão sobre isso.

Bem, com Ruby temos algumas formas de modificar uma classe, como herança, mixins, reabrir a classe ou usar metaprogramação. Perceba que, exceto pela herança, as outras maneiras infringem a ideia original do princípio. Mas, como já vimos, o próprio princípio evoluiu para abraçar mudanças nas linguagens e paradigmas.

O importante, principalmente em linguagens com Duck Typing (que, como já vimos, não dão a mínima para tipos), é que a interface seja fechada. Não importa o modo que você escolher para fazer as modificações, desde que a “superfície de contato” entre as entidades permaneça estável.

No exemplo do post anterior poderíamos modificar as classes que passamos como dependências via parâmetro e utilizá-las sem problemas, desde que a interface permanecesse a mesma. Uma vantagem adicional da tipagem dinâmica é que nesse caso podemos utilizar também herança (modificando o “tipo”) sem precisar modificar o código cliente, que só se importa com a interface.

Dessa forma, podemos modificar um pouco o enunciado do OCP para adaptá-lo à nossa linguagem preferida:

Software entities (classes, modules, functions, etc) should be open for extension, but closed for interface modification.

Recomendo esse vídeo-podcast sobre o assunto: Monkeypatching and the Open-Closed Principle


SOLID Ruby: Dependency Inversion Principle

Continuando os artigos sobre SOLID, vamos falar um pouco sobre o Dependency Inversion Principle. Em resumo, esse princípio diz que os componentes devem depender de abstrações ao invés de implementações.

Bom, isso faz muito sentido em linguagens estáticas como Java, onde há estruturas como Interfaces, classes abstratas e outras parafernalhas. No final, na minha modesta opinião, o código torna-se um espetáculo bizarro de indireção e pode mais confundir do que ajudar se você não for cuidadoso. Mas e nas linguagens dinâmicas?

É comum ouvir que, quando você utiliza uma linguagem que contém Duck Typing (a característica que mais gosto no Ruby), você obtém os benefícios do DIP “de graça”. Isso não é bem verdade. Você os obtém de graça somente se pedir com jeitinho. ;)

O que quero dizer com isso?

Vamos ao exemplo do artigo anterior em sua última versão:

class Game < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :title, :category_id, :description,
                        :price, :platform, :year
end
 
class GamePriceService
  attr_accessor :game
 
  # we could use a config file
  BASE_URL = "http://thegamedatabase.com/api/game"
  API_KEY = "ek2o1je"
 
  def initialize(game)
    self.game = game
  end
 
  def get_price
    data = open("#{BASE_URL}/#{game.name}/price?api_key=#{API_KEY}")
    JsonParserLib.parse(data)
  end
end
 
class GamePrinter
  attr_accessor :game
 
  def initialize(game)
    self.game = game
  end
 
  def print
    price_service = GamePriceService.new(game)
    <<-EOF
      #{game.name}, #{game.platform}, #{game.year}

      current value is #{price_service.get_price[:value]}
    EOF
  end
end

Esse código possui algumas dependências rígidas. Chamamos as classes GamePriceService e JsonParserLib explicitamente dentro de GamePrinter#print e GamePriceService#get_price, respectivamente. Note que, apesar do Duck Typing nos permitir utilizar componentes com uma determinada interface independente de tipo, fizemos com que nossas classes fiquem amarradas à alguns tipos através dessas dependências.

Uma forma de se aproveitar dos benefícios do Duck Typing e, assim, conseguir “de graça” as vantagens do DIP, é tornar nossas dependências transparentes. Enquanto linguagens estáticas costumam se utilizar (embora isso não seja obrigatório) de complexas bibliotecas de injeção de dependência para fazer isso (ah, meus tempos de Java e Spring :D), em Ruby isso é tão simples quanto passar um parâmetro:

class Game < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :title, :category_id, :description,
                        :price, :platform, :year
end
 
class GamePriceService
  attr_accessor :game, :json_parser
 
  # we could use a config file
  BASE_URL = "http://thegamedatabase.com/api/game"
  API_KEY = "ek2o1je"
 
  def initialize(game, json_parser = JsonParserLib)
    self.game = game
    self.json_parser = json_parser
  end
 
  def get_price
    data = open("#{BASE_URL}/#{game.name}/price?api_key=#{API_KEY}")
    json_parser.parse(data)
  end
end
 
class GamePrinter
  attr_accessor :game, :game_webservice
 
  def initialize(game, game_webservice = GamePriceService)
    self.game = game
    self.game_webservice = game_webservice
  end
 
  def print
    price_service = game_webservice.new(game)
    <<-EOF
      #{game.name}, #{game.platform}, #{game.year}

      current value is #{price_service.get_price[:value]}
    EOF
  end
end
 
# Usage example:
game = Game.new(some_game_data)
webservice = GamePriceService.new(game)
game.price = webservice.get_price
 
GamePrinter.new(game).print

Aqui escolhemos passar essas dependências através dos construtores das classes. Dessa forma, ficamos livres da dependência de tipo e passamos a depender apenas de uma interface. Basta que a classe passada no construtor responda aos métodos que utilizamos e não teremos problema. Outra forma seria passar as dependências apenas para os métodos que as utilizam:

class Game < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :title, :category_id, :description,
                        :price, :platform, :year
end
 
class GamePriceService
  attr_accessor :game
 
  # we could use a config file
  BASE_URL = "http://thegamedatabase.com/api/game"
  API_KEY = "ek2o1je"
 
  def initialize(game)
    self.game = game
  end
 
  def get_price(json_parser = JsonParserLib)
    data = open("#{BASE_URL}/#{game.name}/price?api_key=#{API_KEY}")
    json_parser.parse(data)
  end
end
 
class GamePrinter
  attr_accessor :game
 
  def initialize(game)
    self.game = game
  end
 
  def print(game_webservice = GamePriceService)
    price_service = game_webservice.new(game)
    <<-EOF
      #{game.name}, #{game.platform}, #{game.year}

      current value is #{price_service.get_price[:value]}
    EOF
  end
end
 
# Usage example:
game = Game.new(some_game_data)
webservice = GamePriceService.new(game)
game.price = webservice.get_price
 
GamePrinter.new(game).print

É uma maneira melhor para evitarmos construtores muito complexos quando esse for o caso. Uma vantagem adicional é obtida através da utilização de valores padrão para os parâmetros. Desta forma, a não ser que queiramos utilizar alguma dependência não-padrão, as dependências ficam totalmente transparentes.

Mais uma vantagem é no momento de escrever testes. Passando dependências dessa maneira, é muito fácil criar objetos dublês e utilizá-los para facilitar o processo (tornar a execução mais rápida, evitar o uso de webservices reais etc). Um exemplos simples e eficiente pode ser visto aqui. (slides 34, 35 e 36)

Em geral, se você não conseguir substituir uma dependência por um dublê em suas especificações, seu código está muito rígido e pode se beneficiar da aplicação desse princípio.

Leitura adicional:

Make Your Dependencies Translucent with Default Parameters


← Anterior Próxima →