Perguntas e repostas dos comentários do blog (FAQ)

Alô, pessoal, tudo certo? Vocês sabem o que é um FAQ? Trata-se de um acrônimo para Frequently Asked Questions, um documento que contém as perguntas e respostas mais frequentes sobre um determinado assunto. Desde que o blog foi publicado, tenho recebido várias questões no comentários e por e-mail relacionadas ao desenvolvimento de software. Por este motivo, decidi criar um FAQ com as questões mais interessantes, com o objetivo de ajudar também outros profissionais com as mesmas dúvidas.

Qual o procedimento para implementar permissões de acesso no Delphi?

Há basicamente 2 formas de implementar o esquema de permissões de acesso.

A primeira delas, mais fácil, consiste em classificar cada usuário pelo seu nível de permissão. Por exemplo, suponha que existam 3 níveis: Administrador, Gerente e Operador:

  • O Administrador tem acesso à todos os formulários;
  • O Gerente tem acesso à todos os formulários, exceto os de configuração do sistema;
  • O Operador tem acesso somente aos formulários operacionais, como cadastros básicos e emissão de vendas.

Dessa forma, no formulário de login, você pode implementar uma rotina que faz a leitura o nível do usuário e habilite/desabilite os menus conforme necessário, como no exemplo abaixo:

A segunda forma, mais difícil, baseia-se em configurar os níveis de acesso por menu.

Por exemplo, durante o cadastro do usuário, a aplicação pode disponibilizar uma tela para selecionar quais menus estarão disponíveis. Para isso, será necessário criar uma tabela (“Permissões”, por exemplo) para armazenar os menus que o usuário pode acessar.
De um modo mais prático, a segunda forma não deixa de ser um relacionamento Master/Detail.

Como implementar a exclusão de uma venda e seus itens?

Há muitas maneiras de remover os detalhes antes de excluir o registro mestre. Aqui vou apontar algumas delas.

Considere que o nome da tabela mestre seja VENDAS e a tabela de detalhes se chame ITENSVENDA. Uma das maneiras é fazer um loop no DataSet da tabela ITENSVENDA e excluir todos os itens da venda selecionada. Você pode usar o evento BeforeDelete do DataSet da tabela VENDAS para disparar essa operação. O evento BeforeDelete é disparado ANTES do registro ser excluído, portanto, podemos pensar da seguinte forma: ANTES da venda ser excluída, exclua os itens primeiro:

Obs: no código acima não é preciso utilizar o Next, já que, ao deletar um registro, o DataSet automaticamente já é movido para o registro seguinte.

Outra maneira de implementar essa funcionalidade é via SQL. Neste caso, você pode optar por fazer ANTES ou DEPOIS de a venda ser excluída. Basta utilizar uma Query para remover os itens:

Uma maneira mais confiável (porém, mais avançada) é utilizar Triggers para remover os itens. Se você tem um bom conhecimento em SQL, considere criar uma Trigger no banco de dados para remover os itens quando a venda for excluída.

Eu desenvolvi um sistema de gestão de pedidos e utilizo essa opção para realizar essa operação. Quando o usuário remove um pedido, uma Trigger é disparada no banco de dados para que os itens também sejam automaticamente excluídos. Uma das vantagens de criar Triggers é a economia de código do lado da aplicação, já que elas são criadas no banco de dados.

Tentei utilizar a cláusula IN (SQL) com o ParamByName, mas recebo a mensagem de erro “Conversion error from string”.

Para utilizar parâmetros em uma cláusula IN, você deverá concatenar o texto ao invés de usar o ParamByName. Na verdade, aparentemente o ParamByName não traz suporte para utilizar a cláusula IN.

Se você tem um componente TEdit com o texto “11, 12, 13” preenchido pelo usuário, uma das alternativas para montar a instrução é apresentada abaixo:

É possível disparar Stored Procedures pelo Delphi?

Sim! Já existe alguns componentes no Delphi que permitem a execução de Stored Procedures, como seguem:

Para configurá-lo, basta setar a propriedade de conexão e o nome da Stored Procedure que está no banco. Por fim, para dispará-la, chame o método ExecProc:

Tentei configurar uma máscara de moeda (R$) em um campo Aggregates, mas ela não é apresentada.

As versões do Delphi anteriores à 2010 contém um bug na formatação de campos Aggregates. Mesmo inserindo máscaras ou alterando a propriedade currency para True, o valor não é formatado. Neste caso, uma alternativa é formatar o valor no próprio código-fonte em tempo de execução:

Segundo alguns sites, também há uma opção de corrigir esse bug do Delphi. Na unit “DB.pas”, encontre o método TAggregateField.GetText e substitua a linha:

por:

Lembrando que, se essa alteração for realizada, a unit “DB.pas” deverá ser recompilada para que a correção entre em vigor.

Como alterar apenas a cor de uma coluna na DBGrid conforme uma condição específica?

Para alterar a cor de uma única coluna é preciso envolver o código de pintura em uma função IF que identifique a coluna. Utilizando um cenário de contas em atraso como exemplo, essa seria a codificação no evento OnDrawColumnCell do componente TDBGrid:

Existe uma forma de aumentar a altura da linha da DBGrid?

Sim, é possível. Para isso, é preciso criar um “Hack” da classe TDBGrid (ou “class cracker”) e alterar o valor da propriedade DefaultRowHeight:

Porém, quando o TDBGrid recebe os dados, a altura da linha volta ao tamanho padrão. Há 2 alternativas para tratar esse comportamento:

  • Colocar o código de alteração da altura da linha no evento AfterOpen do DataSet. Dessa forma, toda vez que o DataSet for aberto e a TDBGrid receber os dados, o tamanho da linha será alterado;
  • Criar um componente herdado da classe TDBGrid (por exemplo, TMinhaDBGrid) e adicionar os comportamentos desejados, embora essa opção demande um pouco mais de tempo.

O método AddIndex não existe no componente TADOTable, então como trabalhar com ordenação?

Para os componentes do conjunto ADO, realmente há uma diferença. Ao invés de utilizar a propriedade IndexName, deve-se trabalhar com a propriedade Sort:

É possível enviar um e-mail pelo Outlook através do Delphi?

Sim, e é bem simples! Primeiro, declare a unit ComObj na uses da sua classe:

 E no botão para enviar o e-mail, use este código:

Ao enviar o e-mail pela segunda vez consecutiva, ocorre a mensagem: “Error creating SSL context”.

É preciso liberar a DLL após o envio da mensagem para evitar este erro. Declare a unit IdSSLOpenSSL na uses da classe:

Em seguida, após o envio do e-mail, inclua a seguinte instrução (recomendo colocá-la no Finally):

Existe alguma função no Delphi que implemente algo como o IIF do FoxPro da Microsoft?

Infelizmente a função IIF não está disponível no Delphi, mas existe uma função semelhante, chamada IfThen. Confira a documentação dessa função neste link.

Como exibir a porcentagem de envio do e-mail no Delphi, por exemplo, com o TProgressBar?

Uma vez já fui questionado sobre TProgressBar no envio de e-mails. Ao contrário de conexões FTP, que nos permitem “acompanhar” a transferência de dados, as conexões SMTP não retornam o andamento do envio para que possamos exibi-lo em uma barra de progresso.

Alternativamente, podemos preencher a barra de progresso, manualmente, a cada passo: no comando Connect, preenchemos uma porcentagem da barra; em seguida, no comando Authenticate, aumentamos a porcentagem; nas configurações da mensagem, mais uma quantia; na inserção de anexos, preenchemos mais um pouco; e, por fim, completamos a porcentagem ao enviar a mensagem. Dessa forma, controlamos o preenchimento da barra conforme as operações que são realizadas no código.

Outra opção ér criar uma tela de espera enquanto o e-mail é enviado.

Tentei enviar e-mails no formato HTML pelo Delphi, mas sem sucesso.

Para enviar e-mails em formato HTML, é preciso configurar algumas propriedades do objeto TIdMessage:

E-mails enviados em HTML contendo anexos são recebidos sem formatação, além de exibir a mensagem “This is a multi-part message in MIME format…”.

Este é um caso específico de envio que exige uma “separação” entre o corpo da mensagem e o anexo. A solução é utilizar a classe TIdText, conforme o exemplo a seguir:

De qualquer forma, clique aqui e confira o artigo do blog que trata exclusivamente desse tema!

Ao enviar o e-mail, recebo a mensagem “Could not load SSL Library”.

Para desenvolvedores que estão trabalhando com Delphi 7, recomendo utilizar uma versão específica das bibliotecas do Indy, que podem ser baixadas neste link.

Em adição, vale lembrar que as DLLs devem ser copiadas para a mesma pasta em que está o executável. As versões mais recentes do Delphi criam, por padrão, um subdiretório chamado “Debug” na pasta principal do projeto para gerar o executável compilado. Neste caso, é para esse subdiretório que as DLLs devem ser copiadas.

Há alguma forma de criptografar o conteúdo de um arquivo INI?

Infelizmente não há uma forma consistente de criptografar arquivos INI, ainda mais que, caso fosse possível, você teria alguns problemas para ler as seções e as chaves do arquivo pelo Delphi. Uma alternativa é usar uma função de criptografia para gravar os valores criptografados no arquivo INI, como, por exemplo, em vez de gravar “Delphi”, a função gravaria “5&R0@”. Ao ler a chave, basta aplicar a engenharia reversa da criptografia, resultando no valor original.

Como usar um arquivo INI para armazenar o caminho do banco de dados e realizar a conexão?

Para ler o caminho do banco de dados do arquivo INI e atribui-lo à aplicação, você pode escrever o código abaixo no evento que é disparado antes da conexão. Se você estiver utilizando dbExpress ou IBX, utilize o código no evento BeforeConnect do TSQLConnection / TIBDatabase.

A primeira linha carrega o arquivo INI na mesma pasta onde está o executável. Em seguida, a segunda linha lê o caminho do banco de dados e atribui ao componente de conexão. Por fim, a terceira linha libera o arquivo da memória.

Utilizar tabelas temporárias para relacionamentos mestre/detalhe quebra o paradigma MVC?

Na abordagem do MVC, geralmente o registro mestre e os detalhes são tratados como objetos em um relacionamento master/detail. Por exemplo, o desenvolvedor pode criar uma classe chamada “Venda” e, dentro dessa classe, criar uma lista de objetos da classe “ItensVenda”. Isso dá sentido à composição, ou seja, ao instanciar um objeto da classe “Venda”, a aplicação automaticamente cria uma lista de itens (details). Na prática, durante o cadastro de uma venda, basta preencher um objeto dessa classe (adicionando cada item na lista de objetos) e enviá-lo para a camada de persistência. Essa camada irá gravar o registro mestre e percorrer os detalhes (lista de objetos), gravando-os no banco de dados.

Por outro lado, nada impede que você utilize tabelas temporárias. Ao invés de alimentar um objeto, você pode popular uma tabela temporária criada em tempo de execução. No método de gravação, percorra os registros da tabela temporária e, para cada registro, preencha um objeto que será enviado para a camada de persistência.
O conceito pode parecer um pouco complexo, mas é bastante funcional dentro do contexto da Orientação a Objetos.

Qual a forma mais correta de programar o relacionamento Master/Detail com tabelas temporárias?

Em relacionamentos mestre/detalhe eu sempre recomendo a utilização de tabelas temporárias (ou tabelas virtuais, como muitos desenvoledores denominam).

Para exemplificar essa minha preferência, vou citar um cenário parecido com o seu caso: suponha que o usuário iniciou o cadastro de uma venda e, ao incluir os itens, os mesmos já são imediatamente gravados no banco de dados.
Porém, imagine que a energia acabe durante o cadastro de uma venda. Após a energia voltar, o usuário reabre a aplicação e recomeça a mesma venda. No entanto, o registro mestre (venda) e alguns itens da venda (detalhes) já foram gravados no banco de dados antes da energia cair. Resultado: estes registros ficarão “perdidos” no banco de dados, afetando o desempenho e provavelmente causando algum tipo de inconsistência, já que o usuário não saberá que a mesma venda já existe no banco de dados, mesmo que incompleta.

Este é o problema que a tabela temporária visa solucionar. Uma tabela temporária é um DataSet que permanece na memória do computador por um tempo determinado. Logo, neste caso, criaríamos uma tabela temporária para os itens da venda, e este seria o procedimento:

  • Os itens são gravados na tabela temporária durante o cadastro da venda, ao invés de serem gravados no banco de dados;
  • Quando o usuário clicar em “Gravar”, a venda e os itens serão, de fato, gravados no banco de dados de uma vez só, em um único método.

Isso irá reduzir os erros na sua aplicação e aumentar a confiabilidade.

Nas tabelas temporárias (TClientDataSet), é possível trabalhar com colunas definidas como Primary Key?

Ótima pergunta! Com o TClientDataSet, é possível definir um comportamento parcial de uma Primary Key. Para configurar um campo (Field) como obrigatório, basta alterar a propriedade Required para True. Entretanto, não é possível definir um campo como Unique (que não pode se repetir). Neste caso, é necessário controlar este comportamento manualmente, checando se o valor desse campo já existe na tabela antes de gravar o registro.

A respeito do MVC, como padronizar o local de units compartilhadas, como aquelas para trabalhar com arquivos de apoio?

A localização dos arquivos de configuração/utilidade é particular de cada desenvolvedor.
Na verdade, o MVC apenas denota a prática de separar as classes de visão, modelagem e controle em diferentes camadas. Geralmente, eu crio 4 diretórios adicionais:

  • DAO: arquivos da camada de acesso a dados (mesmo que a princípio seja uma responsabilidade da Model, achei mais interessante separar as responsabilidades de modelagem das classes de persistência);
  • Dados: arquivo do banco de dados;
  • Util: classes de validações, backup, envio de e-mails e componentes em comum;
  • Arquivos: demais arquivos utilizados na aplicação (imagens, arquivos INI, XML, JSON, etc…).

Pode-se ainda criar uma pasta para o projeto, contendo o DPR e as DLLs necessárias (como o fbclient.dll para Firebird).
Mas volto a repetir: nada impede que você crie quantas pastas forem necessárias no diretório da sua aplicação. O importante é separar as unidades principais.

Na prática, qual o benefício do MVC na estrutura do projeto?

O MVC é um padrão de arquitetura que tem como objetivo principal a separação de responsabilidades das classes do projeto. Dessa forma, uma das vantagens é a versatilidade dos componentes.

Imagine, por exemplo, um determinado componente da aplicação que foi construído em MVC. Em certo momento, identificou-se que este componente terá de ser utilizado em outra parte da aplicação, mas com um visual diferente. Neste caso, basta substituir somente a View e reutilizar o Model e o Controller. Em outras palavras, pode-se dizer que “removemos” a View do componente e “encaixamos” outra conforme a nossa necessidade. As regras de negócio (Model) e os controladores (Controller) serão os mesmos.

Se o seu projeto contemplar esse tipo de cenário, utilizar o MVC será um grande benefício. Além dessa vantagem, ainda podemos citar a facilidade de manutenção, baixo acoplamento e, claro, redução e organização do código-fonte.

Como os padrões de projeto se relacionam com MVC?

Design Patterns são extremamente úteis ao utilizar MVC. Não é uma regra, mas como são grandes aliados da Orientação a Objetos, podem complementar ou enriquecer a arquitetura do projeto.

Por exemplo, na empresa em que trabalho, utilizamos o Singleton para acessar classes compartilhadas, Builder para construir componentes em tempo de execução, Factory Method para criar Datasets, Observer para notificar componentes ao disparar eventos e Strategy para tratar algumas regras de negócio. Já que a maioria dos padrões de projeto fazem uso de Interfaces, fica relativamente fácil acoplá-los ao MVC.

Vale ressaltar que padrões de projeto podem ser utilizados em qualquer ocasião. Sempre que o desenvolvedor se deparar com uma situação na qual um padrão de projeto possa apresentar uma solução, é altamente recomendável implementá-lo, mesmo se não estiver utilizando um padrão de arquitetura.

Existe muita diferença entre MVC, MVP, MVVM e MGM?

Sim, há algumas diferenças entre esses padrões de arquitetura, principalmente relacionadas às responsabilidades de cada camada, comportamentos dos objetos e a forma como as interações são implementadas. Embora tenham objetivos em comum, os padrões se diferem na forma como são manipulados na arquitetura do projeto. Vale lembrar que estes padrões são flexíveis, logo, nada impede que um desenvolvedor estenda as camadas com novas responsabilidades ou modifique regras de interação, desde que não fuja das diretrizes que o padrão propõe.

Explicar cada um dos padrões resultaria em uma resposta muito extensa, portanto, sugiro acessar este link para compreender melhor as diferenças entre eles. O artigo apresenta características bem detalhadas de cada padrão.

Além dos quatro padrões mencionados nessa questão, há também o MOVE, no qual já publiquei um artigo a respeito. O MOVE sugere uma estrutura um pouco diferente quando comparada aos outros padrões, mas traz uma boa proposta de arquitetura.

Quais as principais vantagens e desvantagens entre programar com DAO e ORM?

Ótima pergunta. Tanto DAO (Data Access Object) quanto ORM (Object–Relational Mapping) são camadas de persistência, mas o ORM tira mais proveito da Programação Orientada a Objetos. O objetivo do ORM é evitar que haja um retrabalho ao criar a modelagem das tabelas no banco de dados e a modelagem das classes que representam essas tabelas. Em outras palavras, o ORM permite, por exemplo, que uma ferramenta leia a estrutura de uma tabela e gere uma classe que a represente (em que os campos da tabela são convertidos em atributos da classe).

A camada DAO geralmente não fornece recursos dessa natureza, mas é útil para assumir como intermediária entre a aplicação e o banco de dados, ou seja, receber os dados, gerar instruções SQL e garantir a persistências dos dados através de transações.

Para realizar o backup de um banco de dados em Firebird, posso somente copiar o arquivo com extensão FDB?

Para fazer uma cópia de segurança (backup), você pode, sim, apenas copiar o arquivo FDB para outro diretório ou dispositivo. Porém, à medida que o banco de dados armazena mais informações, aumentando de tamanho físico, a cópia pode se tornar demorada e, talvez, inconsistente. Por esse motivo, recomendo o uso de um utilitário chamado gbak, disponibilizado na pasta bin do próprio Firebird. Além de confiável, este utilitário é exclusivo para essa finalidade e reduz o tamanho do arquivo em aproximadamente 70%.

Confira abaixo um exemplo de backup e restauração do banco de dados:

Com o Delphi, você pode utilizar o ShellExecute para realizar o backup.
Primeiro, copie o gbak.exe para dentro da pasta da sua aplicação (ele é bem pequeno), e utilize o comando abaixo para disparar a função de backup:

Qualquer pessoa que tenha o Firebird instalado pode acessar o meu arquivo FDB com as credenciais padrão?

O usuário e senha padrão de bancos de dados Firebird são SYSDBA e masterkey, respectivamente. Porém, com o utilitário gsec.exe, é possível alterar estes dados e definir novas credenciais de administração do banco de dados. Sendo assim, mesmo que o arquivo FDB seja transportado para outro computador que tenha o Firebird instalado, não será possível acessá-lo com as credenciais padrão. Esse procedimento garante que somente o criador do banco de dados tenha acesso ao conteúdo das tabelas.

Como executar scripts em um banco Firebird?

Bom, eu recomendo o isql utilizando o parâmetro “-o” para gerar um arquivo de log, como no exemplo abaixo:

Após a execução do script, é possível ler o arquivo de log e verificar se houve algum erro. Outra alternativa é utilizar o método CreateProcess para exibir o resultado das instruções do script na tela do usuário.

Como trabalhar com o relacionamento entre as tabelas “Nota Fiscal” e “Itens da Nota Fiscal”?

Um relacionamento Master/Detail ocorre quando há uma tabela pai (no seu caso, a Nota Fiscal) e uma tabela filha (Itens da Nota Fiscal), e é necessário interligá-las através de um campo, no qual chamamos de chave estrangeira.
Pra ficar mais fácil, acompanhe a lógica:

  • Uma nota fiscal pode ter 1 ou N produtos
  • Um produto pode existir em 1 ou N notas fiscais

A letra “N” significa uma quantidade variável (1, 5, 10, 25…).
Quando nos deparamos com essa regra, dizemos que há uma relação de N para N, também denominada “N:N” ou “muitos-para-muitos”. Neste caso, é necessário criar uma tabela intermediária para acoplar dados das duas tabelas. Essa tabela irá receber a chave primária da tabela “Nota Fiscal” e também a chave primária da tabela “Produtos”, formando a tabela “Itens da Nota Fiscal”.

Isso significa que, se uma nota fiscal for excluída, os itens do nota fiscal também deverão ser automaticamente excluídos. Em outras palavras, os itens da nota fiscal são órfãs da nota fiscal. Tecnicamente, dizemos que a “Nota Fiscal” é a tabela mestre e os “Itens do Nota Fiscal” é a tabela de detalhes. Portanto, temos um relacionamento Mestre/Detalhe:

  • Mestre: Tabela “Nota Fiscal”
  • Detalhe: Tabela “Itens da Nota Fiscal”

Este conceito, em UML, se chama Composição.

No Delphi, quando precisamos trabalhar com Composição, normalmente utilizamos tabelas temporárias. No blog há três artigos sobre como trabalhar com tabelas temporárias no Delphi. Espero que possam lhe ajudar!

Qual a melhor opção para consultar e exibir dados para o usuário: Views ou Stored Procedures?

Eu geralmente analiso o tipo de conteúdo que será exibido, ou seja, se for um conjunto de dados oriundo de duas ou mais tabelas (joins) e o intuito é só exibí-los para o usuário de forma estática, recomendo utilizar Views. Por outro lado, se você precisa realizar algum processamento nos dados antes de exibí-los (como concatenações, formatações e cálculos), então eu sugiro que utilize Stored Procedures.

Como evitar o risco de duplicidade quando mais de um usuário estiver gravando registros ao mesmo tempo?

Essa é uma questão que é tratada de diferentes formas. Uma delas é por meio de Triggers no banco de dados. Este é um recurso que funciona como um “gatilho” quando determinado evento acontece em uma tabela, como, por exemplo, a inserção de um novo registro (On Before Insert). Sendo assim, pode-se criar uma Trigger para gerar um novo código antes que o registro seja inserido. Mesmo que dois usuários gravem registros no mesmo milésimo de segundo, o banco de dados irá analisar as transações e disparar essa Trigger para cada um deles em sequência.

Há também uma forma de controlar essa concorrência pela aplicação. Porém, antes de explicá-la, vale fazer uma observação: muitos desenvolvedores cometem o erro de gerar o novo valor da chave primária (código do cliente, por exemplo), durante a inserção do novo registro. Logo, se dois usuários iniciarem a inserção de dois registros ao mesmo tempo, o mesmo código será gerado, causando a violação de chave primária.

A minha recomendação é atribuir o valor da chave primária antes da gravação do registro, ou seja, ao persistir os dados no banco com o Post e ApplyUpdates. Para isso, consulte o último código da tabela e incremente 1 unidade, atribuindo-o imediatamente ao campo do DataSet. Caso dois usuários insiram dois registros simultaneamente, a possibilidade de duplicidade será bastante reduzida.

Muitos profissionais recomendam a utilização da nuvem para backup de bancos de dados. Sempre utilizei estações na rede para backups e nunca tive problemas.

Utilizar computadores da rede para gravar backups é um método aceitável. Porém, imagine que aconteça um problema com essas estações, como um dano grave no HD. Todos os backups seriam perdidos. A vantagem de utilizar a nuvem é justamente evitar este risco, pois, mesmo se houver uma falha em todos os computadores da empresa, os backups continuariam salvos.

Para essa propósito, pode-se utilizar um servidor web para armazenar um espelho do banco. Ao final de cada dia, o banco de dados é transferido para este repositório como uma forma de cópia de segurança. Se o arquivo se tornar volumoso, experimente compactá-lo antes de enviar. E mais uma dica: agende um horário propício para fazer o envio (durante a madrugada, por exemplo), para não prejudicar a utilização da aplicação.

Estou iniciando um projeto de software para uma empresa que possui várias filiais. Neste caso, como ficaria o banco de dados? Um para cada cliente ou um banco único para todos os clientes?

Depende de como estes bancos de dados serão trabalhados.

Por exemplo, se você precisa integrar dados entre as filiais, como vendas, estoque, clientes ou outras informações, recomendo que utilize um único banco de dados para todo o sistema.

Por outro lado, se as filiais trabalharem independentemente, ou seja, cada uma manter seus próprios dados, é mais apropriado criar bancos de dados distintos, inclusive por contribuir com o desempenho da aplicação.

Há ainda outra modalidade conhecida como replicação. Esse recurso permite que os clientes utilizem bancos de dados diferentes e, periodicamente (uma vez por dia, por exemplo), estes dados são sincronizados em um único banco de dados chamado Base Mestre. Caso seja necessário consultar dados ou gerar relatórios gerenciais com estatísticas de diferentes filiais, basta consultar essa base.
Apesar de funcional, a implementação da replicação é relativamente complexa.

Como resolver o problema de códigos duplicados no projeto?

É possível sim! Existe uma técnica na área de programação conhecida como Clean Code. Um dos conceitos dessa técnica consiste na prática de Refatoração, ou seja, extrair uma porção do código-fonte que é utilizada em mais de um local no projeto.

No seu código, você pode praticar a Refatoração e mover as linhas duplicadas para um método. Em seguida, nos locais onde as linhas estavam duplicadas, basta chamar este método. Isso inclusive contribui para facilitar a manutenção.

Para contribuir com a minha resposta, sugiro que você leia o artigo sobre Sub-Rotinas aqui mesmo no blog. Além disso, muita coisa pode ser melhorada com a utilização de Design Patterns, apesar de exigir um pouco mais de domínio em orientação a objetos.

Como controlar a ativação de softwares, de forma que o sistema não seja utilizado sem autorização em outros computadores?

Existem algumas opções eficientes para implementar esse bloqueio, entre elas, o uso do número serial do HD (HD Serial).

Nessa modalidade, a aplicação somente é executada em computadores nos quais o número serial está autorizado. Para aprimorar a segurança, alguns desenvolvedores criam um sistema de criptografia para retornar uma chave de licença baseada no número serial do HD. O procedimento é este:

  • O cliente fornece o número serial do HD para o desenvolvedor;
  • O desenvolvedor entra com o número serial do HD em um sistema de criptografia e cria uma chave de segurança;
  • O desenvolvedor fornece a chave de segurança para o cliente para que ele possa abrir a aplicação.

Outra opção viável é utilizar a nuvem para fazer essa validação, caso o cliente sempre esteja conectado à Internet. Por exemplo, toda vez que a aplicação for executada, uma rotina pode comparar o número serial do HD com os números seriais autorizados em algum servidor na web.
O mesmo pode ser feito também com o endereço MAC do computador.

“Interface” é o visual de uma aplicação, certo?

O termo “Interface” no cenário de TI é bem ambíguo. Sim, Interface pode ser o visual da aplicação que é apresentado ao usuário, porém, no contexto da Orientação a Objetos, Interface é o nome que se dá a um recurso para trabalhar com abstrações.

O objetivo das Intefaces é prover flexibilidade na estrutura de software de modo que resulte em um baixo acoplamento (dependência) entre classes. Ao invés de trabalhar com implementações concretas, as Interfaces podem ser utilizadas para representar as abstrações no projeto de software em nível de modelagem.

Sobre ser júnior, pleno ou sênior, faltou abordar sobre certificações. Se um trainee tira uma certificação, qual seria o seu nível?

Realmente seria interessante ter abordado a questão das certificações, já que se tornaram muito importantes, principalmente para profissionais de TI.

Eu diria que, embora uma certificação prove que o profissional tenha conhecimento amplo do tema estudado, ela não é o suficiente para transformar um profissional do nível Júnior em um Sênior. Digo isso porque há muita prática envolvida nessa escala. Um desenvolvedor Sênior, por exemplo, possui uma experiência empírica que é adquirida somente com o trabalho desempenhado no dia a dia. Em outras palavras, podemos afirmar que a certificação oferece um embasamento teórico, enquanto o trabalho proporciona um conhecimento prático. Combinar essas duas vertentes é o grande ideal. O encontro do conhecimento teórico com o prático resulta no profissionalismo de alta competência.

Mesmo assim, ressalto que, caso você seja um nível Júnior e se certifique na área em que trabalha, as chances de evoluir para o nível Pleno são grandes, já que, essencialmente, a certificação irá aprimorar a sua perspectiva no trabalho.

Qual a diferença entre variável e constante?

A maior diferença entre variáveis e constantes são que essas últimas não permitem que o valor seja modificado.

Já me deparei com situações em que valores de variáveis foram indevidamente alterados durante a execução da aplicação, gerando inconsistências. Ao trocar essas instâncias por constantes, o problema foi solucionado. O uso de constantes também expressa que o valor é fixo, e não variável. Em um ambiente colaborativo de desenvolvimento, essa informação pode ajudar a compreender melhor a regra de negócio.

Além disso, a utilização de constantes no projeto agiliza, e muito, a atividade de manutenção, sem contar na melhoria da expressividade do código. Quanto mais o código puder “falar por si só”, melhor será a compreensão.

No caso de propriedades científicas e matemáticas, que possuem valores imutáveis, ainda recomendo a utilização de constantes, já que nem todos os desenvolvedores têm conhecimento dos valores dessas propriedades. Por exemplo, ler uma constante chamada nVALOR_PI é mais compreensível do que o magic number “3,14”.

 

Obrigado pela visita e até a próxima, pessoal!


 

André Celestino