Quais são algumas vantagens da digitação de pato vs.digitação estática?

StackOverflow https://stackoverflow.com/questions/47972

  •  09-06-2019
  •  | 
  •  

Pergunta

Estou pesquisando e experimentando mais com o Groovy e tentando entender os prós e os contras de implementar coisas no Groovy que não posso/não faço em Java.A programação dinâmica ainda é apenas um conceito para mim, já que estou profundamente imerso em linguagens estáticas e fortemente tipadas.

Groovy me dá a capacidade de tipo pato, mas não consigo ver o valor.Como a digitação com pato é mais produtiva do que a digitação estática?Que tipo de coisas posso fazer na minha prática de código para me ajudar a aproveitar seus benefícios?

Faço esta pergunta com o Groovy em mente, mas entendo que não é necessariamente uma pergunta do Groovy, por isso agradeço respostas de todos os campos de código.

Foi útil?

Solução

A seguir, o que é melhor:EMACS ou vi?Esta é uma das guerras religiosas em curso.

Pense desta forma:qualquer programa que seja correto, estará correto se o idioma for digitado estaticamente.O que a digitação estática faz é permitir que o compilador tenha informações suficientes para detectar incompatibilidades de tipo em tempo de compilação, em vez de tempo de execução.Isso pode ser um aborrecimento se você estiver fazendo tipos incrementais de programação, embora (eu afirmo) se você estiver pensando claramente sobre o seu programa, isso não importa muito;por outro lado, se você estiver construindo um programa realmente grande, como um sistema operacional ou uma central telefônica, com dezenas, centenas ou milhares de pessoas trabalhando nele, ou com requisitos de confiabilidade realmente altos, então ter o compilador capaz de detecte uma grande classe de problemas sem precisar de um caso de teste para exercitar apenas o caminho de código correto.

Não é como se a digitação dinâmica fosse algo novo e diferente:C, por exemplo, é efetivamente digitado dinamicamente, já que sempre posso converter um foo* para um bar*.Significa apenas que é minha responsabilidade como programador C nunca usar código que seja apropriado em um bar* quando o endereço está realmente apontando para um foo*.Mas como resultado dos problemas com programas grandes, C desenvolveu ferramentas como o lint(1), fortaleceu seu sistema de tipos com typedef e eventualmente desenvolveu uma variante fortemente tipada em C++.(E, é claro, o C++, por sua vez, desenvolveu formas de contornar a tipagem forte, com todas as variedades de conversões e genéricos/modelos e com RTTI.

Outra coisa, porém: não confunda "programação ágil" com "linguagens dinâmicas". Programação ágil é sobre a maneira como as pessoas trabalham juntas em um projeto:o projeto pode se adaptar às mudanças nos requisitos para atender às necessidades dos clientes e, ao mesmo tempo, manter um ambiente humano para os programadores?Isso pode ser feito com linguagens de tipo dinâmico, e muitas vezes é, porque elas podem ser mais produtivas (por exemplo, Ruby, Smalltalk), mas pode ser feito, foi feito com sucesso, em C e até mesmo em assembler.Na verdade, Desenvolvimento de rali até usa métodos ágeis (SCRUM em particular) para fazer marketing e documentação.

Outras dicas

Muitos comentários sobre digitação de pato realmente não fundamentam as afirmações.Não "ter que se preocupar" com um tipo não é sustentável para manutenção ou para tornar um aplicativo extensível.Eu realmente tive uma boa oportunidade de ver Grails em ação durante meu último contrato e é muito engraçado de assistir.Todos estão felizes com os ganhos em poder "criar um aplicativo" e seguir em frente - infelizmente, tudo chega até você no back-end.

Groovy parece o mesmo para mim.Claro que você pode escrever um código muito sucinto e definitivamente há um pouco de açúcar na forma como trabalhamos com propriedades, coleções, etc.Mas o custo de não saber o que diabos está sendo repassado fica cada vez pior.Em algum momento você está coçando a cabeça e se perguntando por que o projeto se tornou 80% de teste e 20% de trabalho.A lição aqui é que “menor” não torna o código “mais legível”.Desculpe pessoal, é uma lógica simples - quanto mais você precisa saber intuitivamente, mais complexo se torna o processo de compreensão desse código.É por isso que as GUIs deixaram de se tornar excessivamente icônicas ao longo dos anos - com certeza parece bonito, mas o que está acontecendo nem sempre é óbvio.

As pessoas nesse projeto pareciam ter problemas para "definir" as lições aprendidas, mas quando você tem métodos que retornam um único elemento do tipo T, uma matriz de T, um ErrorResult ou um nulo ...torna-se bastante aparente.

No entanto, uma coisa que trabalhar com o Groovy fez por mim - incríveis horas faturáveis, uau!

Não há nada de errado com a digitação estática se você estiver usando Haskell, que possui um sistema de tipos estáticos incrível.No entanto, se você estiver usando linguagens como Java e C++, que possuem sistemas de tipos terrivelmente incapacitantes, a digitação com pato é definitivamente uma melhoria.

Imagine tentar usar algo tão simples como "mapa"em Java (e não, não quero dizer a estrutura de dados).Mesmo os genéricos são pouco suportados.

A digitação com pato prejudica a verificação estática da maioria dos IDE modernos, que pode apontar erros enquanto você digita.Alguns consideram isso uma vantagem.Quero que o IDE/Compilador me diga que fiz um truque estúpido de programador o mais rápido possível.

Meu argumento favorito mais recente contra a digitação de pato vem de um DTO do projeto Grails:

class SimpleResults {
    def results
    def total
    def categories
}

onde results acaba sendo algo como Map<String, List<ComplexType>>, que pode ser descoberto apenas seguindo uma trilha de chamadas de métodos em diferentes classes até encontrar onde ele foi criado.Para os curiosos terminais, total é a soma dos tamanhos dos List<ComplexType>areia categories é o tamanho do Map

Pode ter ficado claro para o desenvolvedor original, mas o pobre cara da manutenção (ME) perdeu muito cabelo ao rastrear este.

É um pouco difícil ver o valor da digitação com pato até que você a use por um tempo.Depois de se acostumar com isso, você perceberá o quanto é trabalhoso para sua mente não ter que lidar com interfaces ou se preocupar com exatamente o tipo de algo.

IMHO, a vantagem da digitação de pato aumenta quando você adere a algumas convenções, como nomear variáveis ​​​​e métodos de maneira consistente.Tomando o exemplo de Ken G., acho que seria melhor ler:

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

Digamos que você defina um contrato em alguma operação chamada 'calculateRating(A,B)' onde A e B aderem a outro contrato.Em pseudocódigo, seria lido:

Long calculateRating(A someObj, B, otherObj) {

   //some fake algorithm here:
   if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating());
   else return otherObj.calcRating();

}

Se você quiser implementar isso em Java, tanto A quanto B devem implementar algum tipo de interface que leia algo assim:

public interface MyService {
    public int doStuff(String input);
}

Além disso, se você quiser generalizar seu contrato para cálculo de classificações (digamos que você tenha outro algoritmo para cálculo de classificações), você também terá que criar uma interface:

public long calculateRating(MyService A, MyServiceB);

Com a digitação de pato, você pode abandonar seu interfaces e confie apenas que no tempo de execução, A e B responderão corretamente ao seu doStuff() chamadas.Não há necessidade de uma definição específica de contrato.Isso pode funcionar para você, mas também pode funcionar contra você.

A desvantagem é que você precisa ser extremamente cuidadoso para garantir que seu código não seja quebrado quando alguém o alterar (ou seja, a outra pessoa deve estar ciente do contrato implícito no nome do método e nos argumentos).

Observe que isso se agrava especialmente em Java, onde a sintaxe não é tão concisa quanto poderia ser (em comparação com escala por exemplo).Um contra-exemplo disso é o Estrutura de elevação, onde dizem que a contagem de SLOC da estrutura é semelhante a Trilhos, mas o código de teste tem menos linhas porque não precisa implementar verificações de tipo nos testes.

Aqui está um cenário em que a digitação com pato economiza trabalho.

Aqui está uma aula muito trivial

class BookFinder {
    def searchEngine

    def findBookByTitle(String title) {
         return searchEngine.find( [ "Title" : title ] ) 
    }
}

Agora para o teste de unidade:

void bookFinderTest() {
    // with Expando we can 'fake' any object at runtime.
    // alternatively you could write a MockSearchEngine class.
    def mockSearchEngine = new Expando()
    mockSearchEngine.find = {
        return new Book("Heart of Darkness","Joseph Conrad")
    }

    def bf = new BookFinder()
    bf.searchEngine = mockSearchEngine
    def book = bf.findBookByTitle("Heart of Darkness")
    assert(book.author == "Joseph Conrad"
}

Conseguimos substituir o SearchEngine por um Expando, devido à ausência de verificação de tipo estático.Com a verificação de tipo estático, teríamos que garantir que SearchEngine fosse uma interface, ou pelo menos uma classe abstrata, e criar uma implementação simulada completa dela.Isso exige muito trabalho ou você pode usar uma estrutura sofisticada de simulação de propósito único.Mas a digitação com pato é de uso geral e nos ajudou.

Por causa da digitação duck, nosso teste de unidade pode fornecer qualquer objeto antigo no lugar da dependência, desde que implemente os métodos que são chamados.

Para enfatizar: você pode fazer isso em uma linguagem de tipo estaticamente, com uso cuidadoso de interfaces e hierarquias de classes.Mas com a digitação com pato você pode fazer isso com menos reflexão e menos pressionamentos de tecla.

Essa é uma vantagem da digitação com pato.Isso não significa que a digitação dinâmica seja o paradigma certo para usar em todas as situações.Em meus projetos Groovy, gosto de voltar para Java em circunstâncias em que sinto que os avisos do compilador sobre tipos vão me ajudar.

Com, TDD + 100% de cobertura de código + Ferramentas IDE para executar meus testes constantemente, não sinto mais necessidade de digitação estática.Sem tipos fortes, meus testes de unidade tornaram-se muito fáceis (basta usar o Maps para criar objetos simulados).Especialmente, quando você usa genéricos, você pode ver a diferença:

//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>>

contra

//Dynamic typing
def someMap = [:]   

Não é que a digitação com pato seja mais produtiva do que a digitação estática, mas simplesmente diferente.Com a digitação estática você sempre precisa se preocupar se seus dados são do tipo correto e em Java eles aparecem através da conversão para o tipo certo.Com a digitação de pato, o tipo não importa, desde que tenha o método correto, então isso realmente elimina muito do incômodo de conversão e conversão entre tipos.

@Chris Grupo

Não é que a digitação estática seja mais produtiva do que a digitação com pato, mas é simplesmente diferente.Com a digitação duck você sempre precisa se preocupar se seus dados têm o método correto e em Javascript ou Ruby isso aparece através de muitos testes de método.Com a digitação estática, não importa, desde que seja a interface correta, então isso realmente elimina muitos problemas de testes e conversões entre tipos.

Desculpe, mas eu tive que fazer isso...

Minha opinião:

Linguagens digitadas dinamicamente ou digitadas em pato são brinquedos.Você não pode obter o Intellisense e perde tempo de compilação (ou tempo de edição - ao usar um IDE REAL como o VS, não aquele lixo que outras pessoas pensam que são IDEs) na validação de código.

Fique longe de qualquer linguagem que não seja digitada estaticamente, todo o resto é simplesmente masoquismo.

Para mim, eles não são terrivelmente diferentes se você ver as linguagens de tipo dinâmico simplesmente como uma forma de digitação estática onde tudo herda de uma classe base suficientemente abstrata.

Os problemas surgem quando, como muitos apontaram, você começa a ficar estranho com isso.Alguém apontou uma função que retorna um único objeto, uma coleção ou um nulo.Faça com que a função retorne um tipo específico, não vários.Use várias funções para coleção única ou coleção.

O que acontece é que qualquer um pode escrever código incorreto.A digitação estática é um ótimo dispositivo de segurança, mas às vezes o capacete atrapalha quando você quer sentir o vento no cabelo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top