segunda-feira, junho 02, 2008
terça-feira, maio 20, 2008
Princípios de OO segundo Alan Knight (e Dilbert) (parte 1)
(nossa como faz tempo que não posto... :p)
Faz uns anos eu achei um artigo artigo obscuro escrito por Alan Knight por volta de 2000 ou 2001 que é um excelente resumo de como deve ser um bom código orientado a objetos. O mais interessante é que o artigo faz analogia aos princípios "Dilbert", e por incrível que pareça, a analogia encaixa como uma luva.
O texto é curto e muito divertido, recomendo a leitura!
Como sempre mostro esse artigo pra todo mundo que eu conheço, achei que já era hora de citar ele no blog e fazer um resumo em português pra quem não conhece bem o gringolês. Como posts grandes ninguém lê, vou postando os princípios aos poucos.
Nota: AS IDÉIAS AÍ EMBAIXO NÃO SÃO MINHAS, OKAY? EU GOSTO MUITO DELAS, MAS O AUTOR ORIGINAL É O "TIUZÃO TOP FODA" ALAN KNIGHT!
Avisados? Ótimo! Senão depois o TaQ briga comigo por estar levando crédito por idéias alheias :).
Princípio 1: Nunca faça nada que alguém possa fazer para você.
Esse é basicamente o princípio do "tell, don't ask". Basicamente, objetos não podem ser caras legais. Nada de "Olha, preciso do total dos custos desde o começo do mês, mas não se incomode, me dê os dados aí que eu mesmo faço o cálculo, não deve ser muito complicado, não é?"total = centro_de_custo.itens.inject(0) do|soma, item| if item.data > Date.parse('2008-05-01') soma += item.valor else soma end endObjetos devem ser grandes FDPs. Objeto bom é grosso como seu chefe: "Cara, quero o total dos custos desde o começo do mês! Não quero nem saber e não me interessa como você calcula isso, quero o resultado, e quero JÁ!".
total = centro_de_custo.total_desde('2008-05-01')Em Java, vejo muito uma coisa um pouco mais esquisita, normalmente envolve um DAO, injeção de dependência e costumeiramente um Date que não se pode construir diretamente pois o construtor está "deprecated":
// getCentroDeCustoDAO foi alimentado antes pelo Spring // e "new Date" costuma ser gerado via Calendar, pq esse // construtor está deprecated faz tempo... ListMesmo que você goste muito desse padrão, seria um pouco melhor do jeito abaixo. Embora já tenha ouvido que isso é regra de negócio e não deveria ser feita no DAO.- itens = getCentroDeCustoDAO().procurarPorDataDesde(new Date(2008, 4, 1)); BigDecimal resultado = new BigDecimal(0); for(Item item : itens) { resultado = resultado.add(item.getCusto()); }
// getCentroDeCustoDAO foi alimentado antes pelo Spring // e "new Date" costuma ser gerado via Calendar, pq esse // construtor está deprecated faz tempo... BigDecimal resultado = getCentroDeCustoDAO().calcularCustoTotalPorDataDesde(new Date(2008, 4, 1));"Tell, don't ask" <= "Mande, não peça"...
segunda-feira, março 17, 2008
Avanço tecnológico às avessas...
Não sei se porque ando lendo coisas subversivas mas ando com a nítida impressão que estamos andando em círculos ao invés de fazer avanços na área de software...
Alguns fatos me deram essa impressão:
1 - "Redescoberta" de closures -> coisa que antes do hype do Ruby ninguém dava bola, e agora é a discussão do momento. Só que isso existe desde 58 com LISP. Ou, se vc quiser uma roupagem mais moderna, desde 78 com Smalltalk. (Clipper tb tinha, alguém lembra?)
2 - Domain Driven Development -> não entrei muito a fundo, mas me parece basicamente o que o pessoal do movimento O.O. de 1970 e uns quebrados pregava.
3 - TDD -> okay, mesmo o Kent Beck confessava que isso não era novidade.
4 - Uma entrevista com John McCarthy (criador do LISP) na InfoQ -> Tem uma pergunta quase no final da entrevista sobre LISP sendo citado como influência para novas linguagens:
"...many modern programming languages like Ruby are claiming big influences from Lisp..."
McCarthy confessa que não conhece Ruby, mas pergunta:
"Does it use, for example, list structures as data?"
O entrevistador responde negativamente, e McCarthy completa:
"So if you want to compute with sums and products, you have to parse every time? So, in that respect Ruby still isn't up to where Lisp was in 1960."
Interessante...
Existem alguns outros pequenos fatos aqui e ali, pequenos demais pra eu estar totalmente consciente deles, mas ando sentindo um "deja vu" desgramado. Será que sei pouco e na verdade há um avanço nessas coisas que não estou percebendo ou será que sei o suficiente e realmente estamos andando em círculos em algumas coisas?
Como eu sempre assumo que não sei o suficiente, a questão agora está em saber o que não estou percebendo. Toca estudar mais... :)
quarta-feira, março 12, 2008
Sobrecarga de métodos em Ruby
Uma das coisas muito legais que o Java possui é sobrecarga de métodos (várias vezes confundido com polimorfismo) que é a capacidade de possuir métodos diferentes com mesmo nome, mas com parâmetros diferentes.
Bom, isso não existe em Ruby, mas nada impede que isso seja implementado. Rascunhei isso aqui:
class Object
def self.generate_method_name_for(method_name, *parameter_types)
# Usamos espaços no nome dos métodos gerados para evitar chamadas acidentais
"#{method_name}_#{parameter_types.collect{|it| it.to_s}.join(' ')}".to_sym
end
def self.def_method(method_name, *parameter_types, &block)
# Definindo o método genérico da classe,
define_method method_name.to_sym do |*params|
send("#{method_name}_#{params.collect{|it| it.class.to_s}.join(' ')}", *params)
end unless self.method_defined?(method_name.to_sym)
#Definindo cada método específico
specific_method = generate_method_name_for(method_name, *parameter_types)
define_method(specific_method, &block)
end
end
E para usar: class Test
def_method :test, String, String do |a, b|
puts "Duas strings"
end
def_method :test, String do |a|
puts "Uma string"
end
def_method :test, Fixnum do |a|
puts "Um número"
end
end
x = Test.new
x.test("a", "b")
x.test("a")
x.test(1)
É lógico que isso custa desempenho, mas serve. Acho também que o código poderia ser grandemente melhorado, até pq não sei se isso funcionaria corretamente com herança e polimorfismo, mas não quis perder mais tempo brincando nisso :) Moral da história: adoro classes abertas.
def self.generate_method_name_for(method_name, *parameter_types)
# Usamos espaços no nome dos métodos gerados para evitar chamadas acidentais
"#{method_name}_#{parameter_types.collect{|it| it.to_s}.join(' ')}".to_sym
end
def self.def_method(method_name, *parameter_types, &block)
# Definindo o método genérico da classe,
define_method method_name.to_sym do |*params|
send("#{method_name}_#{params.collect{|it| it.class.to_s}.join(' ')}", *params)
end unless self.method_defined?(method_name.to_sym)
#Definindo cada método específico
specific_method = generate_method_name_for(method_name, *parameter_types)
define_method(specific_method, &block)
end
end
E para usar: class Test
def_method :test, String, String do |a, b|
puts "Duas strings"
end
def_method :test, String do |a|
puts "Uma string"
end
def_method :test, Fixnum do |a|
puts "Um número"
end
end
x = Test.new
x.test("a", "b")
x.test("a")
x.test(1)
É lógico que isso custa desempenho, mas serve. Acho também que o código poderia ser grandemente melhorado, até pq não sei se isso funcionaria corretamente com herança e polimorfismo, mas não quis perder mais tempo brincando nisso :) Moral da história: adoro classes abertas.
segunda-feira, janeiro 14, 2008
O que é preciso para DSLs?
(Se vc não sabe o que é DSL, Akita explica... se quiser uma discussão aprumada e ajeitada sobre o assunto, dá uma olhada no que diz o tio Ronaldo)
Alguns dizem que Smalltalk, Ruby e Lisp são boas linguagens para criar DSLs. Mas Java, C# e afins não são tão boas? Por quê?
Depois de ler um tanto e experimentar mais outro tanto, acredito que para criar DSLs internas (veja o que diz Martin Fowler sobre DSLs internas e externas), uma linguagem precisaria dos seguintes items:
- Reflexão(de verdade), ou seja, capacidade de inspecionar e alterar suas próprias definições em tempo de execução - DSL normalmente envolve criar e inspecionar código, e sem reflexão decente não dá pra fazer metaprogramação
- Metaprogramação, ou seja, código que gera código - Muito da DSL na verdade é fazer com que pouco código faça muita coisa, às vezes de maneiras pouco convencionais. Para isso sua linguagem precisa conseguir criar código "on the fly" sem grandes maracutaias, porque afinal, você já vai estar fazendo maracutaias, aí a metaprogramação fica maracutaia ao quadrado :p
- Closures (ou lambdas) - Permitir passar blocos de código pra cima e pra baixo são essencias para você codificar em um ponto e poder executar em outro, normalmente após preparar o terreno para a execução da closure
- Sintaxe flexível - Não é essencial, mas ajuda a vc criar uma DSL simples. Sintaxes rígidas limitam a maneira com a qual você pode se expressar nas DSLs e elas acabam ficando mais complicadas sem necessidade. Um exemplo desse princípio é a tal da "interface fluente". Sem um sintaxe flexível, ela fica meio canhestra.
- Classes abertas - Isso é uma extensão da "Reflexão", na verdade é permitir que você possa redefinir tipos básicos como String, Integer e outros. Se você olhar a tal da "interface fluente" vai ver que com classes abertas ficaria bem mais fácil criá-las
quinta-feira, janeiro 10, 2008
Desgaste de informação
Hoje me deparei com uma situação que todo mundo passa, conhece, mas parece que ninguém toma medidas para conter o problema: quanto mais gente no meio entre a fonte de informação e o utilizador (EU!), pior a informação vai ficando.
Cliente pede uma feature -> Relacionamento com cliente repassa para Gerente de projeto -> Gerente de projeto passa para Analista -> Analista passa para desenvolvedor.
Nesse caminho, o que o cliente pediu já foi interpretado de maneiras diferente QUATRO vezes (Relacionamento, Gerente, Analista e Desenvolvedor). Em cada interpretação a informação sofreu um certo desgaste, detalhes se perderam e o pior: detalhes que podem ser irrelevantes foram adicionados.
Esse é de longe o maior problema da informática, depois da comunicação falha (que é relacionada, mas não é a mesma coisa). Alguém sabe se existe alguma área que estuda o que acontece com a informação?
Assinar:
Postagens (Atom)