Programação - WEB arquivo - Sefaz-BA

Propaganda
SEFAZ-BA
SGF
DTI
GEDES
Secretaria da Fazenda do Estado da Bahia
Superintendência da Gestão Fazendária
Diretoria de Tecnologia da Informação
Gerência de Administração de Dados e Desenvolvimento de Sistemas
BOAS PRÁTICAS DE
PROGRAMAÇÃO
WEB
Versão 01.00.00
Salvador (Ba), maio de 2017
Secretaria da Fazenda do Estado da Bahia
29/05/17
DTI - Diretoria de Tecnologia da Informação
CONTROLE DE VERSÃO
Versão
Data
01.00.00 01/10/2009
Responsável
Equipe AD
(Padrões)
Página 2
Histórico
1. Versão inicial;
582807064
Secretaria da Fazenda do Estado da Bahia
29/05/17
DTI - Diretoria de Tecnologia da Informação
ÍNDICE
1.
INTRODUÇÃO ................................................................................................................................. 4
2.
CONSIDERAÇÕES GERAIS .......................................................................................................... 5
2.1.
3.
BOAS PRÁTICAS .NET ................................................................................................................... 6
3.1.
3.2.
3.3.
3.4.
3.5.
3.6.
3.7.
3.8.
3.9.
3.10.
3.11.
4.
ORGANIZAÇÃO DE CÓDIGO ................................................................................................. 5
CONSTANTES .......................................................................................................................... 6
STRINGS .................................................................................................................................... 6
VARIÁVEIS DE SESSÃO ......................................................................................................... 6
PASSAGEM DE PARÂMETROS.............................................................................................. 6
CAMADA DE DADOS .............................................................................................................. 7
DISPOSE .................................................................................................................................... 9
POSTBACK.............................................................................................................................. 10
MAPEANDO CAMINHO ABSOLUTO APLICAÇÃO ........................................................... 10
DATASET E DATAREADER ................................................................................................. 10
NOTHING ................................................................................................................................ 11
XML ......................................................................................................................................... 12
BOAS PRÁTICAS ASP .................................................................................................................. 13
4.1.
4.2.
4.3.
4.4.
4.5.
UTILIZAÇÃO DE CSS ............................................................................................................ 13
CÓDIGO HTML LIMPO .......................................................................................................... 13
IMAGENS DE CABEÇALHOS E RODAPÉS DA SEFAZ ..................................................... 13
BARRA DE ROLAGEM .......................................................................................................... 13
COMPATIBILIDADE ENTRE IE E FIREFOX ....................................................................... 13
Página 3
582807064
Secretaria da Fazenda do Estado da Bahia
29/05/17
DTI - Diretoria de Tecnologia da Informação
1. INTRODUÇÃO
O objetivo deste documento é fornecer algumas orientações referentes a boas práticas de
programação para nortear a manutenção e o desenvolvimento de aplicações da SEFAZ baseadas
na arquitetura Web e contribuir para a melhoria de sua qualidade. Ele aborda as tecnologias
ASP, VB.NET e JavaScript.
Página 4
582807064
Secretaria da Fazenda do Estado da Bahia
29/05/17
DTI - Diretoria de Tecnologia da Informação
2. CONSIDERAÇÕES GERAIS
2.1. ORGANIZAÇÃO DE CÓDIGO
A organização de código é um fator muito importante para garantir uma boa legibilidade e
entendimento do que foi implementado, facilitando, assim, a sua manutenção. Para tanto, alguns
fatores devem ser considerados:
 A utilização de comentários explicativos facilita o entendimento do código, documentando
o que foi feito. Eles devem ser claros e objetivos, de forma que o código não fique poluído.

Comentários de documentação: têm por objetivo descrever a funcionalidade. Deve-se
utilizar este tipo de comentário nos trechos de código mais importantes para a regra de
negócio.

Comentários de implementação: fornecem uma explicação sobre a forma de
implementação em relação aos controles de fluxo, iterações, recursos da linguagem
utilizados e trechos de código que não foram especificados e não fazem parte do
negócio, mas sim da solução implementada.
 Qualquer tipo de código desnecessário, como blocos comentados e variáveis não utilizadas,
deve ser retirado para não dificultar o entendimento e a manutenção.
 Cada comando deve estar disposto separadamente em uma ou mais linhas. A utilização de
vários comandos em uma mesma linha compromete a legibilidade do código, tornando-o
mais complexo.
 O recurso Region (#Region em VB .NET), fornecido pelo Visual Studio .Net, permite que os
métodos sejam agrupados de acordo com a similaridade de função, o que viabiliza o uso do
recurso de recolher e expandir visualmente conjuntos de métodos.
 Indentação e organização do código em blocos lógicos facilitam a legibilidade e
manutenção.
 Variáveis, constantes, métodos e objetos utilizados na implementação devem ter
nomenclatura clara, sempre tentando representar de forma mais intuitiva seu significado de
acordo com o negócio ou utilização.
 A adoção de padrões de nomenclatura para variáveis, constantes, métodos e objetos é muito
importante para a organização e entendimento do código.
Página 5
582807064
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
3. BOAS PRÁTICAS .NET
3.1. CONSTANTES
O uso de constantes é indicado na representação de dados que não sofrem alteração durante a
execução do código, evitando valores fixos espalhados, que dificultam muito a manutenção.
Uma boa prática é centralizar suas definições e atribuições numa classe separada. Este conceito
de constantes globais aplica-se no caso daquelas que são utilizadas por várias classes do projeto
e devem ser declaradas como Friend , nunca como Public.
3.2. STRINGS
Ao concatenar strings repetidas vezes, deve-se usar o objeto StringBuilder
(System.Text.StringBuilder). Isso evitará que, a cada alteração da string, seja alocado um novo
bloco de memória.
Para máxima performance, deve-se instanciar o StringBuilder com tamanho igual ou maior que
a quantidade máxima de caracteres a ser armazenada e nunca reaproveitar o objeto após a
extração da string com o método ToString.
Se a concatenação foi restrita a até 3 vezes, pode-se usar concatenação de strings com o
operador +=, caso contrário se recomenda o uso de StringBuilder.
3.3. VARIÁVEIS DE SESSÃO
O programador deve ter bastante cautela quando decidir usar variáveis de sessão na sua
implementação, pois elas são armazenadas na memória e o desempenho da aplicação pode ser
comprometido, dependendo do número de usuários da aplicação, quantidade e complexidade
dos objetos armazenados em sessão.
Se a solução realmente precisar deste recurso, deve-se limpar as sessões que não estão mais
sendo utilizadas para liberar espaço de memória.
Deve-se atentar para o nome dado às variáveis de sessão para que não haja sobreposição de
outra variável em outro contexto.
3.4. PASSAGEM DE PARÂMETROS
É muito importante indicar, na declaração dos métodos, o tipo de passagem dos seus
parâmetros. Existem duas formas de um método receber os parâmetros: por valor (ByVal em VB
.NET) e por referência (ByRef em VB .NET).
Em ambientes centralizados, todos os objetos são passados por referência, mesmo que na
declaração do método esteja explícito que o argumento é ByVal. Em ambientes distribuídos, a
passagem de parâmetros ocorre tal como é definida no método. Deve-se ter muita atenção
quando os parâmetros forem objetos porque o custo relacionado à passagem por valor é muito
alto.
Página 6
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
3.5. CAMADA DE DADOS
As classes deste grupo isolam o resto da aplicação de tudo que esteja relacionado à manipulação
dos dados. Elas fornecem informações para as regras de negócio de forma mais simplificada e
modificam o conteúdo do banco de dados sob a orientação dessas regras, isolando as
funcionalidades e os detalhes da implementação física. Em .NET, pode-se usar o ADO.NET,
que é um conjunto de assemblies contidos no .NET Framework que tem por objetivo prover a
comunicação com os bancos de dados.
Como boa prática em termos de arquitetura de aplicação .NET, sugere-se a divisão em camadas
funcionais. A utilização de uma camada de dados encapsulando o ADO.NET, separada da
camada de regra de negócios, garante a padronização deste componente para as funcionalidades
mais comuns, promovendo facilidade de manutenção, extensão e produtividade. É importante,
portanto, identificar padrões que resolvam os tipos de problemas mais freqüentes.
A depender do banco de dados acessado pela aplicação, o ADO.NET disponibiliza vários
“Clientes” de fonte de dados. Para alcançar um melhor desempenho, deve-se escolher o
provedor mais adequado à fonte de dados que será utilizada. Abaixo estão algumas orientações
sobre os provedores:
 SQL Server .NET Data Provider: Localizado no namespace System.Data.SqlClient, é
recomendado para aplicações que utilizam banco de dados SQL Server 7.0 ou superior.
 NET Data Provider for Oracle: Localizado no namespace System.Data.OracleClient, é
recomendado para aplicações que utilizam banco de dados Oracle.
 OLE DB .NET Data Provider: Localizado no namespace System.Data.OleDb, é
recomendado para aplicações que utilizam banco de dados Microsoft Access ou SQL
Server 6.5.
 ODBC .NET Data Provider: Localizado no namespace Microsoft.Data.Odbc, é
recomendado para aplicações que utilizam fonte de dados conectadas via driver ODBC.
Esta forma de acesso a dados só deve ser utilizada em último caso.
Abaixo estão algumas orientações que devem ser seguidas como boas práticas para acesso a
dados com ADO.NET:
 As conexões devem sempre ser abertas o mais tarde possível e fechadas o mais cedo
possível;
 Sempre utilizar transações em operações que envolvam alterações de dados (UPDATE,
INSERT, DELETE) em mais de uma tabela simultaneamente;
 Sempre que for necessário, atribuir valores numa instrução SQL, seja numa cláusula
WHERE seja na atualização de um campo através de um INSERT ou UPDATE, deve-se
utilizar parâmetros em vez da concatenação de strings. Desta forma, evita-se erros e
ataques de injeção SQL, já que os parâmetros são valores com tipos definidos. Tal prática
permite que o SGBD compile o código SQL apenas uma vez, melhorando a performance,
já que, após a primeira execução, a instrução estará pré-compilada no banco de dados;
 Deve-se construir filtros e ordenações diretamente na instrução SQL, através das cláusulas
WHERE, ORDER e GROUP, e não via código.
As orientações a seguir são para aplicações que utilizam o Framework UNIFW.NET.
Página 7
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
O UniFW auxilia na implementação de componentes de acesso à base de dados de diversos
tipos. Componentes como as classes Unitech.Component.UniComponent e Unitech.Data
fornecem funcionalidades prontas de abertura de conexão, conversão de resultados de Stored
Procedures em DataSets, formatação de dados SQL.
As classes da aplicação que necessitam fazer acesso ao banco de dados devem herdar de
Unitech.Component.UniComponent: Utilizando este componente, a classe não precisa se
acoplar a uma implementação específica do ADO.NET e a string de conexão com o banco de
dados não precisa ser mantida e gerenciada em código.
O UniComponent utiliza o conceito de origem de dados para identificar o tipo de banco de
dados e sua string de conexão através de uma simples sigla. No exemplo a seguir, vê-se o uso
do método CreateDataBaseInstance, que recebe um parâmetro denominado
“minhaorigemdados”.
Dim SGBD As Unitech.Data.IDatabase = CreateDataBaseInstance(“minhaorigemdados”)
Esta forma de utilização do método CreateDataBaseInstance se aplica quando os métodos da
classe acessam diferentes origens de dados. Esta definição é feita dentro da implementação do
próprio método. Quando os métodos da classe acessam a mesma origem de dados, sua chamada
dentro dos métodos é feita sem passagem de nenhum parâmetro conforme exemplo abaixo:
Dim SGBD As Unitech.Data.IDatabase = CreateDataBaseInstance()
Se nenhum parâmetro for passado para o método, será utilizado como origem de dados um valor
padrão definido na propriedade “SiglaDataSource” existente no UniComponent. Esta definição
deve ser feita no construtor da classe conforme exemplo abaixo.
Public Sub New()
SiglaDataSource = "minhaorigemdados"
End Sub
O framework UniFW.Net oferece alguns mecanismos para controle de transações. Através do
pacote DAC, é possível controlar o funcionamento das transações (Supported, Required, etc) e
também o nível de isolamento entre elas (ReadCommitted, Serializable, etc.).
Desta forma, todo método que efetua operações de consulta em banco de dados deve possuir a
seguinte configuração antes da sua assinatura:
<TransactionContext(Transaction.Supported,Isolation:=Isolation.ReadCommitted)> _
Os métodos que efetuam operações de atualização em banco de dados devem possuir a seguinte
configuração antes da sua assinatura:
<TransactionContext(Transaction.Required,Isolation:=Isolation.ReadCommitted)> _
Estas configurações fornecem o comportamento que, normalmente, os bancos de dados
oferecem como padrão. Se a especificação do software contiver tais informações, deve-se,
obrigatoriamente, utilizar as configurações determinadas. O contexto da transação possui quatro
possíveis comportamentos:
 Disabled: Suporte a transação está desabilitada.
 NotSupported: O objeto nunca participa da transação.
Página 8
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
 Supported: O objeto participa da transação, se existir.
 Required: Se o objeto existir, participa da transação, caso contrário cria uma nova
transação.
 RequiredNew: O objeto sempre cria uma nova transação.
O UniComponent fornece aos componentes de negócio/dados a funcionalidade de geração de
números seqüenciais, baseada no recurso existente no UniFW.Net, com configuração através de
sua interface de administração. Tais seqüenciais únicos podem servir, por exemplo, como
chaves primárias.
O método GetNewNumber do UniComponent recebe como parâmetro a sigla da seqüência
desejada. Segue um exemplo:
Dim X as Integer = GetNewNumber(“minhasequencia”)
3.6. DISPOSE
Uma característica muito interessante da plataforma .NET é o gerenciamento automático de
memória, feito pelo Garbage Collector, que é responsável por liberar os recursos da memória
de objetos gerenciados quando eles não estão mais em uso.
Apesar do .NET oferecer esta solução, deve-se atentar para o uso eficiente da memória, de
forma que o desempenho da aplicação não seja comprometido. Nunca se sabe quando o
Garbage Collector vai liberar os recursos que não estão mais sendo utilizados pela aplicação.
Além disso, ele não reconhece recursos não gerenciados.
Por conta disso, existem no .NET objetos que implementam a interface IDisposable através do
método Dispose, que permite ao programador liberar os recursos no momento que precisar, sem
depender do finalizador acionado em um momento desconhecido, o que pode comprometer o
sistema como um todo por acúmulo de recursos inativos em memória.
Recomenda-se que os objetos que implementam a interface IDisposable
tenham
obrigatoriamente a chamada do método Dispose explicitada. Esta chamada deve ser feita,
preferencialmente, na zona Finally de um bloco Try/Catch/Finally, já que todo código presente
nesta zona será executado, independentemente da ocorrência ou não de erros. Outro ponto a ser
observado é que a chamada do Dispose deve ser feita após a verificação de não nulidade do
objeto, já que algum erro pode ter ocorrido no momento da criação da sua instância. Segue
abaixo exemplo:
Private Sub CarregaDadosTela()
Try
// Código Imaginário de manipulação do DataSet
Finally
If Not LdstDados is Nothing then
LdstDados.Dispose()
End If
End Try
End Sub
Página 9
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
3.7. POSTBACK
O recurso de ViewState está presente na maioria dos controles Web e serve para que estes
possam manter, automaticamente, seus valores em caso de atualização da página.
Antes da atribuição de valores em controles que possuam este recurso, o programador deve
verificar se ocorreu uma atualização na página ou não, para evitar processamento desnecessário
por conta de obtenção de valores no banco de dados ou sincronização de valores com controles
da tela.
O recurso de BookMark do UniFW.NET, por exemplo, mantém seus valores em caso de
atualização, devendo estar dentro do teste de PostBack. Recomenda-se utilizá-lo sempre que
possível. Abaixo segue exemplo:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles MyBase.Load
If Not IsPostBack Then
' Adiciona página ao bookmark
Unitech.Web.Util.BookMark.Marcar()
Dim LobjClasseBD As ClasseBD = New ClasseBD
InnerDataSet = LobjClasseBD.ObterTodos()
SyncForm()
End If
End Sub
3.8. MAPEANDO CAMINHO ABSOLUTO APLICAÇÃO
Se, durante o desenvolvimento, houver a necessidade de se mapear o caminho absoluto
referente à pasta onde a aplicação Web está localizada, não é indicado o mapeamento fixo no
código, porque este caminho pode variar dependendo da estrutura de pastas do ambiente em que
a aplicação está rodando.
Recomenda-se o uso do método AppDomain.CurrentDomain.BaseDirectory(), existente no
FrameWork .NET, para obter esta informação. Este método irá retornar o caminho físico da
aplicação que está sendo executada conforme configurado no IIS.
3.9. DATASET E DATAREADER
Até a versão 1.1, o ADO.NET fornecia dois objetos para retornar e armazenar dados em
memória.
Um deles é o DataReader, que fornece um acesso conectado somente-leitura e somente-parafrente a uma fonte de dados. A utilização deste objeto é recomendada quando:
 A cache de dados não é necessária;
 Só é necessário trabalhar com uma tabela de dados por vez;
 Quando os dados são acessados de forma rápida, somente-leitura e somente-para-frente.
Ao final da leitura, deve-se fechar o DataReader. Se a conexão existir apenas para retornar o
DataReader, deve ser fechada imediatamente depois deste.
O DataReader não pode ser trocado entre camadas, somente um pode ser aberto por vez.
Página 10
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
Quando há a necessidade de uso prolongado do objeto DataReader, aconselha-se a opção dos
Datasets, que são sempre desconectados (isso aumenta a escalabilidade). O objeto DataSet é
uma representação relacional dos dados em memória, incluindo tabelas que contém dados,
restrições de dados e relacionamentos entre as tabelas. Os DataSets são estruturas complexas
similares a um banco de dados relacional, que provêem um conjunto de objetos dispostos de
maneira hierárquica (tabelas, linhas e colunas). Sua utilização é recomendada quando:
 É necessário manipular dados de Banco de Dados, XML, Planilhas Eletrônicas, etc;
 A cache de dados é necessária;
 É necessária a navegação entre múltiplas tabelas de resultados;
 A aplicação precisa trocar dados entre diferentes camadas.
As tabelas de um DataSet e as linhas e colunas de uma tabela podem ser acessadas de duas
formas: pelo índice do elemento ou pelo nome do elemento. O acesso por índices não é o
recomendado, pois eventuais mudanças na estrutura do DataSet implicam um re-trabalho de
correção dos índices, além do custo de processamento no processo de identificação dos locais
onde são realizados acessos por índices.
Desta forma, recomenda-se que o acesso aos elementos de DataSets seja feito por meio dos seus
nomes, conforme exemplo abaixo.
LdstExemplo.Tables(NOME_TABELA).Rows(0)("Dominio") = "SEFAZ"
LdstExemplo.Tables(NOME_TABELA).Rows(0)("Login") = "almamorim"
LdstExemplo.Tables(NOME_TABELA).Rows(0)("Nome") = "Augusto Amorim"
Quando for necessário testar a nulidade de campos de DataSets, deve-se utilizar DBNull.Value.
Nas aplicações desenvolvidas com o framework UniFW.NET, existe a possibilidade de se
utilizar um DataSet chamado InnerDataSet, que existe para armazenamento dos dados obtidos
por algum método ou passados a algum método de uma classe de negócio. Ele pode ser
utilizado em conjunto com os métodos SyncData e SyncForm.
O método SyncData preenche os campos do InnerDataSet com os dados da interface Web; de
maneira inversa, o método SyncForm preenche os campos da interface Web com os dados
armazenados no InnerDataSet. Os campos na interface devem ter o mesmo nome das colunas
no InnerDataSet. Não é preciso passar parâmetros aos métodos e estes devem ser chamados
dentro de um IF Not IsPostBack, por questões de performance.
3.10. NOTHING
Para verificar se um objeto não foi inicializado (não teve sua instância criada), o VB.NET
oferece a palavra reservada Nothing. A comparação com “Is Nothing” só é verdadeira quando o
objeto não está inicializado ou quando se trata de um parâmetro para o qual foi passado
Nothing.
Nothing também representa um valor numérico nulo (zero). A comparação de valores numéricos
nulos, entretanto, não deve ser realizada por meio da expressão “= Nothing”, mas sim “= 0”,
porque Nothing representa uma referência, e não um valor.
Quando se trata de comparações com strings, a expressão “= Nothing” é verdadeira para strings
vazias, mas recomenda-se a comparação através da expressão “= String.Empty”.
Página 11
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
3.11. XML
Extensible Markup Language é um padrão consolidado no mundo da tecnologia para organizar
e descrever informações. Devido à sua essência auto-descritiva (Dados + Metadados), a XML é
usada em todas as camadas lógicas e físicas de uma aplicação como meio para a comunicação e
a transferência de dados.
Em .NET, existe um namespace específico para a rápida manipulação e criação de informação
no formato XML:
System.Xml
Este namespace oferece classes com métodos que executam as principais tarefas relacionadas à
manipulação de dados em XML, tais como:
1.
Carregar dados XML numa estrutura hierárquica em memória (System.Xml.Serialize).
Exemplo:
XmlDocument myDoc = new XmlDocument();
myDoc.Load ("c:\\samples\\arquivo.xml");
2.
Pesquisar, no documento, valores ou atributos de um determinado TAG ou
característica. Exemplo:
XmlNodeList myList;
myList = myDoc.SelectNodes ("Book[Author/@lastname='Smith']");
3.
Criar nodes XML e permitir sua inclusão em documentos de maior hierarquia.
Exemplo:
XmlNode newNode = myDoc.CreateElement ("Book");
myDoc.DocumentElement.AppendChild (newNode);
4.
Alterar valores ou atributos. Exemplo:
// Incluindo um novo atributo
XmlAttribute newAttr = myDoc.CreateAttribute ("Title");
newNode.Attributes.Append (newAttr)
// Alterando o valor de um atributo
newNode.Attributes["Title"].Value = "Visual Studio.Net";
Página 12
Boas_Praticas_de_Programacao_Net
Secretaria da Fazenda do Estado da Bahia
DTI - Diretoria de Tecnologia da Informação
29/05/2017
4. BOAS PRÁTICAS ASP
4.1. UTILIZAÇÃO DE CSS
Os arquivos de estilo (.CSS) contêm todas as definições de formato necessárias ao
desenvolvimento dos protótipos e das aplicações (cores, tamanho e tipo de fontes, título,
tamanhos de divs, estilos de botões e componentes, etc.).
O ideal é que as páginas contenham somente tags de código html (html, body, div, table, p, br,
etc.). Qualquer definição de estilo deve ser importada dos arquivos css e definida em html como
classe ou ID.
Para maiores informações, consultar o documento Manual de Padrões - Interface WEB.
4.2. CÓDIGO HTML LIMPO
O reaproveitamento de protótipos já montados para criação de novos, normalmente, implica
sujeira no código, tornando o html pesado, com renderização lenta e suscetível à
incompatibilidade entre browsers (IE, Mozila).
O código não deve conter excessos (ex: divs dentro de tabela apenas para centralização do
conteúdo) e sua correção precisa ser garantida (ex: toda tag aberta deve ser fechada).
4.3. IMAGENS DE CABEÇALHOS E RODAPÉS DA SEFAZ
É preciso atentar para a especificação correta do caminho dos diretórios das imagens.
Para maiores informações, consultar o documento Manual de Padrões - Interface WEB.
4.4. BARRA DE ROLAGEM
Para evitar a barra de rolagem horizontal, deve-se utilizar a classe <div id="conteudo">, que
engloba as tabelas, as abas etc.
Para maiores informações, consultar o documento Manual de Padrões - Interface WEB.
4.5. COMPATIBILIDADE ENTRE IE E FIREFOX
Além de utilizar arquivos de estilo e manter o código limpo, deve-se evitar o uso de
componentes JavaScript obsoletos criados antes do surgimento do Firefox.
Página 13
Boas_Praticas_de_Programacao_Net
Download