segunda-feira, 7 de janeiro de 2019

Aprenda 23 princípios para escrever código legível

Ler o código de outras pessoas pode ser confuso. Podem passar horas para resolver problemas que poderiam ter sido corrigidos em minutos. Neste artigo, Artur Śmiarowski quer compartilhar dicas sobre como escrever códigos que serão mais fáceis de entender e manter.
Antes de começar, observe que este não é um guia para escrever "código limpo". As pessoas tendem a entender coisas diferentes com este termo, algumas preferem facilmente extensíveis e genéricas. Outros preferirão abstrair a implementação e apenas garantir a configuração e outros, que apenas querem ver um código subjetivamente bonito. Este guia se concentra na visibilidade do código. Com isso quero dizer um pedaço de código que comunica as informações necessárias para outros desenvolvedores, da forma mais eficiente possível.
Abaixo estão 23 princípios que ajudarão você a escrever um código mais legível. Este artigo é longo, não hesite em ir diretamente para as partes que lhe interessam.

II. Esteja ciente do seu problema antes de criar a solução

Se você corrige um bug, adiciona um novo recurso ou projeta um aplicativo, você resolve um problema para outra pessoa. Idealmente, você vai querer fazer isso, deixando o mínimo de anomalias atrás de você. Você deve ter clareza sobre os problemas que está resolvendo com suas escolhas de "padrão de design", retrabalho, dependências externas, bancos de dados e todas as outras coisas nas quais gasta tempo precioso.
Seu pedaço de código é um problema em potencial. Até o código mais bonito é. A única vez que um trecho de código não é mais um problema é quando um projeto é concluído e está inativo ou não é mais suportado. Por quê? Porque alguém terá que lê-lo durante sua vida, entendê-lo, corrigi-lo, estendê-lo ou mesmo remover completamente a funcionalidade que ele traz.
Manter o código básico leva muito tempo e poucos desenvolvedores gostam de fazê-lo porque falta criatividade. Escreva seu código da maneira mais simples possível para que um desenvolvedor júnior possa corrigi-lo quando necessário e você estará livre para lidar com problemas maiores.
A perda de tempo é um problema. Uma solução perfeita para sua tarefa pode estar disponível, mas às vezes é difícil para um desenvolvedor vê-la. Há tarefas para as quais a melhor solução é convencer o cliente de que o que ele procura não é necessariamente o que ele precisa. Isso requer um entendimento mais profundo do aplicativo e seu propósito. Seu cliente pode querer um módulo inteiro que eventualmente se tornará milhares de linhas adicionais de código, enquanto ele precisa apenas personalizar ainda mais suas opções existentes. Você pode precisar modificar apenas o código existente, economizando tempo e dinheiro.
Existem outros tipos de problemas. Digamos que você precise implementar uma lista de registros com filtros. Os dados são armazenados em um banco de dados, mas o link entre os diferentes registros é complexo. Depois de analisar como o cliente deseja que os dados sejam filtrados, você descobre que a complexidade do banco de dados exigirá 20 horas de criação de consultas SQL complexas, com várias associações e consultas internas. Por que não explicar que existe uma solução diferente que levará apenas uma hora, mas que não cobrirá toda a funcionalidade? Esse recurso adicional pode não justificar esse esforço, o que resultará em economia de custos.

III. Escolha a ferramenta de trabalho certa

Essa linguagem especial, a estrutura que você tanto ama ou um novo mecanismo de banco de dados, pode não ser a ferramenta certa para o problema que você está tendo. Em um projeto sério, não escolha ferramentas que você já ouviu falar como sendo fabuloso para tudo. Isso vai levar você ao desastre. Se seus dados precisarem de relacionamentos, escolher o MongoDB apenas para aprender terminará mal. Você sabe que pode fazer isso, mas muitas vezes precisará de uma solução alternativa que gere código adicional e forneça soluções que não são ideais. Claro, você pode dirigir um prego com uma tábua de madeira, mas uma busca rápida no google o mandaria de volta para um martelo.

IV. Simplicidade é rainha

Você já deve ter ouvido a frase "otimização prematura é a fonte de todo o mal". Ela tem uma verdade incompleta. Você deve preferir soluções simples, a menos que tenha certeza de que não funcionará, mas não acredite, você já deve ter verificado ou calculado anteriormente e ter certeza. Escolher uma solução mais complexa por qualquer motivo, velocidade de execução, falta de memória, falta de dependências ou qualquer outro motivo pode ter um impacto significativo na legibilidade do código. Não complique as coisas, a menos que você tenha que fazê-lo. A exceção a isso seria conhecer uma solução mais eficiente e saber que sua implementação não afetará a legibilidade e sua programação.
Da mesma forma, você não precisa usar todos os novos recursos do seu idioma se não beneficiar você e sua equipe. Novo, não significa melhor. Se você não tiver certeza, tente novamente no início e reconsidere o problema que está tentando resolver antes de fazer qualquer alteração. Se você precisar do índice de um loop, não é porque existem novas maneiras diferentes de escrever em Javascript que a sintaxe "for" é obsoleta.

V. Suas funções, classes e componentes devem ter um objetivo claramente definido

Você conhece os princípios do "  SOLID  "? Achei prudente projetar biblioteca genérica, mas mesmo que eu usei algumas vezes e eu vi implementações em projetos, acho que as regras são um pouco confuso e complicado.
Divida seu código em funções para que cada um faça apenas uma coisa. Por exemplo, considere como podemos implementar um botão. Pode ser uma classe que agrupa todos os recursos de um botão. Você poderia implementar o botão com uma função para exibi-lo na tela, outro para destacá-lo quando for executado pelo mouse, depois outro para o clique do botão e outro para animá-lo ao clicar. Você pode dividi-lo ainda mais. Se você precisar calcular o retângulo da posição do botão com base na resolução da tela, não o faça na função de exibição. Implemente esse cálculo em uma classe diferente, pois ele poderá ser usado por outros elementos da IHM e usá-lo para exibir o botão.
É uma coisa simples de seguir, assim que você pensa "isso não tem nada a ver aqui", você pode movê-lo para outra função, fornecendo mais informações aos colegas encapsulando um bloco de código com um nome função e comentários.
Examine os exemplos abaixo que fazem a mesma coisa, que informa o mais rapidamente possível do que faz?
 
selecionar
// C++
if (currentDistance < radius2) {
 // C'est la vue du joueur
  if (!isLight) {
  // Si l'éclairage de la tuile est d'environ 30% (alors la vue dans l'obscurité est pire) ou la distance du joueur est de 1, la tuile devrait être visible.
    if (hasInfravision || map.getLight(mapPosition) > 0.29f || ASEngine::vmath::distance(center, mapPosition) == 1) {
      map.toggleVisible(true, mapPosition);
    }
  }
  // Ceci est pour le calcul de l'éclairage
  else {
    ASEngine::ivec3 region = World::inst().map.currentPosition;
    ASEngine::ivec2 pos = mapPosition;
    if (mapPosition.x > 63) {
      pos.x -= 64;
      region.x += 1;
    }
    else if (mapPosition.x < 0) {
      pos.x += 64;
      region.x -= 1;
    }
    if (mapPosition.y > 63) {
      pos.y -= 64;
      region.y += 1;
    }
    else if (mapPosition.y < 0) {
      pos.y += 64;
      region.y -= 1;
    }
    map.changeLight(pos, region, 1.0f - static_cast(currentDistance) / static_cast(radius2));
  }
}
 
selecionar
// C++
if (currentDistance < radius2) {
 // C'est la vue du joueur
  if (!isLight) {
    this->markVisibleTile(hasInfravision, map, center, mapPosition);
  }
  // Ceci est pour le calcul de l'éclairage
  else {
    ASEngine::ivec3 region = World::inst().map.currentPosition;
    ASEngine::ivec2 pos = map.getRelativePosition(mapPosition, region);
    map.changeLight(pos, region, 1.0f - static_cast(currentDistance) / static_cast(radius2));
  }
}

VI. Naming é difícil, mas é importante

Os nomes das variáveis ​​e funções devem ser distintos e fornecer uma ideia geral de seu uso. É importante que os nomes descrevam a utilidade para sua equipe e que eles estejam em conformidade com as convenções adotadas pelo projeto. Mesmo se você não concordar com eles. Se cada consulta para procurar um registro em um banco de dados começar com a palavra "find", como "findUser", sua equipe poderá ficar confusa se você nomear sua função "getUserProfile" porque está acostumado com ela. Tente agrupar os nomes sempre que possível. Por exemplo, se você tiver várias classes para validação de entrada, adicione "Validator", pois o sufixo de nome pode fornecer rapidamente informações sobre o propósito da classe.
Escolha e siga um tipo de caso padrão. Pode ser confuso acabar com um case de camelo (camelCase), um snake_case , um kebab-case (as letras estão em letras minúsculas e estão ligadas por traços) e um case de cerveja usado em diferentes arquivos do mesmo projeto.

VII. Não duplique o código

Já estabelecemos que o código é um problema. Então, por que duplicar seus problemas para ganhar alguns minutos? Não faz sentido. Você pode pensar que vai resolver algo rapidamente, simplesmente copiando e colando. Mas se você tiver que copiar mais de duas linhas de código, diga que pode perder uma oportunidade de encontrar uma solução melhor. Talvez uma função genérica ou um loop?

VIII. Remover código não utilizado, não deixá-la nos comentários

O código no comentário é confuso. Foi removido temporariamente? É importante? Quando foi comentado? É inútil, faça desaparecer. Eu entendo que você está hesitante em remover este código, porque as coisas podem dar errado e você só quer descomentar. Você pode até ser muito apegado a ele depois de todo o tempo e energia que você coloca nele. Ou talvez você ache que pode ser necessário "em breve". A solução para todos esses problemas é o software de versão. Use o histórico do GIT para encontrar o código, se precisar. E limpe depois de você!

IX. valores constantes devem ser lista constante ou estática

Você usa strings ou inteiros para definir os tipos de objetos? Por exemplo, um usuário pode ter uma função de "administrador" ou "convidado". Como você vai verificar se ele tem o papel de "administrador"?
 
selecionar
if ($user->role == "admin") { 
// L'utilisateur est un administrateur
}
Isso não é ótimo. Primeiro, se o nome "admin" mudar, você terá que modificá-lo em todo o aplicativo. Você poderia dizer que isso raramente acontece e os IDEs modernos facilitam a substituição. Isso mesmo. A outra razão é a ausência de autocomplete e erros de sintaxe podem ocorrer. Eles podem ser muito difíceis de depurar.
Ao definir constantes globais ou enumerações, dependendo do idioma usado, você pode aproveitar o preenchimento automático e editar um valor em um só lugar, caso precise dele. Você nem precisa lembrar que tipo de valor está oculto atrás da constante, apenas deixe fazer o EDI e a magia do autocomplete.
Não é apenas sobre o tipo de seus objetos. No PHP, você pode definir matrizes com strings como nomes de campos. Com estruturas complexas, pode ser difícil não cometer erros de tipo. E por esse motivo, é melhor usar objetos. Tente evitar codificar com strings e você fará menos erros de digitação e acelerará com o AutoComplete.
 
selecionar
// PHP
const ROLE_ADMIN = "admin";

if ($user->role == ROLE_ADMIN) { 
// l'utilisateur est un administrateur
}
 
selecionar
// C++
enum class Role { GUEST, ADMIN }; // Il est possible de mapper ces énumérations vers des chaînes de caractères, mais ce n'est pas nécessaire.

if (user.role == Role.ADMIN) { 
// l'utilisateur est un administrateur
}

X. Prefere funções nativas a soluções personalizadas

Se o seu idioma ou estrutura que você selecionou para o seu projeto fornecer uma solução para o seu problema, use-o. Todos podem pesquisar rapidamente na Internet o que uma função faz, mesmo que não seja usada com frequência. Provavelmente, levará mais tempo para entender sua solução personalizada. Se você encontrar uma parte do código que faz a mesma coisa em uma função interna, refatore-a rapidamente, não a deixe como está. O código excluído não é mais um problema, por isso é importante excluí-lo!

XI. Use as recomendações específicas do idioma

Se você escrever em PHP, você deve conhecer os PSRs . Para Javascript, há uma recomendação decente do Airbnb . Para C ++, há uma recomendação do Google ou instruções básicas de Bjarne Stroustrup, o criador do C ++. Outros idiomas também devem ter suas próprias recomendações para a qualidade do código e você pode até mesmo fornecer seus próprios padrões para sua equipe. A importância é fortalecer o uso de recomendaçõesescolhido para o projeto, para que haja uma visão unificada de como ele deve ser desenvolvido. Isso evita muitos problemas de pessoas diferentes com experiências únicas e fazendo o que eles estão acostumados.

XII. Evitar a criação de blocos de código aninhados um no outro

Compare esses dois blocos de código:
 
selecionar
void ProgressEffects::progressPoison(Entity entity,
    std::shared_ptr<Effects> effects) {

  float currentTime = DayNightCycle::inst().getCurrentTime();
  if (effects->lastPoisonTick > 0.0f
      && currentTime > effects->lastPoisonTick + 1.0f) {
    if (effects->poison.second > currentTime) {
      std::shared_ptr < Equipment > eq = nullptr;
      int poisonResitance = 0;
      if (this->manager.entityHasComponent(entity, ComponentType::EQUIPMENT)) {
        eq = this->manager.getComponent < Equipment > (entity);
        for (size_t i = 0; i < EQUIP_SLOT_NUM; i++) {
          if (eq->wearing[i] != invalidEntity
              && this->manager.entityHasComponent(eq->wearing[i],
                  ComponentType::ARMOR)) {
            std::shared_ptr < Armor > armor = this->manager.getComponent < Armor
                > (eq->wearing[i]);
            poisonResitance += armor->poison;
          }
        }
      }

      int damage = effects->poison.first - poisonResitance;
      if (damage < 1)
        damage = 1;
      std::shared_ptr < Health > health = this->manager.getComponent < Health
          > (entity);
      health->health -= damage;
    } else {
      effects->poison.second = -1.0f;
    }
  }
}
 
selecionar
void ProgressEffects::progressPoison(Entity entity, std::shared_ptr effects)
{
    float currentTime = DayNightCycle::inst().getCurrentTime();
    if (effects->lastPoisonTick < 0.0f || currentTime < effects->lastPoisonTick + 1.0f) return;
    if (effects->poison.second <= currentTime) {
        effects->poison.second = -1.0f;
        return;
    }

    int poisonResitance = this->calculatePoisonResistance(entity);
    int damage = effects->poison.first - poisonResitance;
    if (damage < 1) damage = 1;
    std::shared_ptr health = this->manager.getComponent(entity);
    health->health -= damage;
}
O segundo é mais fácil de ler, não é? Se tal solução for possível, evite aninhar os blocos condicionais e os loops entre si. Um truque é inverter a instrução "if" e retornar à função de chamada antes de prosseguir para o próximo código, como no exemplo acima.

XIII. Não é o número de linhas que representam

Costumamos dizer que um pedaço de código que leva o menor número de linhas para realizar a tarefa é melhor. Alguns de nós ficam obcecados com o número de linhas de código que adicionamos ou removemos, medindo nossa produtividade por esse número. Fazemos isso para simplificação, mas não é uma regra que deve ser seguida sem levar em conta a legibilidade. Você pode reduzi-lo a uma única linha de código, mas é provável que seja mais difícil de entender do que se separar em algumas linhas simples com apenas um comando por linha.
Os idiomas fornecem a capacidade de escrever expressões condicionais curtas, como:
 
selecionar
$variable == $x ? $y : $z; // if ($variable == x) { $result = $y; } else { $result = $z; }
Pode ser uma boa escolha, mas também pode ser exagerada:
 
selecionar
$variable == $x ? ($x == $y ? array_merge($x, $y, $z) : $x) : $y; // Pardon ???
Deve ser mais fácil de entender após o desenvolvimento:
 
selecionar
$result = $y;

if ($variable == $x && $x == $y) $result = array_merge($x, $y, $z);

else if ($variable == $x) $result = $x;
Essas três linhas ocupam mais espaço na tela, mas leva menos tempo para analisar o que é feito com os dados.

XIV. Aprenda os modelos de design e quando não usá-los

Existem diferentes modelos de design que são frequentemente escolhidos para resolver problemas de desenvolvimento. Embora possam resolver problemas específicos, deve-se ter em mente que sua utilidade pode ser afetada por vários fatores, como o tamanho do projeto, o número de pessoas trabalhando lá, as restrições de tempo (custo) ou a complexidade necessária do projeto. a solução. Alguns modelos foram denominados anti-modelos, como o Singleton, porque, mesmo que forneçam soluções, também introduzem problemas em alguns casos.
Certifique-se de entender o custo da implementação em termos de complexidade introduzida antes de escolher um modelo de design para sua solução. Você pode não precisar de um modelo "Observador" para se comunicar entre componentes em um único sistema. Alguns booleanos podem ser uma solução fácil de seguir. É mais apropriado gastar tempo implementando um modelo de design selecionado em um aplicativo maior e mais complexo.

XV. Separe suas aulas em gerenciador de dados e manipulador de dados

Um gerenciador de classes de dados é uma classe que contém dados em sua estrutura interna. Permite o acesso aos dados via accessors (getters) e mutators (setters) conforme necessário, mas não manipula os dados a menos que sejam modificados quando você os mantém no sistema ou se eles devem sempre ser modificados quando acesso.
Um bom exemplo está no modelo de arquitetura Entity Component System , em que os componentes contêm apenas os dados e os processos do sistema e os manipulam. Outro caso de uso seria o padrão de design "Armazém" implementado para comunicação com um banco de dados externo, em que uma classe "Modelo" representa os dados do banco de dados com uma estrutura de linguagem específica e a classe "Armazém" sincroniza dados com um banco de dados, passando as alterações de volta ao modelo ou recuperando os dados.
Essa separação facilita a compreensão das diferentes partes do seu aplicativo. Pegue o exemplo acima do armazém. Se você deseja exibir uma lista de dados contidos em uma coleção de "Modelos", você precisa saber de onde os dados vêm? Você precisa saber como os dados são armazenados no banco de dados e como eles precisam estar relacionados a estruturas específicas do idioma? Não. Você recupera os "Modelos" através dos métodos existentes e você se concentra apenas na sua tarefa, exibe os dados.
E quanto ao exemplo do Entity Component System  ? Se você precisar implementar sistemas que gerenciam o uso de uma habilidade, reproduza animações, som, cause dano e assim por diante. Você não precisa saber como a habilidade foi ativada. Não importa se um script de AI iniciou a habilidade sob certas condições ou se um jogador usou uma tecla de atalho para ativá-lo. A única coisa que você precisa saber é que os dados de "Componente" foram alterados, indicando qual habilidade deve ser gerenciada.

XVI. Corrigir problemas em sua raiz

Você precisa implementar um novo recurso, adicionando-o ao código existente. Neste código você tem um problema. A estrutura de entrada da função não funciona adequadamente com suas necessidades. Então você tem que escrever um pouco mais de código para reorganizar os dados e extrair mais antes de implementar sua solução.
Antes de fazer isso, tente voltar alguns passos no seu código. De onde vêm esses dados e como eles são usados? Você pode, talvez, recuperá-los mais facilmente de uma fonte externa ou modificá-los assim que eles são adquiridos? Ao corrigir este problema na sua raiz, você pode resolver o mesmo problema em muitos lugares e para futuros recursos ou modificações. Sempre tente simplificar a maneira como você armazena seus dados para facilitar o acesso assim que recebê-los. Isso é especialmente importante quando os dados vêm de uma fonte externa. Se você precisar de dados de usuários do aplicativo ou APIs externas, deverá eliminar itens desnecessários e reordenar o restante imediatamente.

XVII. As armadilhas de abstração oculta

Por que escrevemos soluções gerais e abstratas para nossos problemas? Estender facilmente nossos aplicativos, facilitar a adaptação a novas necessidades e reutilizar nossas dicas de código, para que não seja necessário reescrevê-las.
Muitas vezes há um custo significativo para a abstração em termos de legibilidade. O nível mais alto de abstração é quando tudo é resolvido enquanto a implementação está oculta. Você tem a capacidade de configurar como processar seus dados recebidos, mas não tem controle sobre os detalhes, como eles serão armazenados no banco de dados, a eficiência com que serão processados, quais informações serão registradas etc. A vantagem dessa solução é que, se uma nova fonte de dados for tratada da mesma maneira que a fonte atual, é fácil configurá-la com a biblioteca e selecionar o local de armazenamento. Você basicamente negocia a velocidade do controle de implementação.
Quando algo dá errado e não é um problema devidamente documentado, alguém terá dificuldade em entender todas as idéias de propósito geral que estão tentando resolver muito mais do que o necessário. Se pudermos permitir, devemos evitar ocultar os detalhes da implementação. Manter o controle sobre o banco de dados permite mais flexibilidade. Não escreva uma solução geral para um problema simples apenas porque você acha que ela poderia ser estendida no futuro. Este raramente é o caso e pode ser reescrito quando necessário.
Vamos dar um exemplo: se você criar uma classe, em 10-15 linhas de códigos legíveis, que importam dados de um arquivo CSV e os armazenam em um banco de dados, por que se preocupar em fazer duas classes e generalizar a solução para que poderia ser estendido para importar XLS ou XML no futuro, quando você nem sequer tem idéia se será necessário para sua aplicação? Por que arrastar uma biblioteca externa de 5000 linhas de código que você não precisa resolver esse problema?
Raramente é necessário tornar genérico o local de armazenamento de seus dados. Quantas vezes em sua carreira você mudou seu mecanismo de banco de dados? Nos últimos dez anos, só encontrei uma vez um problema que foi resolvido dessa maneira. Criar soluções abstratas é caro e muitas vezes inútil, a menos que seja para uma biblioteca que tenha que lidar com uma ampla variedade de projetos de uma só vez.
Por outro lado, quando você tem certeza de que precisa permitir a importação de arquivos XLS e CSV, a solução geral deve ser uma opção perfeitamente viável. Também é possível escrever uma solução geral mais tarde, quando os requisitos da sua aplicação mudarem. Será muito mais fácil para uma pessoa ter uma solução simples e clara quando precisar substituí-la.

XVIII. As regras do mundo não são as regras da sua aplicação

Eu tive um argumento interessante sobre a modelagem do "mundo real" ao implementar o paradigma OOP em um aplicativo. Digamos que tenhamos que processar dados grandes para um sistema de anúncios. Existem dois tipos de mensagens "log". O primeiro, que contém dados sobre a emissão de um anúncio. O segundo, que contém os mesmos dados que o show e alguns campos adicionais, informa às pessoas cliques no anúncio.
No mundo real, poderíamos considerar as duas ações, visualizar e clicar em um anúncio como separado, mas semelhante. Então, modelando o mundo real, poderíamos criar uma classe "Log" básica que estenderíamos para as classes "ClickLog" e "EmissionLog", conforme abaixo:
 
selecionar
struct Log {
    int x;
    int y;
    int z;
}
struct EmissionLog : public Log {}
struct ClickLog : public Log {
    float q;
}
O exemplo acima mostra muito bem como o sistema funciona no mundo real. Emitir um anúncio é completamente diferente de alguém clicar nele. No entanto, essa escolha não fornece informações importantes. Em nosso aplicativo, qualquer coisa que possa processar um "log" de transmissão pode funcionar em cliques. Podemos usar as mesmas classes para lidar com as duas, mas apenas certos processadores "log" de cliques não podem trabalhar nos logs de transmissão, devido à diferença nos dados.
Em nossa aplicação, diferente do mundo real, nossa classe "ClickLog" é uma extensão do "EmissionLog". Eles podem ser tratados da mesma maneira, usando as classes que funcionam no "EmissionLog". Ao estender a classe "ClickLog" de "EmissionLog", você informa seus colegas que qualquer coisa que possa acontecer a um programa pode chegar a cliques, sem que eles precisem conhecer todos os possíveis processadores "log" em a aplicação.
 
selecionar
struct EmissionLog {
    int x;
    int y;
    int z;
}
struct ClickLog : public EmissionLog {
    float q;
}

XIX. Typez suas variáveis, se puder, mesmo se você não tem que fazer o

Você pode passar essa regra apenas se estiver programando em idiomas com tipos estáticos. Em linguagens dinamicamente tipificadas, como PHP ou Javascript, pode ser muito difícil entender o que uma parte do código deve fazer sem ver o conteúdo das variáveis. Pela mesma razão, o código pode ser muito imprevisível quando uma única variável pode ser um objeto, uma matriz ou nulo, dependendo de determinadas condições. Permitir o menor número possível de variáveis ​​em suas configurações de função. Soluções estão disponíveis. O PHP pode ter argumentos e retornos tipados desde a versão 7. E você pode usar o Typescript em vez do Javascript. Isso ajuda a legibilidade do código e evita erros bobos.
Se você não precisar, não permita o uso de "Nulo" também. "Nulo" é uma abominação. Sua existência deve ser explicitamente verificada para evitar erros fatais que exigem código inútil. As coisas são ainda mais terríveis em Javascript com o seu "null" e "indefinido". Marque as variáveis ​​que podem ser nulas para informar seus colegas:
 
selecionar
// PHP >= 7.1
function get(?int count): array { 
    //... 
}
// Typescript
interface IUser = {
    name?: string; // name field might not be available
    type: number;
}

XX. Escrever testes

Ao longo dos anos e evitando o esgotamento, estamos progredindo ao ponto de podermos mapear os recursos mais complexos em nossas mentes e implementá-los sem verificar se o código funciona até que o primeiro rascunho seja totalmente implementado. . Neste ponto, pode parecer uma perda de tempo escrever no ciclo TDD, porque é um pouco mais lento verificar cada coisa antes de escrevê-la. É uma boa prática escrever testes de integração que permitam que a funcionalidade funcione como esperado, simplesmente porque você provavelmente deixará alguns erros pequenos e poderá verificar tudo isso em alguns milissegundos.
Se você ainda não está familiarizado com seu idioma ou biblioteca e está tentando abordagens diferentes para resolver seu problema, pode ganhar muito com a redação de testes . Isso incentiva a divisão do trabalho em partes mais gerenciáveis. Os testes de integração rapidamente explicam quais tipos de problemas o código resolve, o que pode fornecer informações mais rapidamente do que uma implementação genérica. Um simples "esta entrada impulsiona essa saída" pode acelerar o processo de compreensão do aplicativo.

XXI. Use ferramentas de análise de código estático

Existem muitas ferramentas de código aberto para análise de código estático. Muito disso também é fornecido em tempo real por IDE IDEs com recursos avançados. Eles ajudam a manter seus projetos nos trilhos. Você pode automatizar alguns deles em seus pipelines de armazenamento para que eles sejam executados em todos os "commit" em um ambiente "docker".
Escolhas sólidas para PHP:
Copiar / colar detector  ;
PHP Mess Detector - verifica potenciais bugs e complexidade;
PHP Code Sniffer - verifica os padrões de desenvolvimento;
PHPMetrics - ferramenta de análise estática com painel e gráficos.
Javascript:
JsHint / JsLint - procura por erros e problemas potenciais, pode ser integrado em um EDI para análise em tempo real;
Platão - visualização de código fonte e ferramenta de complexidade.
C ++:
Cppcheck - detecta bugs e comportamentos indefinidos;
OClint - aumenta a qualidade do código.
Ferramenta que suporta diferentes idiomas:
Pmd - detector de distúrbios.

XXII. Revisão por pares

O replay de código é feito simplesmente por outro desenvolvedor que examina seu código em busca de erros e ajuda a melhorar a qualidade do software. Embora eles possam contribuir para a qualidade geral do aplicativo e permitir um fluxo de conhecimento dentro da equipe, eles são úteis apenas se todos estiverem abertos a críticas construtivas. Às vezes, as pessoas que releem impõem sua visão e experiência e não aceitam um ponto de vista diferente, o que também pode ser difícil de aceitar.
Pode ser difícil ter sucesso dependendo do ambiente da equipe, mas a recompensa pode ser incrível. O maior e mais limpo aplicativo que já participei no desenvolvimento foi feito com revisões de código muito completas.

XXIII. Comentários

Você já deve ter notado que eu gosto de manter regras de codificação simples, para que sejam fáceis para toda a equipe seguir. É o mesmo com os comentários.
Acredito que os comentários devem ser adicionados a cada função, incluindo construtores, a cada propriedade de classe, constante estática e a cada classe. É uma questão de disciplina. Quando você permite a preguiça, permitindo exceções aos comentários quando "algo não requer comentário, porque é auto-explicativo", a preguiça é muitas vezes o que você recebe.
O que quer que você pense ao implementar o recurso (relevante para o trabalho!), É bom escrever nos comentários. Especialmente como tudo funciona, como uma classe é usada, qual é o propósito dessa enumeração e assim por diante. O objetivo é muito importante porque é difícil explicar por uma designação correta, a menos que alguém já conheça as convenções com antecedência.
Eu entendo que o "InjectorToken" tem todo o seu significado para você e que você poderia considerá-lo como "explícito". Francamente, é um bom nome. Mas quando eu vejo essa classe, eu quero saber o que é o token, o que ele faz, como eu posso usá-lo e o que é essa coisa de injetor. Seria ótimo ver isso nos comentários para que ninguém tenha que procurar em toda a aplicação, é?

XXIV. A documentação

Eu sei, eu sei, eu odeio escrever documentação também. Se você escrever tudo o que sabe em seus comentários, a documentação pode ser gerada automaticamente por ferramentas. Além disso, a documentação pode fornecer uma maneira rápida de pesquisar informações importantes sobre a operação esperada do aplicativo.
Você pode usar o Doxygen para geração automática de documentação.

XXV. Conclusão

Eu preferi um conjunto de princípios, não de regras, porque acho que há muitas maneiras de estar certo. Se você está convencido de que tudo deve ser abstrato, tente. Se você acredita que os princípios do SOLID devem ser usados ​​em todas as aplicações ou se uma solução não é feita de acordo com um padrão de projeto conhecido, então é imediatamente ruim, está bem para mim.
Escolha o caminho que parece certo para você e sua equipe e cumpri-lo. E se você já estiver no clima experimental, experimente algumas das coisas mencionadas neste artigo. Espero que isso melhore a qualidade do seu trabalho. Obrigado por ler e pensar em compartilhar se você achou o artigo interessante.

sexta-feira, 7 de dezembro de 2018

O PHP 7.3 está disponível na versão estável:

Uma visão geral do que há de novo na linguagem de programação do lado do servidor

Mantendo o hábito de publicar uma nova versão no final do ano (final de novembro - início de dezembro), a equipe de desenvolvimento do PHP acaba de anunciar o lançamento da terceira atualização do PHP 7, chamada PHP. 7.3. 

Antes de prosseguir, lembre-se que o fim da vida útil do PHP 5.x, a última ramificação antes do PHP 7.x, está programado para 31 de dezembro de 2018, portanto, em cerca de vinte dias. Após essa data, nenhuma versão do PHP 5.x ainda se beneficiará da atualização de segurança, uma vez que o PHP 5.6, a versão mais recente da ramificação, também será cortado dessas atualizações. A comunidade PHP sabe disso há algum tempo, mas parece que não foi alertada.

De acordo com o W3Tech, a versão 5.x do PHP é atualmente (6 de dezembro) usada por 75,6% dos sites usando PHP. O PHP também é usado por 78,9% de todos os sites da Web como linguagem do lado do servidor. O que significa que cerca de 60% de todos os sites usam PHP 5.xe podem ser expostos após 31 de dezembro de 2018. É recomendado migrar para o PHP 7.1+ (o PHP 7.0 não possui mais uma atualização de segurança) desde 3 de dezembro).


PHP 7.0 to PHP 7.2

Desde o PHP 7.0 , tem sido uma boa quantidade de recursos que foram adicionados; recursos que podem ser importantes para lembrar para aqueles que ainda estão no PHP 5.x.

O PHP 7.0 trouxe ganhos de desempenho com um mecanismo Zend Engine até duas vezes mais rápido que na versão 5.6, mas também muitas melhorias e novos recursos. Isso incluiu um uso de memória bastante reduzido, o AST (Abstract Syntax Tree), suporte compatível de 64 bits, um aprimoramento da hierarquia Exception, muitos erros "fatais" convertidos para " Exceções ", um gerador de número aleatório seguro (RNG), a exclusão de interfaces de programação de aplicativos de servidor (SAPI) antigas e extensões, bem como aquelas que não são suportadas, o operador de coalescência nulo (??), Declarações de retorno e escalares , classes anônimas, declarações de custo zero,

A versão 7.1.0 da linguagem de desenvolvimento da Web no lado do servidor também seguiu com novos recursos e até ganhos de desempenho: até 35% mais rápido para cargas de trabalho com uso intensivo de CPU. Em relação aos novos recursos do PHP 7.1.0, isso incluiu o suporte de tipos anuláveis , a introdução de um tipo de retorno void que indica que uma função não retorna nada, um novo pseudo-type similar a callable chamado Iterable , adicionando suporte para especificar visibilidade de constantes (pública, protegida e privada), manipulação de exceção de multi-captura, e assim por diante. 

PHP 7.2.0introduzida como principal novidade Biblioteca criptográfica de sódio que foi integrada no núcleo da linguagem. Esta biblioteca de software permite criptografia, descriptografia, assinaturas, hashing de senhas e muito mais. Além do sódio, o PHP 7.2 veio com melhorias e novos recursos, como a capacidade de converter chaves numéricas em objetos e tabelas ao lançar, contando objetos incontáveis, HashContext como um objeto, o Algoritmo Argon2 para hashing de senha, aprimoramento de constantes TLS, etc.


O que há de novo no PHP 7.3

Como nas atualizações anteriores, o PHP 7.3.0 vem com muitos novos recursos e melhorias. Entre os mais importantes, pode-se notar que o PHP 7.3 torna mais flexível sintaxe Heredoc e nowdoc . A sintaxe Heredoc e Nowdoc estão entre as quatro maneiras de declarar strings no PHP. Para a sintaxe Heredoc, por exemplo, após o operador<<<, um identificador é fornecido, seguido por uma nova linha. A string em si vem em seguida, seguida pelo mesmo identificador para fechar a notação. O identificador final deve começar na primeira coluna da linha. Além disso, o identificador deve seguir as mesmas regras de qualquer outro rótulo do PHP: ele deve conter apenas caracteres alfanuméricos e sublinhados, e deve começar com um caractere não numérico ou um sublinhado. Abaixo está um exemplo de uso do Heredoc .

Código PHP:Selecione tudo
1 2 3 4 5 6 7
<? Php 
class foo { 
pública $ bar = <<< EOT bar EOT ; } ?>     




O Nowdoc é identificado com a mesma sequência <<< usada pelo Heredoc , mas o seguinte identificador é colocado entre aspas simples. Abaixo está um exemplo de uso do Nowdoc .

Código PHP:Selecione tudo
1 2 3 4 5 6 7
<? Php 
class foo { 
pública $ bar = <<< EOT ' bar EOT ; } ?>     




Para fechar a cadeia, até o PHP 7.2, você tinha que escrever o identificador para a linha (colada à esquerda, sem recuo). Felizmente, essa sintaxe ( Heredoc e Nowdoc ) é mais flexível com o PHP 7.3 e permite recuar o identificador final.

Código PHP:Selecione tudo
1 2 3 4 5 6 7 8
<? Php 
class foo { 
pública $ bar = <<< EOT bar     EOT ; } // O identificador pode ser indentado ?>     





Também deve ser notado que, atualmente, uma nova linha deve seguir o marcador para concluir o Heredoc / Nowdoc . Mas o PHP 7.3 vai mudar isso e permitir que você termine o Heredoc / Nowdoc na mesma linha. 

O PHP 7.3 também permite finalizar vírgulas na chamada de função. No PHP, é possível deixar uma vírgula no final da lista de elementos em uma tabela. O PHP 7.2 estendeu essa possibilidade para namespaces agrupados. No PHP 7.3, essas vírgulas serão permitidas nas declarações de função. 

Novas funções também foram adicionadas no PHP 7.3. Este é o caso, por exemplo, da função is_countable () . Já existe uma função count ()que retorna um erro se o parâmetro passado para ele não é enumerável. No PHP 7.3, a função is_countable () é introduzida para verificar se o valor é enumerável antes de executar uma contagem. A nova versão do lado do servidor linguagem de programação web ainda introduziu duas novas funções array_key_first () e array_key_last () para obter a primeira ou a última chave para uma mesa. 

Como outros novos recursos, observe também que a extensão PCRE foi atualizada para o PCRE2; a capacidade de configurar a extensão JSON para lançar uma exceção no erro; Melhoria do algoritmo de hashing Argon2 com a adição de uma nova variante recomendada chamada Argon2id.

Observe também a desaprovação e exclusão de constantes não diferenciadas de maiúsculas e minúsculas entre outros recursos reprovados no PHP 7.3. Você encontrará no site oficial do PHP a lista exaustiva de mudanças nesta versão. 

 Downloads do PHP 7.3

quinta-feira, 6 de dezembro de 2018

O Blogger é uma das melhores opções para quem quer ter um blog. Utilizado por milhares de internautas, este serviço é um dos mais famosos, completos e eficientes para quem quer ter seu “cantinho” na web.
Em mais um tutorial do Tecmundo, você vai ver como é simples criar a sua página com este serviço da Google. E mesmo que você seja “marinheiro de primeira viagem”, não se preocupe, basta conferir nossas dicas e ver como não há nenhum mistério.

Se você não tem uma Conta do Google

Primeiro, acesse a página do Blogger (www.blogger.com/). Se você não tem uma Conta do Google, deverá clicar em “Iniciar” (ou “Get Started”, dependendo das configurações de idioma do seu navegador) para criar a sua.

Neste momento, é necessário fornecer um endereço de email válido, além de criar uma senha e um nome de exibição (a assinatura das suas postagens). Também é preciso informar seu sexo e data de nascimento antes de concluir esta parte com uma verificação de palavras.
O próximo passo é o “batizado” do blog, ou seja, você cria um título que será exibido nas páginas publicadas, no seu painel e no seu perfil. Também deve ser criado o endereço, que segue o padrão http://[nome].blogspot.com. Clique em “Verificar disponibilidade” para saber se a URL desejada está disponível.
O Blogger conta com vários modelos visuais para você deixar o seu blog com a sua cara. Por isso, você deve escolher um modelo inicial, o qual pode ser editado e até mesmo alterado posteriormente.
Um aviso confirma que o seu blog está pronto, e você pode começar a postar imediatamente ou personalizar a aparência dele. Repare que há também o botão “Opções de configuração avançadas”, para que você configure um domínio personalizado ou importe um blog já existente para este que acabou de ser criado.

Se você tem uma Conta do Google

Acesse a página do Blogger (www.blogger.com/). Se você já tem uma Conta do Google, basta fazer o login com seu email e senha. Então, será pedido para que você crie um nome de exibição — ou seja, a assinatura das postagens do seu blog, o nome que as outras pessoas verão nos seus textos.
Em seguida, é aberta a página principal de gerenciamento do Blogger. Nela, clique no botão “Novo blog”.
Uma pequena tela se abre, na qual você deve criar o título que será exibido nas páginas publicadas, no seu painel e no seu perfil. Também deve ser criado o endereço, que segue o padrão http://[nome].blogspot.com. Em seguida, você deve escolher um modelo inicial, o qual pode ser editado ou alterado posteriormente. Feitos estes ajustes iniciais, clique em “Criar um blog”.
.....
Pronto. O seu blog no Blogger está criado. Agora, basta utilizar os recursos disponíveis para recheá-lo com seus conteúdos favoritos.

quinta-feira, 22 de novembro de 2018

Como transformar seu PC ou notebook em um roteador WiFi

Você pode transformar o seu PC portátil com Windows em um hotspot sem fio para que outros dispositivos se conectem a ele e compartilhem da mesma conexão com a internet. Isso pode ser especialmente útil caso você tenha um login único para usar em algum estabelecimento, como um hotel, mas possua mais de um gadget que precise estar online.
É isso aí: o Windows possui um “adaptador” de criação de ponto WiFi que não fica muito visível no sistema, mas que funciona sem problemas e permite tal função após uma rápida configuração que requer poucos passos, independente do método que você escolher. O sistema operacional oferece uma série de funções integradas para que você crie o hotspot, e você verá logo abaixo que essa não é a única forma de aproveitar essa ferramenta.
O site How to Geek publicou uma série de tutoriais que ajudam nessa configuração. Abaixo, você conhece algumas dessas alternativas, de acordo com a geração sistema operacional. Eles não exigem grande conhecimento técnico, mas preste atenção nas configurações.

O JEITO FÁCIL: VIRTUAL ROUTER

O software Virtual Router é um configurador de hotspot de código aberto que funciona de forma muito dinâmica. O programa em si é bastante leve, a interface é extremamente simples e você só precisa preencher alguns campos para começar a compartilhar a conexão com outros dispositivos. Ele ainda tem a vantagem de funcionar independentemente da versão do seu sistema operacional ou das ferramentas próprias que ele fornece – ou seja, você não precisa fazer os passos “Windows 7” ou “Windows 8” caso esse processo funcione.
A principal utilidade dele é fazer com que você se conecte a uma rede WiFi existente e, a partir do roteador virtual, seja o host de uma "nova" conexão. Há segurança garantida (criptografia WPA2) e nenhuma mágica para que ele funcione: o programa utiliza a própria API do Windows para rodar, porém de uma forma simplificada.
Não há segredo: em "Network Name (SSDI)", coloque o nome que você quiser para a conexão a ser compartilhada. Em "Password", a senha de acesso para todos que desejarem a comunicação. Por fim, marque a opçao "Shared Connection" para "Wi-Fi" e clique em "Start Virtual Router". Vale lembrar que você já precisa estar devidamente conectado a alguma rede sem fio neste momento.
O programa também permite a conexão via LAN, cabo, celullar e até a boa e velha dial-up (discada). Qualquer gadget com a capacidade de se conectar a uma rede WiFi pode utilizar a conexão criada no roteador virtual, mas vale lembrar que o ponto de acesso deve permanecer ligado e em uma faixa de espaço não muito distante dos demais dispositivos..

Deu erro?

É possível que alguns computadores sofram para habilitar o Virtual Router. A mensagem “Virtual Router could not be started” é frequente. Uma das formas de solucionar isso é habilitar o adaptador de hotspot manualmente e só depois partir para o programa.
Para isso, abra o prompt de comando do PC (digite cmd.exe na pesquisa) como Administrador e insira os seguintes códigos, um por um, seguidos da tecla Enter:
netsh wlan set hostednetwork mode=allow ssid=Test key=password
netsh wlan start hostednetwork
Depois dessa configuração, o seu computador provavelmente estará habilitado para usar o Virtual Router sem a mensagem de erro. Caso contrário, pode ser que o seu dispositivo não seja habilitado a criar um hotspot (certifique-se de que ele possui uma placa de rede sem fio pré-instalada, por exemplo), seja pelo tipo de aparelho ou do WiFi do local. De qualquer forma, você ainda pode tentar as alternativas abaixo.

O JEITO MENOS FÁCIL: CONFIGURANDO MANUALMENTE

Windows 7

No caso do Windows 7 sem o uso do Virtual Router, há uma interface pronta para a criação de uma rede. Vá até o Painel de Controle, acesse a opção "Central de Rede e Compartilhamento". Lá, vá para “Configurar uma nova conexão ou rede”. Em seguida, selecione a opção “Configurar rede ad hoc sem fio (computador a computador)”.
Clique em "Avançar" e vá para a tela seguinte, que é praticamente a mesma do Virtual Router: selecione o nome da rede, o tipo de criptografia e a senha. Vá novamente para "Avançar" e conclua o processo.

Windows 8 e 8.1

No Windows 8, a Microsoft removeu a interface gráfica que permitia a criação de uma rede sem fio, mas a função ainda existe. Abra o Prompt de Comando (busque cmd.exe na Tela Iniciar e execute como Administrador).
O primeiro passo é configurar um hotstop WiFi criptografado por segurança WPA2-PSK (AES). Na caixa de texto, digite o seguinte código (com as aspas e colocando as informações necessárias no lugar dos termos indicados):
netsh wlan set hostednetwork mode=allow ssid=”INSIRA NOME DA REDE AQUI” key=”INSIRA SENHA AQUI”
Em seguida, coloque a linha a seguir, que serve como um comando para iniciar a transmissão da rede que você acabou de criar:
netsh wlan start hostednetwork
O último passo é o comando de exibição que mostrará a você alguns dados, como o número de clientes na rede e outras informações importantes. Digite:
netsh wlan show hostednetwork
Pronto! A partir de agora, outros dispositivos já podem se conectar ao hotspot criado.

MAC E UBUNTU

No Mac, clique no menu Apple, selecione “Preferências do Sistema” e vá em “Compartilhamento”. A opção que você procura é a “Compartilhamento de internet”.
Na nova janela, mude a criptografia para WEP, digite uma senha e altere o nome da rede, se você desejar. Selecione a caixa em "Compartilhamento de Internet".
Para Ubuntu, o processo é ainda mais fácil. Vá até Sistema > Preferências > Conexões de Rede. Na aba de rede sem fio, vá ao item "Adicionar".
A tela de configuração de um SSID e uma senha aparecerá – e, assim como nos métodos anteriores, você só precisa preencher as informações necessárias.

CONEXÃO VIA CABO E OUTRAS ALTERNATIVAS

Você também pode usar a transmissão de conexões via Windows de outras formas: é possível compartilhar a conexão via Bluetooth PAN ou até um cabo Ethernet que esteja conectado ao seu PC.
Para isso, certifique-se de habilitar uma opção no sistema. Na Central de Rede e Compartilhamento, vá até a conexão em que você está no momento, acesse o painel "Status de Wi-Fi" e clique em "Propriedade". Na aba "Compartilhamento", deixe ambas as caixas da janela marcadas para que computadores conectados à sua rede ganhem o acesso. Conclua a operação e tente realizar o compartilhamento.
É claro que usar um cabo Ethernet para conectar o seu aparelho com uma eventual entrada ou carregar sempre um roteador WiFi com você são soluções igualmente funcionais, apesar de menos práticas.

terça-feira, 23 de maio de 2017

Execução do desenvolvimento do NetBeans IDE 9 no JDK 9 EA Build 162

Para os tempos aventureiros, interessantes à frente, já que você é capaz de experimentar o build de desenvolvimento do NetBeans IDE 9 em cima do JDK 9 EA, no meu caso, Build 162.

Quando você configura isso no arquivo 'etc / netbeans.conf', a primeira coisa que você vê na inicialização é este erro, que parece estar relacionado ao plugin Darcula que estou usando. É claro, desde que é incrível apesar deste aspecto, por enquanto, estou usando o plugin Darcula, que você pode baixar e instalar a partir daqui , ou seja, simplesmente usar a versão mais recente do plugin para 8.2, funciona bem em 9 builds de desenvolvimento, Exceto para este erro.

Depois de ver o acima, basta clicar em Cancelar, e, até agora, apesar do acima, as coisas são muito boas!

Há duas grandes vantagens em rodar o NetBeans IDE 9 Development Build em cima do JDK 9.

A primeira é que as fontes são automaticamente muito melhores, pelo menos, no Windows e, no mínimo, no meu sistema (clique para ampliar o abaixo, onde você vê o NetBeans IDE 9 Development Build no JDK 9 à esquerda versus on JDK 8 Update 121 à direita):

<

p>

A segunda vantagem é que você tem automaticamente a integração do JShell (escolha Ferramentas | Abra o Shell da Plataforma Java), como mostrado abaixo. Excelente aprimoramento aqui é que, desde a última vez que eu usei, há agora uma dobra de código para o texto na parte superior do JShell.

Além da aplicação para celular vs. navegador para celular

A discussão contínua de se criar um aplicativo móvel nativo / híbrido ou criar uma aplicação acessada através do navegador, tem duas alternativas recentes. O ponto de partida é reconhecer que a maioria das pessoas usa cerca de 5 aplicativos em seu dispositivo móvel e ignorar os outros 30 ou mais que existem por padrão. A maioria das pessoas não tem idéia sobre quais aplicativos estão disponíveis e não os procura aleatoriamente em uma loja de aplicativos. Em vez disso, eles vão para o navegador no celular, acessam a página que lhes interessa, por exemplo, o site do jornal de escolha ou o hotel que desejam reservar e, em seguida, usam o navegador para ler o jornal ou reservar o hotel .

Progessive Web Apps (PWA). No cenário descrito acima, quando você estiver no site de seu jornal ou hotel, você será solicitado a fazer o download de um aplicativo que reabrirá essa página do navegador ou iniciará um aplicativo dedicado, ou seja, um aplicativo nativo / híbrido. Por exemplo, normalmente, você não está ciente de que seu jornal também tem um aplicativo e, portanto, você não vai para a loja de aplicativos para procurar esse aplicativo. Em vez disso, você acessa o jornal no navegador. Se esse jornal baseado em navegador for um PWA, ele permitirá que você baixe um aplicativo, sim, o aplicativo que você não conhecia o jornal disponibilizado, mas que o seu estar no navegador lendo o jornal oferece uma oportunidade para a organização por trás do jornal Para disponibilizar diretamente para você o aplicativo conectado ao jornal.

Chatbots. Em resposta ao mesmo cenário descrito acima, os chatbots se integram com seus aplicativos existentes, por exemplo, SnapChat, Facebook, WhatsApp, fornecendo um mecanismo de comunicação puramente conversacional baseado em texto para interagir em torno de algum tópico, por exemplo, quando você está na IKEA, Uma mensagem através de um ou mais dos seus aplicativos existentes para perguntar o que você está procurando e, em seguida, o IKEA chatbot ajuda a reduzir suas aspirações e direciona você para onde você pode cumpri-los. Um efeito colateral é que não há nenhuma interface de usuário, simplesmente texto, ou seja, estamos de volta na linha de comando de muitas maneiras, com chatbots.

"Os robôs vão começar a substituir os aplicativos para dispositivos móveis. Não é mais necessário procurar um app, fazer o download de um aplicativo, atualizar um aplicativo ou gerenciar um aplicativo." - Gartner, 2017

Os desenvolvimentos interessantes e, sim, nenhuns são novos, embora ambos devem sempre ser incluídos em toda a discussão no app móvel contra o app do browser.

China apresenta computador mais potente do mundo

  China apresenta computador mais potente do mundo Capacidade de processamento de dados equivale à de 175.0000 A China apresentou ...