Embora Eu adoro cordas, às vezes o String
pode lhe dar um desgosto. Por exemplo, em C#, não existe a classe String.Left()
function. É justo; podemos arregaçar as mangas e escrever nossa própria função rapidamente:
public static string Left(string s, int len) { if (len == 0 || s.Length == 0) return ""; else if (s.Length <= len) return s; else return s.Substring(0, len); }
E chame-o assim:
var s = "Supercalifragilisticexpialidocious"; s = Left(s, 5);
Bastante indolor, certo?
Mas, com o advento do C# 3.0, há uma maneira ainda melhor — o senhor pode usar o C# 3.0 para fazer isso. métodos de extensão. Com um método de extensão, “estendemos” o String
para adicionar a função que falta. O código é bastante semelhante; destacarei as partes alteradas em vermelho.
public static string Left(this string s, int len)
{
if (len == 0 || s.Length == 0)
return "";
else if (s.Length <= len)
return s;
else
return s.Substring(0, len);
}
E agora podemos chamá-lo de como se esse mesmo método existisse na classe String como foi enviado:
var s = "Supercalifragilisticexpialidocious";
s = s.Left(5);
Bastante inteligente. É difícil não se apaixonar pelos métodos de extensão, pois eles permitem moldar as classes exatamente como o senhor acha que elas devem ser. Isso é bastante inócuo no C#, pois o os métodos de extensão permitem apenas que o senhor adicione novas funcionalidades às classes, não sobrescrevem, removem ou substituem nada.
Mas imagine se o senhor pudesse.
Bem, é exatamente assim que acontece em outras linguagens mais dinâmicas, como Javascript, Python, Perl e Ruby. Algo tão prosaico quanto as extensões de C# é algo antigo para essas pessoas. Nessas linguagens, o senhor poderia redefinir tudo na seção String
se o senhor quisesse. Isso é comumente conhecido nos círculos de linguagem dinâmica como monkeypatching.
Se a ideia de monkeypatching assusta um pouco o senhor, provavelmente deveria. O senhor consegue imaginar a depuração de um código em que o String
tivesse comportamentos sutilmente diferentes da classe String
que o senhor aprendeu a usar? Monkeypatching pode ser incrivelmente perigoso nas mãos erradas, como observa Avdi Grimm:
O novo preto é a aplicação de patches em macacos [in the Ruby community]. É o que todos os jovens da moda estão fazendo. A ponto de os hackers inteligentes e experientes usarem o monkey patch como a ferramenta de primeiro recurso, mesmo quando uma solução mais simples e tradicional é possível.
Não acredito que essa situação seja sustentável. Onde eu trabalho, já estamos vendo problemas sutis e difíceis de depurar surgirem como resultado de patches de plug-ins feitos por macacos. Os patches interagem de forma imprevisível e combinatória. E, por sua natureza, os bugs causados por monkey patches são mais difíceis de rastrear do que aqueles introduzidos por classes e métodos mais tradicionais. Apenas um exemplo: em um projeto, era sabido que não podíamos confiar nos atributos herdáveis de classe fornecidos pelo ActiveSupport. Ninguém sabia o motivo. Todos os modelos que escrevíamos precisavam usar soluções alternativas complicadas. Por fim, localizamos o problema em um plug-in que gerava consoles de administração. Ele estava sobrescrevendo
Class.inherited()
. Levamos meses para descobrir isso.Isso só vai piorar se não fizermos algo a respeito. E o “algo” terá que ser uma mudança cultural, não uma correção técnica. Acredito que é hora de programadores experientes de Ruby se livrarem de “monkey patching” e começarem a demonstrar técnicas mais robustas.
Tente imaginar um mundo onde o todo programador que o senhor conhece é um aspirante a designer de linguagem, empenhado em moldar a linguagem de acordo com seus caprichos. Quando fecho os olhos e imagino isso, tenho uma visão do apocalipse, uma tempestade perfeita e escura como breu de código totalmente incompreensível e patologicamente difícil de depurar.
Outro dia, eu estava examinando um código de plug-in PHP aleatório e, francamente, ele era uma porcaria. Mas isso é porque o a maioria dos códigos é uma porcaria. Incluindo o meu próprio. Infelizmente, essa é a norma estatística. É por isso que o sites como o The Daily WTF têm a garantia de ter mais material do que poderão publicar nos próximos milênios. (Nota para mim mesmo: investir nesse site). Só posso imaginar como seria o código do plug-in PHP se o desenvolvedor tivesse a capacidade de redefinir palavras-chave e classes fundamentais do PHP à vontade. Esse é o tipo de pensamento que me leva a beber Bawls. E esse material é nojento.
O senhor pode dizer que o PHP, sem a capacidade fundamental da linguagem dinâmica de fazer monkeypatch, é apenas mais uma linguagem Blub de baixa qualidade. Mas também há uma tonelada de código PHP incrivelmente útil por aí. Portanto, parece-me que a capacidade de fazer monkeypatch não impede que as pessoas produzam um grande volume de código útil, mesmo em uma linguagem meio… horrível. Alguns deles são até bons!
Embora eu reconheça o poder e a utilidade do monkeypatching de linguagem dinâmica, sei o suficiente sobre programadores – eu mesmo absolutamente inclusive — para saber que a grande maioria de nós não tem absolutamente nenhum interesse em redesenhar uma linguagem de programação. Há uma razão pela qual alguns dos mais profundamente respeitado computador cientistas no mundo acabam se tornando designers de idiomas.
Talvez então, dados os riscos, monkeypatching deveria significar pegar o meta-hammer com a menor frequência humanamente possível. Essa é uma posição que o próprio Avdi defende em um comentário posterior:
Temo que muitas pessoas não tenham entendido a essência do meu argumento – que a extensão dinâmica de classes é atualmente usada em excesso no Ruby, de maneiras que são:
- Desnecessário – outra técnica (como um mixin, ou estender localmente objetos individuais) teria funcionado tão bem ou melhor.
- Complicado demais – o uso de um monkey patch na verdade criou mais trabalho para o autor.
- Frágil – a solução está fortemente vinculada a componentes internos de terceiros, reduzindo a utilidade do plug-in ou da gem porque é propensa a falhas.
- Escopo excessivamente amplo – ao codificar extensões para as classes principais, o autor tira do usuário do plugin/gem a opção de escopo da alteração, limitando ainda mais a utilidade.
O que quero dizer é que existem alternativas, muitas vezes alternativas que são realmente mais fáceis de implementar e que tornarão seu plug-in ou gem mais útil para o usuário.
Embora eu goste da natureza aditiva das extensões do C#, até mesmo essas extensões são suficientes para me deixar um pouco nervoso, por mais brandas que sejam. O monkeypatching de linguagem dinâmica completa vai ainda mais longe; pode até ser a expressão máxima do poder de programação. Existe algo mais puro e divino do que o programar sua própria linguagem de programação?
Mas se exercer esse poder não o assustar e humilhar um pouco, talvez seja melhor deixar o monkeypatching para o macacos realmente inteligentes.