[i]
Patterns são sinais de fraquezas nas linguagens de programação.
Quando identificamos e documentamos uma, este não deve ser o fim da história. Em vez disso, nós devemos ter o objetivo de longo prazo de entender como melhorar a linguagem de tal forma que o pattern se torne invisível ou desnecessário.
[/i]
Ah sim, para os trolls de plantão, detenham-se nos conceitos apresentados no texto.
Proteu, alguns seguimentos de raciocínio do autor se baseiam em suposições que na realidade ocorreram de forma diferente na história da computação e ainda despreza o fato de que muitos patterns servem para qualquer linguagem. Por exemplo: adapter, chain of responsabilities e proxy podem ser usados entre sistemas usando linguagens diferentes, inclusive Fortran e Cobol. Quem trabalha com EDI já usa Adapter muito antes de saber que o que ele fazia podia ser chamado de adapter.
Dizer que em C se pode fazer orientação a objetos é esquecer totalmente as diferenças entre as necessidades de um compilador C e de um compilador C++, que no mínimo precisa de mais tabelas de símbolos.
O Bjarne Stroustrup nunca tentou fazer isto pelas óbvias dificuldades. Ele sempre sonhou com um Simula com features do C e em suas primeiras tentativas já criou um simulador que não era Simula. Em 1979, nos primórdios do C++, ele criou o C com classes que também não era C.
Padrões evoluem e o uso as linguagens também. Por exemplo em C todo mundos adorava usar vadriáveis globais. Hoje em Java quase ninguém usa. Mas em Ruby pode ser que haja há gente que usa. Isto seria sinal de fraqueza da linguagem?
Proteu, alguns seguimentos de raciocínio do autor se baseiam em suposições que na realidade ocorreram de forma diferente na história da computação e ainda despreza o fato de que muitos patterns servem para qualquer linguagem. Por exemplo: adapter, chain of responsabilities e proxy podem ser usados entre sistemas usando linguagens diferentes, inclusive Fortran e Cobol. Quem trabalha com EDI já usa Adapter muito antes de saber que o que ele fazia podia ser chamado de adapter.
[/quote]
Bom, quanto aos patterns que servem pra qualquer linguagem, o texto exemplifica o MVC e a dificuldade (impossibilidade?) de uma linguagem absorvê-lo completamente. Isso de fato enquadra o padrão como algo que ganhou vida independente. Concordo contigo, não sei se uma linguagem conseguiria introduzir um conceito que tornasse o MVC dispensável.
Daí eu concluo que uma linguagem evolui em conceitos (de C para C++, por exemplo), no caso de um código fazer gambiarras para suprir uma necessidade porque desconhece um conceito mais elegante de estruturar sua solucão. Daí que mudancas na infra-estrutura de um compilador para absorver esse conceito são se fato necessárias (como voce mesmo falou, mais tabelas de simbolos, por exemplo).
De fato, penso que mais dificil do que introduzir um conceito numa nova linguagem é você vê-la funcionando plenamente.
Seguindo a linha do texto, o mecanismo de heranca era um pattern (meio capenga e criado do zero sempre que necessario) para quem usava a linguagem C. Hoje em dia acho dificil que um desenvolvedor enxergue que no passado heranca foi um padrao que foi incorporado numa linguagem (no caso de C, para C++).
Pessoas usam heranca naturalmente, nao precisam pensar nela necessariamnte como pensamos hoje em patterns.
Bom, de um modo geral, quantos patterns temos catalogados para linguagens orientadas a objetos que estariam eleitos a serem completamente absorvidos por uma linguagem nova?
Acho interessante pensar que as linguagens de programacão evoluem em funcão de necessidades, e pensar em incorporar patterns (que possam ser incoporados, logicamente) me parece um bom caminho a seguir.
Bom, mas isso é um trabalho para projetistas de linguagens, não é mesmo?
Nem foi à toa que não colocaram o mecanismo de ponteiros (e sua aritmética) na linguagem java. Sempre tinha que se preocupar em fazer aquela velha tarefa repetitiva e propensa a falhas de alocar e desalocar memoria, dentre outras obscuridades perigosas de se trabalhar com ponteiros. Por que não pensar que isso também não se enquadra num pattern absorvido por uma linguagem evoluida?
O Akita (e os posters originais) começa bem mas peca em querer tirar uma conclusão fácil.
Design Patterns não são ‘fraquezas’ nem ‘defeitos’ em linguagens. Patters são simplesmente soluções catalogáveis para problemas recorrentes.
Dizer que uma linguagem criada hoje de madrugada precisa já implementar nativamente os padrões X, Y e Z que são utilizados o tempo todo tudo bem, mas não dá pra simplesmente dizer que se uma linguagem precisa de patterns é para compensar fraqueza.
O problema é que existem patterns que são intrisnicamente apenas isso, soluções. Bridge, Façade, a já citada pelo Luca Chain of Responsibility, Visitor, Mapper, Flyweight e vários outros não fazem sentido em serem implementados pela linguagem. A menos que seja uma DSL. Uma DSL é especializada para o domínio em questão e pode incluir os patterns mais utilizados.
O ponto é que linguagens genéricas como Java nunca vão ter todos os patterns implementados dentro de si, até pelo seu amplo escopo.
Recomendo ao autor a leitura de Jack Greenfield, autor do Software Factories, que é partidário de que uma linguagem é tão moderna quanto os patterns embarcados nela. Este inclusive é um argumento martelado no livro o tempo todo para dizer que C# é mais moderno que Java.
Bom, é fato que patterns como os citados estruturam-se a partir de um problema real e uma linguagem de proposito geral não é desenhada para atingir propósitos específicos.
Shoes, você falou em DSL, e numa discussao sobre isso na RubyOnBr foi mencionado que é viável termos uma DSL que expande a linguagem ao invés de ser criada do zero:
Temos API + linguagem criadas a partir de uma expansão. Acho que não tem muito a ver com patterns, mas esse tipo de coisa me lembra o suporte a script (Groovy, JRuby?) planejado para java. Essas linguagens dinamicas acabam atingindo um nicho específico de forma mais efetiva do que se a linguagem de propósito geral estivesse sozinha na parada
Já me aproveitando de vocês que são mais experientes, tenho uma dúvida: pensando nessa proposta de expansão de DSL’s é possível que alguns desses patterns poderiam ser plugaveis a uma linguagem de proposito geral sem que tenhamos que ficar reimplementando o pattern sempre que ele for adequado para o caso?
Resumindo o escopo para nossa discussão, as vezes uma DSL que expande a linguagem é boa, te da mais poder e praticidade para tarefas específicas do seu domínio, mas muitas vezes o que você quer com uma DSL é exatamente diminuir as possibilidades do programador.
Exemplo? Imagine o que os DBAs fariam se SQL fosse uma linguagem para qualquer propósito em vez de uma DSl apra álgebra relacional. Teríamos consultas que abririam sockets e resviriam seus resultset em HTTP :S
Vamos jogar mais lenha na fogueira. Artigos como o de Mark Dominus, que eu traduzi no meu blog, não tem a pretensão de serem teses de doutorado. Muito pelo contrário, são palavras de provocação. Não no sentido pífio de simples ofensas, mas no sentido de forçar o leitor a repensar “era isso mesmo que eu estava vendo?”. Inclusive, recomendo ler os outros artigos e apresentações de Mark, ele apresenta de maneira muito didática assuntos como lambda calculus. Originalmente ele parece ser mais presente na comunidade Perl, mas isso não impede que rubistas ou javeiros entendam os conceitos. “Ciência” da Computação é muito mais do que o brand X ou Y.
O autor apenas alerta que existem duas visões para “Design Patterns”: a atual, como continuação do trabalho mais famoso, do GoF, ou o original, mais filosófico de Christopher Alexander, em gerar uma “linguagem comum” para os problemas. Essas duas abordagens são diferentes (embora a primeira se diga uma derivada da segunda) mas podem se complementar.
Um caminho (ainda incompleto) é o MVC implementado como DSL em Ruby, na forma de Rails. Ou o próprio ActiveRecord de Rails. São patterns informais mas que, para todos os efeitos, quase se tornaram invisíveis. É uma funcionalidade de linguagens dinâmicas como Ruby permitir a criação de DSLs que quase se “mesclam” à linguagem em si.
Mas já que tocaram também no assunto da evolução de C++, Lisp, recomendo a leitura do livro “The Design and Evolution of C++”, escrita pelo próprio Bjarne Stroustrup. É onde ele explica porque certas coisas no C++ são como são. Ele se extende desde o primeiro “C with Classes” em 1979 até a padronização ANSI/ISO em 1994. De fato, uma de suas fontes de inspiração foi o próprio Simula, a primeira linguagem importante inspirada nas escolas de Turing e Church, C e Lisp. Mas cuidado, trata-se da visão exclusiva de Bjarne, que não deve ser julgada nem certa nem errada, apenas foi sua visão particular, da mesma forma como Niklaus Wirth, Anders Hejlsberg, James Gosling, Larry Wall ou Matz tem as suas.
Em resumo: Design Patterns, como conhecidos hoje, não são os fins da linha. Não deixam de ser muito úteis no presente, mas podem ser aprimorados para o futuro. Muitos já se levantaram para fazer exatamente isso.
Não li (ainda, maldita Amazon!) Alexander mas no sentido de ‘pattern language’ acho que você está certo.
Entretanto, não encaro isso como uma limitação da linguagem (seja Java, Ruby, perl ou ZahlScript.Net) mas simplesmente como um problema de linguagens de uso geral.
Java é uma linguagem de uso geral, alguns conceitos mais globais como Thread e Observers são integrados (ou quase) mas cosias específicas como Datamappers/DAOs ou MVC não fazem sentido.
O mesmo em Ruby. Rails extende a linguagem criando construtos para seu domínio (coisa que em Java é um parto e mais sete velas pra fazer) mas ter isso dentro da linguagem Ruby é algo uim, pois ela não está limitada a MVC+AR.
Bom, então o que se pode desejar é uma linguagem extensível (como Ruby e as novas versões de Java e C#) que permita aplicar estes ‘themes’ em ocasiões adequadas (DSLs internas).
O que fazer se inserissemos um Pattern, que em um momento fosse realmente um Pattern e que num futuro se torna-se um Anti-Pattern? Concordo que as linguagens em alguns pontos devem evoluir para absorver determinados patterns que se tornem commodities, porém sempre vão existir patterns e surgir novos patterns mesmo que esses subam seus níveis de abstração.
Akita, bem vindo ao GUJ. Aqui você vai encontrar muitas discussões envolvendo patterns, OOP, Ruby, Java,etc.
Eu entendi claramente a intenção de provocar um debate de alto nível. Aliás, se tivesse considerado como guerra de tecnologias nem me meteria.
Adoro uma boa discussão fundamentada em razoável raciocínio lógico mesmo na base de sofismas. O artigo em questão não é totalmente falso mas foi escrito usando algumas premissas colocadas com uma certa estratégia de levar o raciocínio do leitor para a conclusão que ele gostaria.
Philip
Eu, infelizmente por sérios e irreversíveis problemas de DNA, vivenciei a evolução das principais linguagens. Acho que entendo a motivação por trás do surgimento de algumas delas. Muitas foram criadas para atender determinados tipos de problemas sem que fossem chamadas na época de DSL. Depois, por teimosia da comunidade desenvolvedora, as linguagens acabaram extendidas para linguagens de uso geral e consequantemente receberam críticas.
PS: Pelo visto o Akita conhece o histórico livro do Stroustrup que citei no comentário do blog.
Eu conheci o blog do Mark pelos textos sobre matemática que ele frequentemente publica (foi via Reddit, eu acho). E essa última resposta dele parece típica de um argumento feito por matemático*. O prof Johnson argumenta que incluír patterns nativamente em linguagens de programação detrai da simplicidade da linguagem, e que, portanto “there will always be things that are not in the language. These things will have to be patterns”. O Mark contra-argumenta contextualizando a idéia de falha numa linguagem: “when pattern P applies to language L, then, to the extent that some programmer on some project finds themselves needing to use P in their project, the use of P indicates a deficiency in language L for that project”.
Eu acho que os design patterns representam sim um defeito no programador, e não na linguagem. O simples fato de existir DP demonstra que ainda tem gente que procura uma receita para tudo que faz. Conheci muita gente que não tinha o mínimo de idéia do que era um DP mas quando desenhava uma solução parecia que o cara tinha seguido a cartilha GoF.
Não to dizendo que patterns não servem para nada, mas hoje as pessoas estão maravilhados em desmerecer o design das linguagens. Como eu já falei anteriormente, linguagens de programação são iguais a linguagem falada, evoluem de uma forma culta para uma forma popular mas mesmo assim existem regras de formação, gramática, não se tem como fugir disso. Uma analogia eu vejo DP como um esperanto (ou o que o esperanto deveria ter se tornado ) uma forma de comunicação universal, seja qual linguagem você esta usando se você ver um pattern você vai reconhecê-lo.
Bom, desculpa, acho que me expressei mal. Quando me referi a expandir uma linguagem foi em relação às suas possibilidades de plugar um pattern quando ele for necessário ou não. Estando ele conectado ao seu devido ponto de execução, ele assumiria o comportamento restrito que você mencionou no seu comentário.
Tem um exemplo escabroso que vi em ruby que é o Magic Models (o site é feio pra caramba, mas releva aí hehe) que cria relacionamentos e validações sem você precisar escrever nenhuma linha de código.
O que você precisa fazer é colocar a linha no enviroment.rb
require 'dr_nic_magic_models'
Que sua aplicação assume o comportamento descrito pelo plugin. Sei que isso é uma facilidade que a linguagem propicia (não uma DSL), o que questionei é se as DSLs poderiam assumir essa mesma postura.
Enfim, acho que esse comportamento “DSL plugável” nos aproxima ainda mais do conceito de Humane Interface
ArrayList l = new ArrayList();
for (Object object : l) {
}
senão o pattern Iterator incorporado à sintaxe da linguagem Java em sua versão 5.0? E não deixou de ser uma linguagem de propósito geral…
Eu acho o termo defeito forte demais. Sou partidário da opinião de que se um design pattern se fez necessário, é porque sua linguagem não tem um nível de abstração alto o suficiente (daí a “defeito” há uma enorme distância).
Só um detalhe… por que chamam o Rails de MVC? O MVC clássico usa o pattern Observer entre Model e View, e isso não se aplica ao ambiente web, entrecortado por requisições http. O rails seria “Model 2”, não?
No fundo, não existe nenhum framework para Web, seja em Java, Ruby, C# ou qualquer outra plataforma que possa ser academicamente chamada de “MVC”. Isso pela própria incapacidade de uma View Web de conseguir manter seu próprio estado e de receber mensagens. Existem algumas adaptações (até mesmo fazer um timer em javascript para buscar alterações de estado do servidor), mas nada próximo do MVC clássico. Isso por si só não chega a ser necessariamente ruim, apenas os requerimentos Web são diferentes dos requerimentos Desktop. Por isso a Sun adaptou sua própria versão e chamou de “Model 2”. Poderia ter qualquer nome. Na prática, separando Model, Controller e uma View/Template é suficiente, ou seja, não fazendo o jeito “clássico” de ASP ou PHP, com a macarronada misturada toda na mesma página, é suficiente.