Fork me on GitHub

Keep Learning Conhecimento nunca é o bastante

Flickr
Ver todas »
Nouvel an chinois, Paris 2012La ciudad de la luz IIVisit Tour EiffelParis CatacombsPont RoyalSt-Germain-des-Pres

SOLID Ruby: Single Responsibility Principle

Utilizamos BDD e técnicas do programação orientada a objetos não apenas para obter código mais limpo e bonito. Na verdade, essas são consequências do principal objetivo: criar código que tenha baixo custo de manutenção, isto é, não demande muito tempo e pessoas para correções e melhorias.

Um conjunto de técnicas que podemos utilizar para atingir esse objetivo é chamada de SOLID, um acrônimo que representa cinco técnicas:

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

O objetivo desses princípios é fazer com que alterações necessárias sejam feitas no menor número possível de locais no código. Em outras palavras, é diminuir o custo dessas mudanças através de um design que reduz os efeitos colaterais das modificações.

Nesse artigo vou mostrar um pouco sobre a aplicação do Single Responsibility Principle em Ruby.

Esse princípio nos diz que uma classe deve ter apenas uma responsabilidade e deve executá-la por completo (não devem haver outras classes que executem partes dela). É uma forma de conseguir alta coesão, uma qualidade desejável em código orientado a objetos. Uma classe coesa executa completamente uma responsabilidade, ou seja, essa responsabilidade não fica fragmentada e espalhada entre diferentes entidades no domínio.

Uma forma de pensar sobre o que é uma responsabilidade para facilitar a absorção do conceito é que esta representa uma razão para mudar. Então podemos definir o princípio como: uma classe tem uma e apenas uma razão para mudar.

Vamos a um exemplo de um modelo ActiveRecord que viola esse princípio (alerta para pseudo-código que nem deve funcionar, é apenas um exemplo):

class Game < ActiveRecord::Base
  belongs_to :category
  validates_presence_of :title, :category_id, :description,
                        :price, :platform, :year
 
  def get_official_price
    open("http://thegamedatabase.com/api/game/#{name}/price?api_key=ek2o1je")
  end
 
  def print
    <<-EOF
      #{name}, #{platform}, #{year}

      current value is #{get_official_price}
    EOF
  end
end

Esse modelo tem como sua responsabilidade principal cuidar da lógica de negócio ligada à entidade Game. Porém, como podemos ver aqui, ele também está responsável por consultar um webservice para obter a cotação do jogo e por formatar sua exibição. Essa implementação possui baixa coesão pois essa classe possui mais de um motivo para mudar. Ela será alterada se os requisitos de validação de dados mudarem, se algum detalhe da chamada do webservice mudar ou se precisarmos exibir seus dados numa formatação diferente.

Uma forma de resolver isso seria desmembrar essa classe tendo esse resultado:

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

Assim aumentamos bastante a coesão de nosso sofware. Cada classe possui apenas uma razão para mudar e atende ao SRP.

Um exemplo mais real pode ser visto no modelo Creditcard do Spree. Note que, entre outras coisas, o modelo também é responsável pelos processos de compra e autorização do cartão (métodos purchase e authorize). Note como esse métodos implementam parte do processo e delegam outras partes para outros componentes quando a responsabilidade deveria ser 100% realizada em outra parte. Isso cria baixa coesão (múltiplas responsabilidades por classe) e alto acoplamento (uma responsabilidade dividida por várias classes interdependentes).

Como um exercício a parte podemos pensar sobre até que ponto modelos do ActiveRecord quebram o SRP por serem responsáveis por persistência e lógica de negócios (que inclui validação de dados). Há muitos desenvolvedores dos dois lados nessa questão. Acredito que há pequenas violações mas de uma forma não prejudicial, já que a persistência é implementada por uma classe especializada e apenas herdada em nossos modelos, de forma que essa é delegada ao framework (que cuida de possíveis modificações necessárias à persistência).

É preciso ter em mente que ser muito extremista com esses princípios também pode levar à problemas e a um excesso de preciosismo que torna-se prejudicial.

Bom, a partir daqui vamos para outros artigos onde serão explorados os outros princípios. O pseudo-código utilizado no exemplo acima ainda tem algumas coisas a ganhar através da aplicação de outros princípios.

Leitura recomendada:


[Rant] Menos guerrinhas. Mais princípios

Aviso: o post não é lá a coisa mais coesa do mundo mas, bem, é um rant, então estou despejando pensamentos aqui. :)

É algo muito bom que cada desenvolvedor possua sua “stack” preferida de ferramentas. O apreço por esse conjunto leva à dedicação e ao domínio do mesmo.

Entre nós, que orgulhosamente nos categorizamos como “trabalhadores do conhecimento”, creio que, apesar desse apreço à priori não causar prejuízo, trocar de conjunto de ferramentas deveria ser muito fácil (e uma habilidade desejável) já que o que importa é a transformação do conhecimento da forma bruta (dados) à forma desejada (informação), independente do que foi utilizado para isso. É claro que há sempre a curva de aprendizado numa troca dessas: uma nova linguagem, nova DSL/API etc, mas os fundamentos e princípios se mantém.

Nerds, assim como toda pessoa imersa numa sociedade consumista, se definem pelo que consomem e utilizam. Isso é algo bem comum atualmente e vai desde roupas até computadores, editores de texto, smartphones e, pasmem, à p**** da biblioteca que o cara usa pra fazer parsing de XML. Algo que tornou-se muito comum, principalmente com a facilidade que existe para que cada um publique seus pensamentos (blogs, Twitter e afins), é que ocorram “guerras” sobre esse tipo de coisa, já que, em nossa mente, criticar uma coisa que consumo é praticamente criticar a mim e meu modo de viver.

Entre desenvolvedores isso é bem visível. Além das guerrinhas entre comunidades (.NET x Java, Ruby x Python etc) há também as guerras intra-comunidade, em que escolhas de ferramentas são motivo para disputas de ego intermináveis. O cenário, em geral, mostra uma galera que gosta da ferramenta X e acha que, por isso, a ferramenta Y é uma merda inútil e o pessoal que gosta da ferramenta Y achando o mesmo da ferramenta X.

Passemos a um exemplo num contexto diferente para que pré-disposições não afetem nossas conclusões. Imagine que Gordon Ramsay irá a sua casa para fazer o jantar. Ele não vai levar as facas, panelas e demais utensílios que usa em seu dia-a-dia. Vai utilizar apenas o que tem em sua casa. Dias depois você irá a casa dele e utilizará todos os utensílios profissionais à disposição. Bom, a não ser que você seja um anônimo chef de primeira categoria, sabemos quem irá preparar o melhor jantar, não? (sem piadinhas hein! :P )

Mais um. Quem será que toca melhor: Steve Vai com uma guitarra Giannini de 500 reais ou você com as guitarras ultra-caras dele?

Onde está o conhecimento? Na ferramenta ou no profissional?

Algo bizarro que acontece é ver pessoas que deveriam ter bons conhecimentos sobre lógica confundindo argumento com preferência. Dizer que você prefere X porque acha mais bonito ou mais legal é só sua preferência, não é um argumento para diminuir as alternativas. Chega de “RSpec é mais bonito e moderno, então Test::Unit sucks!” e vice-versa (exemplo aleatório, sem flames por favor).

BREAKING NEWS: se você não sabe patavinas sobre design de código orientado a objeto e uma meia dúzia de princípios básicos, escrever testes em RSpec não vai fazer com que você escreva código melhor do que escrevendo com Test::Unit. Aliás, é bem capaz de piorar as coisas, mas isso é conversa pra outro post. ;)

Em linhas mais práticas, pouco importa para o interessado na informação se ela é processada em PHP ou Ruby, se é formatada em ERB ou HAML ou se está armazenada em MySQL ou PostgreSQL. A ferramenta “certa” é a que faz o trabalho. Sempre há muita discussão sobre isso de “ferramenta certa” e, a não ser que seja algo obviamente errado como usar o Rails pra fazer uma aplicação desktop com controles nativos, o que sempre acaba entrando em jogo é preferência, gosto, estética e etc. Novamente, isso simplesmente não serve como evidência de que algo é superior ou inferior.

“Mas eu sou mais produtivo com X do que com Y!”
“Olha só como meu código fica mais limpo com Z do que com N!”

É, amiguinho. Pra você. Assim como tem muita gente que acredita em divindades criadoras e muita gente que não. Muita gente que acha carros da Porsche bonitos e muita gente que os acha toscos e que os carros da Toyota sim são lindos. Daí a dizer que algum é melhor ou pior é um longo caminho (e, na minha visão, um caminho inexistente).

Pois bem. Por isso creio que, como comunidade, devemos mudar um pouco nosso foco. Tudo bem falar sobre ferramentas, é algo saudável desde que feito com bom senso. Com argumentos, fatos, contexto e harmonia. Não com guerrinhas bobas sobre gosto pessoal e como seu pau é maior que o dos outros sua gem preferida é a mais fodolenta do OMNIverso. É bom para conhecermos alternativas e evoluirmos. Se todo mundo se prendesse ao atual e rejeitasse as novidades, ainda estaríamos perfurando cartões. Enfim, é preciso foco em coisas que realmente fazem a diferença na qualidade. Foco em fundamentos e princípios.

Estatísticas do DataLucas mostram que, a cada dois dias, surgem dezenas de posts falando sobre alguma gem que é a coisa mais maravilhosa já inventada desde o carro voador Toddynho (e que prova que todo o resto é uma merda!), sobre uma miríade de workflows para o VCS da moda, sobre porque a minha biblioteca é mais foda que a sua, sobre como o Agile morreu e, claro, como ele ainda está vivo e funciona muito bem sim senhor!

Legal, todo mundo tem uma opinião sobre o que prefere e sobre o que não gosta. Muito bom, ótimo, sensacional. Mas e o que é imutável? Aquilo que determina qualidade independente do meio utilizado? Já postei bastante essa frase, mas sempre gosto de relembrar:

As to methods there may be a million and then some, but principles are few. The man who grasps principles can successfully select his own methods. The man who tries methods, ignoring principles, is sure to have trouble.

— Ralph Waldo Emerson

Ainda não achei frase que se encaixe mais com meu pensamento sobre desenvolvimento de software. Seja no nível gerencial ou técnico, dominar métodos (ferramentas) ignorando princípios com certeza leva à problema. Manjar de todas as cerimônias e artefatos das metodologias ágeis não vai produzir software de qualidade magicamente. Entender seus princípios, mesmo que você não aplique toda a parafernalha cerimonial, com certeza trará efeitos positivos.

Então fica a pergunta: onde estão os posts e talks sobre princípios? Vamos falar sobre coisas como SOLID (não, não é um besteirol acadêmico), acoplamento, coesão, testes que realmente especificam comportamento, trade-offs arquiteturais e outras questões importantes?

Tentarei fazer a minha parte nisso. Ano passado na RubyConf parte da minha apresentação falou sobre princípios (e outra sobre ferramentas :P ) e penso em continuar com essa temática tanto no blog quanto em futuras apresentações. Gostaria muito que a comunidade valorizasse mais esse tipo de discussão (wishful thinking…).

Já fiz muito dessas guerrinhas, não sou inocente, mas aprendi a valorizar a base, principalmente abrindo a mente (olha a piadinha!) e dando chances à ferramentas diferentes e, com isso, percebendo que em nossa profissão, felizmente, há vários meios eficientes para atingir os resultados desejados.

And… here comes the flames? ;)

PS: se você é do contra e vai bater o pézinho dizendo que preferência e estética são evidências qualitativas, pode desistir e vaza. :D

Leitura recomendada: “A Little Knowledge is a Dangerous Thing


Dica rápida: formatação da saída do Test::Unit com a gem turn

Recentemente voltei a brincar com um pet project para atualizá-lo do Rails 2.3 para o Rails 3. A suíte de testes do projeto foi escrita com Test::Unit e Shoulda e, quando a executei sob Ruby 1.9.2, vi que a saída não estava formatada, isto é, não havia alinhamento, as falhas não ficavam em vermelho e os sucessos em verde. Pelo que me lembro, quando rodava esses testes no Ruby 1.8, a saída era formatada e com cores. Provavelmente algo que foi retirado do Ruby 1.9, talvez na mudança de Test::Unit para MiniTest.

Bem, não me preocupei muito com o motivo disso. Busquei algumas maneiras de formatar essa saída e, entre outras, encontrei a gem turn. Configurei-a no Gemfile do projeto e tudo funcionou perfeitamente de primeira, sem nenhum incômodo. Além da formatação, uma feature interessante é que, ao invés de mostrar pontinhos e letras (ex: …..F…F.), o turn vai mostrando as descrições dos testes e seu status (passou/falhou) à medida que a suite é executada. Feedback bem mais útil.

Veja um exemplo de como fica a formatação através desse link: http://d.pr/mWPe (assim não quebro todo o layout do blog). :)


Mantenha seu MacBook (Core 2 Duo) mais frio com o CoolBook – e aumente a autonomia da bateria

CoolBook é um pequeno utilitário para OS X capaz de modificar frequência e vontagem de operação de processadores Intel Core 2 Duo.

Em geral o processador do meu MacBook opera em temperaturas razoáveis. Porém, quando preciso abrir algum conteúdo em Flash… temperaturas de até 92ºC e fan a 6500 rpm são lugar comum. Uma maneira de resolver isso seria a Adobe criar vergonha na cara é utilizar o CoolBook para fazer com que o processador opere numa voltagem mais baixa.

Um pouquinho de teoria: é óbvio que, para operar, o processador precisa consumir energia. A frequência de clock é a principal determinante de quanta energia será consumida. Quanto maior a frequência de operação, mais energia é necessária. O fabricante do processador estabelece voltagens seguras para o clock de cada processador. Porém, nem todo processador nasce igual: por motivos dos mais variados, alguns aguentam mais tranco que os outros. Processadores de notebooks costumam ser os mais “aptos” a isso, pois funcionam com voltagens inferiores aos processadores de desktops. Por isso é possível brincar um pouco e testar voltagens inferiores à padrão.

Com o CoolBook é possível fazer isso, prática conhecida como undervolting (não é possível fazer overclocking com ele). Algumas vantagens do undervolting são: menor temperatura de operação (que leva a menos barulho de fans), maior autonomia da bateria e maior durabilidade da máquina (devido a menos aquecimento). O aplicativo também permite configurar o throttling, que é o ajuste automático da frequência do processador de acordo com a carga de trabalho. Isso ajuda com o consumo de bateria, desde que as frequências mais baixas utilizem voltagens mais baixas. Utilizar frequência de 1Ghz a 0,9v não economiza nada em relação a uma frequência de 1,5Ghz a 0,9v. Dessa forma, só é algo útil se seu processador não for capaz de rodar em sua frequência máxima com a voltagem mínima.

Ao abrir o CoolBook, são mostrados os pares frequência/voltagem padrão para seu processador:

Note as abas “Adapter” e “Battery”. Através delas é possível estabelecer configurações distintas para quando o notebook está ligado à tomada e para quando está utilizando a bateria.

Ao fazermos as modificações, precisaremos fazer alguns testes de carga para verificar se o sistema se comportará bem. Para fazer esses testes, basta utilizar o CPUTest, com as configurações abaixo:

Quanto mais pesados os testes, maior a “garantia” de que o sistema está estável.

Uma coisa importante é nunca salvar as configurações antes de testá-las apropriadamente. Para isso, sempre utilize o botão Set, nunca o Save. Ao clicar nesse botão, o CoolBook colocara o processador para funcionar na frequência e voltagem selecionadas nos combos (caixas de seleção) acima dele.

O processo então é o seguinte: nas caixas de seleção, escolha a frequência e a voltagem desejadas. Clique em Set. Inicie um teste no CPUTest. Se tudo deu certo, é uma configuração estável (caso contrário ocorrerá um kernel panic e basta reiniciar a máquina). O recomendado é baixar um “degrau” de voltagem por vez e testá-lo bastante. Após isso utilize o botão Add para adicionar o par à janela de configuração.

Em geral, faz-se isso com dois ou três pares de frequências e voltagens e deixa-se habilitado o throttling para que o CoolBook selecione a frequência apropriada para o nível de carga de trabalho atual. Não é preciso deixar a janela do programa aberta. Você pode fechá-la e haverá um pequeno processo do CoolBook rodando em background o tempo todo para ajustar as frequências. O nível de throttling (Low, Medium, High, Very High) define a agressividade do mesmo: quanto mais alto, mais frequentemente será feita a troca de frequência. Também é possível configurar uma temperatura limite que, ao atingida, causará a seleção de pares com frequência e voltagem inferiores. O checkbox B/2 serve para cortar a velocidade de bus à metade para diminuir ainda mais a “sede” do processador. Não utilizo essa opção, então não posso afirmar se é eficiente.

O throttling é algo bem útil, mas deixará um processo rodando o tempo todo, consumindo um pouco de processamento e memória (bem pouco, mas ainda sim consumindo). Se você der mais sorte, conseguirá o supra-sumo da eficiência energética e computacional: configurar o processador para sua máxima frequência e mínima voltagem. Dessa forma terá a máxima eficiência: muitas vezes, diminuir a frequência leva o processador a precisar de mais ciclos para terminar uma tarefa. Assim, poderá consumir mais energia que uma frequência mais alta, mesmo utilizando menos voltagem (daí não fazer sentido manter frequências baixas com voltagens iguais à de frequências mais altas).

Eu tive essa sorte e consegui configurar meu processador (Core 2 Duo L9600 2.13Ghz) da seguinte maneira:

Assim, consegui os benefícios que citei acima. Jogando Madden Superstars, um jogo em Flash bem pesado, as temperaturas batiam em 92ºC e o fan disparava, fazendo bastante barulho. Agora, as temperaturas ficam na casa de 78ºC e o fan só começa a girar alto o suficiente para causar ruído (>4000 rpm) após um bom tempo no jogo, presumivelmente por aquecimento da mesa também, o que diminui e eficiência do resfriamento – se utilizo uma base refrigerada, isso não ocorre. A temperatura “em repouso” (quando não estou fazendo quase nada) caiu para ~45ºC, mesmo em dias quentes. Isso tudo também leva a um ganho de duração de bateria na casa de 10% a 15% (claro, dependendo da carga de trabalho).

Note que, como desabilitei o throttling, não há um processo do CoolBook rodando em background e checando as configurações. Com isso, se reiniciar o sistema, a configuração volta ao padrão (no meu caso, 2128 Mhz e 1,0625v). Para não esquecer desse detalhe e acabar usando o processador com mais voltagem do que o necessário, configurei o CoolBook para abrir automaticamente após o boot do sistema – dessa forma, basta clicar em Save logo após o carregamento do sistema e o par frequência/voltagem será aplicado. Como é bem raro precisar reiniciar, não chega a ser um incômodo.

Como a explicação textual não é lá a mais eficiente, se ficou com alguma dúvida, visite a página de vídeos do CoolBook, que mostra esse processo.

Aviso: apesar de underclocking e undervolting não serem considerados arriscados como overclocking e overvolting, não me responsabilizo por problemas decorrentes do uso do CoolBook ou aplicativos semelhantes.


Uma frase para complementar o último post…

As to methods there may be a million and then some, but principles are few. The man who grasps principles can successfully select his own methods. The man who tries methods, ignoring principles, is sure to have trouble.

— Ralph Waldo Emerson

Substitua “método” por “linguagem” e isso resume muito bem o meu pensamento.


← Anterior Próxima →