Ontem publiquei no meu blog pessoal algumas críticas ao método clone. Eu diria que esse é um assunto um pouco delicado. Resolvi escrever um pouco aqui também em homenagem a uma discussão sobre este mesmo assunto que tivemos em uma das turmas da Academia do Arquiteto, alguns meses atrás.
Antes de entrar nos detalhes, vamos deixar claro o ponto aonde vamos chegar: objetivos imutáveis podem ajudar muito na qualidade do nosso código, e estudar a linguagem Scala me ajudou a enxergar isso. Dito isso, vamos aos pormenores.
O método clone tem pelo menos dois grandes problemas. O primeiro deles é conceitual. Para suportarmos operações de clone em nossos objetos, temos que implementar a interface Cloneable. Faria todo o sentido do mundo, se o método clone estivesse definido nesta interface, e não em Object.
O segundo problema é de ordem mais prática. Vejamos o código abaixo, em Scala, que é o mesmo que usei no meu post mencionado acima:
Temos três classes, A, B e C. Criamos um objeto a, e depois clonamos ele. O clone, diferente do que pode parecer, faz apenas uma cópia rasa do objeto. Ou seja, ele não vai criar um novo b dentro do a, vai apenas copiar a referência. Se quisermos uma cópia profunda, criando novos objetos internos, temos que implementar isso na nossa sobrecarga do método clone. Veja o que acontece na versão atual:
Ou seja, a última linha, c.x = -99, alterou tanto o a quanto o a2, o que não era o que gostaríamos.
E o que Scala tem a ver com tudo isso? Imutabilidade. Isso não é excluisivo desta linguagem, mas um dos pensamentos que linguagens funcionais (o que inclui Scala) traz é a preferência por estruturas de dados imutáveis.
E é aqui que Scala ajuda a melhorar nosso código - nos expondo a novas idéias. Em um pensamento "tradicional" na linguagem java, a tentação seria sobrescrever corretamente o método clone, mesmo isso dando um certo trabalho, e com grande risco de não funcionar corretamente.
Nosso novo pensamento é: vamos remover completamente a funcionalidade de clone e vamos tornar os objetos imutáveis. Algo assim:
Agora todos os elementos das nossas classes são imutáveis - i.e. não podem ser alterados. Se quisermos alterar alguma coisa, temos que fazer o que já sabemos fazer com Strings: criar um objeto novo, com a alteração desejada. Denovo no meu post mencionado lá em cima, eu explico um pouco mais sobre como criar esse objeto novo, sem ter que fazer muito trabalho manual.
Além da corretude do código, isso traz diversos outros benefícios, como a não necessidade de locks - o estado não muda, não precisamos bloquear o acesso a ele.
E isso, é claro, é apenas um exemplo. Scala tem muito mais recursos interessantes que, mesmo quando não estamos usando a linguagem, servem para abrir nossa cabeça.
Se quiser saber mais sobre Scala, na semana que vêm teremos mais um turma do Mini Curso gratuito de Scala. E em setembro teremos a primeira turma do Core Scala, um treinamento completo nesta linguagem.
Por fim, o Kleber também publicou dois posts muito bons sobre como começar a programar em Scala, aqui e aqui. Vale a pena ler.
----------
contatos:
blog: http://jcranky.com
twitter: http://twitter.com/jcranky
scaladores: http://scaladores.com.br
core scala: http://www.globalcode.com.br/treinamentos/cursos/core-scala
Antes de entrar nos detalhes, vamos deixar claro o ponto aonde vamos chegar: objetivos imutáveis podem ajudar muito na qualidade do nosso código, e estudar a linguagem Scala me ajudou a enxergar isso. Dito isso, vamos aos pormenores.
O método clone tem pelo menos dois grandes problemas. O primeiro deles é conceitual. Para suportarmos operações de clone em nossos objetos, temos que implementar a interface Cloneable. Faria todo o sentido do mundo, se o método clone estivesse definido nesta interface, e não em Object.
O segundo problema é de ordem mais prática. Vejamos o código abaixo, em Scala, que é o mesmo que usei no meu post mencionado acima:
class A(b: B) extends Cloneable { override def clone() = super.clone override def toString() = "[A: %s]".format(b) } class B(c: C) extends Cloneable { override def clone() = super.clone override def toString() = "[B: %s]".format(c) } class C(var x: Int) extends Cloneable { override def clone() = super.clone override def toString() = "[C: %d]".format(x) } val c = new C(10) val b = new B(c) val a = new A(b) val a2 = a.clone c.x = -99
Temos três classes, A, B e C. Criamos um objeto a, e depois clonamos ele. O clone, diferente do que pode parecer, faz apenas uma cópia rasa do objeto. Ou seja, ele não vai criar um novo b dentro do a, vai apenas copiar a referência. Se quisermos uma cópia profunda, criando novos objetos internos, temos que implementar isso na nossa sobrecarga do método clone. Veja o que acontece na versão atual:
scala> println(a) [A: [B: [C: -99]]] scala> println(a2) [A: [B: [C: -99]]]
Ou seja, a última linha, c.x = -99, alterou tanto o a quanto o a2, o que não era o que gostaríamos.
E o que Scala tem a ver com tudo isso? Imutabilidade. Isso não é excluisivo desta linguagem, mas um dos pensamentos que linguagens funcionais (o que inclui Scala) traz é a preferência por estruturas de dados imutáveis.
E é aqui que Scala ajuda a melhorar nosso código - nos expondo a novas idéias. Em um pensamento "tradicional" na linguagem java, a tentação seria sobrescrever corretamente o método clone, mesmo isso dando um certo trabalho, e com grande risco de não funcionar corretamente.
Nosso novo pensamento é: vamos remover completamente a funcionalidade de clone e vamos tornar os objetos imutáveis. Algo assim:
case class A(b: B, name: String) case class B(c: C) case class C(x: Int) val c = C(10) val b = B(c) val a = A(b, "jcranky")
Agora todos os elementos das nossas classes são imutáveis - i.e. não podem ser alterados. Se quisermos alterar alguma coisa, temos que fazer o que já sabemos fazer com Strings: criar um objeto novo, com a alteração desejada. Denovo no meu post mencionado lá em cima, eu explico um pouco mais sobre como criar esse objeto novo, sem ter que fazer muito trabalho manual.
Além da corretude do código, isso traz diversos outros benefícios, como a não necessidade de locks - o estado não muda, não precisamos bloquear o acesso a ele.
E isso, é claro, é apenas um exemplo. Scala tem muito mais recursos interessantes que, mesmo quando não estamos usando a linguagem, servem para abrir nossa cabeça.
Se quiser saber mais sobre Scala, na semana que vêm teremos mais um turma do Mini Curso gratuito de Scala. E em setembro teremos a primeira turma do Core Scala, um treinamento completo nesta linguagem.
Por fim, o Kleber também publicou dois posts muito bons sobre como começar a programar em Scala, aqui e aqui. Vale a pena ler.
----------
contatos:
blog: http://jcranky.com
twitter: http://twitter.com/jcranky
scaladores: http://scaladores.com.br
core scala: http://www.globalcode.com.br/treinamentos/cursos/core-scala
Comentários