Buscar

Dart: Uma Linguagem Orientada a Objetos

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 19 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 19 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 19 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Dart - a linguagem
Criada em 2011 com foco inicial em desenvolvimento web, Dart é uma linguagem puramente orientada a objetos, baseada em classes e de herança única. É opcionalmente tipada pois dart suporta inferência de tipos (o interpretador tenta reconhecê-los por si mesmo), e o tipo de cada objeto durante o tempo de execução é uma instância da classe Type. (Especificações da linguagem). Isso quer dizer que tudo em Dart é um objeto, que a linguagem é fortemente baseada nas classes, o conceito fundamental da programação orientada a objetos, e que você pode ou não declarar o tipo da sua variável.
Motivos para usar dart
· Compilação AoT - apps criados em dart são compilados antes de serem executados, fazendo com que rodem de forma suave;
· sintaxe semelhante às linguagens baseadas em C. Se você desenvolve em java, não terá muita dificuldade em migrar;
· Pub - por mais que o gerenciados de pacotes ainda não seja tão grande quanto o NPM, há vários pacotes voltados às plataformas nas quais a roda;
· open source - sim, você pode contribuir com a linguagem. Clique aqui para ter acesso aos repositórios no GitHub;
· animações a 60 FPS em flutter;
Instalação
Não é necessário instalar as ferramentas de desenvolvimento para acompanhar esta postagem pois os mantenedores da linguagem criaram o DartPad, que permite testar códigos Dart e criar aplicativos simples diretamente no seu navegador. Entretanto, caso deseje instalar, eis o passo-a-passo:
· Clique em get dart;
· escolha o seu sistema operacional e digite os comandos no terminal ou baixe o gerenciador de instalação; Após instalar as ferramentas para desenvolver em Dart você poderá rodar arquivos .dart através do terminal.
IDEs
IDEs, ou ambientes integrados de desenvolvimento, são como editores de texto, mas com ferramentas específicas para ajudar no desenvolvimento: autocomplete, verificador de sintaxe, debuggers etc. Há vários IDEs gratuitos. Os meus preferidos são VSCode, Atom e Android Studio, por mais que o último seja um tanto pesado para computadores com poucos recursos de memória e processamento.
Variáveis
Variáveis são como caixinhas que armazenam um valor na memória. Em vez de utilizar o código hexadecimal correspondente ao lugar dessas caixinhas na memória (o endereço delas), é possível nomeá-las, facilitando o uso dos dados que elas contém.
Imagine que você precise somar o salário de todos os gastos de uma papelaria qualquer, e que para isso, apenas jogue os números em um papel (ou calculadora) a partir dos valores encontrados nos recibos
 1000
 1500
 2000
 1000
 1250
 1000
 1300
 1500
total: 10550
Agora imagine que daqui a 1 ano os recibos tenham se apagado e você, por alguma razão, precise achar o valor que foi gasto com cadernos. Olhando a conta acima, conseguiria achá-lo? Não seria mais simples se houvesse uma "legenda" dizendo a quê se referem esses valores? Imagine a bagunça conforme os anos se passarem...
Declarar variáveis é uma das operações fundamentais na programação. Em algumas linguagens, é algo simples como
pi = 3.14
Em Dart, declará-las é feito de forma parecida, apenas colocando a palavra "var" atrás dos nomes, ou o tipo delas na mesma posição
var pi = 3.14;
// ou
double pi = 3.14;
Tipá-las previnirá alguns erros futuros e facilitará seu trabalho com IDEs (ambientes de desenvolvimento integrados). Entretanto, as guidelines da linguagem indicam que para variáveis locais (como aquelas dentro de funções), é preferível o uso de "var".
Caso queira que suas variáveis não mudem, utilize a anotação "const" ou "final" antes do tipo
final name = 'Bob';
Em outras linguagens há uma forma de mudar o nível de acesso das variáveis, ou seja, mudar o quanto elas podem ser vistas de fora da classe. Essa forma se dá através da utilização das palavras "protected", "public" e "private". Em dart, basta escrever "_" antes do nome delas para torná-las privadas.
Há dois escopos para elas: local e global. O primeiro se refere àquelas que são declaradas dentro de funções e não podem ser acessadas fora delas. O segundo, àquelas que são declaradas fora de funções e podem ser acessadas de qualquer lugar no código.
Como escolher o nome de uma variável
Ok, é mais fácil trabalhar com o nome de uma variável do que com o endereço na memória (exemplo: 0x9cf10c). Porém não pense que nomeá-las de qualquer jeito facilitará seu trabalho.
int a = 10;
O que significa "a" ? A quê esse valor se refere ? Tudo bem, você pode saber o significado, mas e as pessoas que darão manutenção ao código? O computador entenderá o que significa com ou sem nome. Todo código escrito por você deve ser direcionado às pessoas que lerão ele, então facilite o trabalho delas 👍.
Recomendo que leia o livro Código limpo para uma abordagem mais completa sobre o assunto.
· A linguagem não aceita que os nomes comecem por números, nem que contenham espaços em branco ou caracteres especiais(com exceção de "$" e "_"), e muito menos que seja uma das palavras reservadas dela.
// errado
int 1Numero = 10;
//correto 
int umNumero = 10;
Algoritmos
É um tanto clichê falar que algoritmos são como receitas culinárias, mas são mesmo: uma sequência de instruções para a execução de uma tarefa. Pense nos ingredientes como "variáveis", e no modo de preparo como a execução do seu programa. Veja uma receita simples de brigadeiro (o máximo que consigo fazer em uma cozinha 🤣):
· Ingredientes:
1 caixa de leite condensado;
3 colheres (sopa) de achocolatado;
1 colher (sopa) de manteiga;
1 xícara (chá) de chocolate granulado
Podemos dizer que o nome de cada ingrediente é igual ao nome de uma variável, e que a quantidade dos ingredientes, o valor delas. Assim sendo:
int caixaDeLeiteCondensado = 1;
int colheresDeAchocolatado = 3;
int colherDeManteiga = 1;
int xicaraDeChocolateGranulado = 1
· Quanto ao modo de preparo:
1 - Adicione o leite condensado, o achocolatado e a manteiga em uma panela;
2 - misture em fogo baixo por 10 minutos;
3 - deixe esfriar em um prato untado;
4 - enrole a mistura e adicione granulado;
Em código usando funções (última sessão do artigo):
var mistura = adicionarNaPanela(caixaDeLeiteCondensado, colheresDeAchocolatado, colherDeManteiga);
misturarEmFogoBaixo(mistura);
deixarEsfriar(mistura, pratoUntado);
enrolarEGranular(mistura);
É claro, há vários níveis de complexidade para algoritmos de uma mesma tarefa: você pode dizer que começa o dia levantando da cama, ou pode ser mais profundo e dizer que começa abrindo os olhos, se espreguiçando, levantando o tronco, sentando na cama...
Algoritmos são a base da programação. E pasme: você aprenderá sobre algumas ferramentas durante sua vida profissional, mas a maioria dos seus problemas iniciais será resultante da falta de prática com algoritmos, não com as ferramentas em si!
**NUNCA SE ESQUEÇA DE UTILIZAR ; (PONTO E VÍRGULA) AO FINAL DE CADA INSTRUÇÃO
Comentários
Comentários servem para escrever uma mensagem no código que não será interpretada pelo computador. Essa mensagem costuma ser criada para documentar o código (explicá-lo), mas não se engane: a necessidade de diversos comentários pode ser indicativo de que a escolha dos nomes das variáveis e funções, ou o uso delas, não está claro o suficiente. Um código bem escrito deve falar por si só.
Dart suporta 3 tipos de comentários:
· os de 1 única linha
// esse é um comentário de linha única
· os de múltiplas linhas
/*
 Esse é um comentário em
 múltiplas linhas
*/
· os de documentação
/// comentários de documentação podem começar com 3 barras invertidas ou /**
Boa parte dos resultados de instruções neste texto foram escritos em comentários na frente delas.
Tipos de dados
Strings
String nome = 'Lucas';
Strings são as representações de frases, palavras, caracteres etc. Sempre que for necessário armazenar uma das opções citadas, declare uma string com as palavras dentro de aspas simples ('') ou duplas(""). Elas são as responsáveis por textos em interfaces de sites e aplicativos. Em flutter, por exemplo, são o tipo de dado esperado pelo widget Text()Text("Digite o seu nome: ");
Métodos são blocos de código na forma de funções (veja ao final da postagem) que facilitam o trabalho com dados de determinados tipos. Como exemplo para strings temos "toUpperCase()", que transforma a string em letras maiúsculas, ou "split()", que separa ela em uma lista de caracteres. Esse tipo também conta com algumas propriedades, como "length", que conta o número de caracteres (espaços em branco também), que, como métodos, também são acessadas utilizando um "." após o nome da variável e o nome da propriedade em seguida
String meuNome = "Lucas";
print(meuNome.toLowerCase()); // lucas
print(meuNome.toUpperCase()); // LUCAS
print(meuNome.length); // 5
É possível concatenar duas ou mais strings, unindo-as uma única.
String nome = "Lucas";
String sobrenome = "Silva";
print(nome + ' ' + sobrenome);
Repare no espaço em branco entre as variáveis. Dart possui suporte à interpolação, que é uma maneira de criar strings sem a necessidade do operador "+"
· "$variavel" para expressões simples
String nome = "Lucas";
String sobrenome = "Silva";
print("$nome $sobrenome"); // Lucas Silva
· "${variavel.metodo}" para expressões mais complexas
String nome = "Lucas";
String sobrenome = "Silva";
print("${nome.toUpperCase()} $sobrenome"); // LUCAS Silva
Números
Dizem que ciências da computação requerem muita matemática. É e não é verdade. Não precisa ser doutor em matemática para saber programar, mas não deixará os números de lado.
Você pode ter ouvido falar que os computadores entendem códigos binários e tal. Esse tipo de abordagem aos números ("binários") se refere à base deles. Podemos utilizar binários, hexadecimais (muito utilizados em cores), octais, decimais etc.
Os números possuem basicamente dois tipos: os inteiros e os decimais. O primeiro tipo é comumente chamado de "int". O segundo, em algumas linguagens, é chamado de "float" enquanto em outras, como dart, é chamado de "double".
int umNumero = 10;
double pi = 3.14;
Várias operações matemáticas estão disponíveis para esse tipo de dados. Arredondamento, Máximo divisor comum, checar se um número é par ou ímpar etc.
Boolean
É um tipo de dados que representa 1 ou 0, sendo esses verdadeiro ou falso, respectivamente (embora essa correspondência não seja válida em Dart). Possui esse nome em homenagem a George Boole, que definiu um sistema de lógica algébrica. São muito utilizados em expressões, principalmente as condicionais.
bool verdade = true;
bool mentira = false;
Listas
É difícil falar que algum tipo de dado é mais importante que o outro, mas sério, aprenda tudo sobre listas. Saber manipular dados corretamente é o primeiro passo para conseguir um emprego na área.
Por mais que o interpretador consiga entender o tipo, é sempre interessante tipar suas variáveis para evitar erros. Logo, listas se declarariam da seguinte forma:
List<int> listaDeNumeros = [1, 2, 3];
List<String> vogais = ['a', 'e', 'i', 'o', 'u'];
Listas são como coleções de valores. As notificações de um usuário no Instagram, por exemplo, são listas de objetos chamados Notificações (veremos sobre Programação orientada a objetos na próxima postagem). A famosa lista de tarefas (todo List) é uma lista de... adivinha? Tarefas!
Já utilizou Todoist ? Pense assim:
O app é basicamente uma lista. Nessa lista, é possível adicionar e remover tarefas. Essas operações são feitas em Dart utilizando "add()"/"insert()" e remove(). O legal é que insert pode utilizar o index do novo elemento, e é possível procurar diretamente pelo elemento a ser removido no remove. Também é possível remover todas as tarefas da lista utilizando "clear()"
List<String> vogais = ['a', 'e', 'i', 'o', 'u'];
vogais.add('r'); // ['a', 'e', 'i', 'o', 'u', 'r']
vogais.remove('r'); // ['a', 'e', 'i', 'o', 'u']
vogais.remove('e'); // ['a', 'i', 'o', 'u']
vogais.insert(1, 'e'); //['a', 'e', 'i', 'o', 'u']
vogais.clear(); // [] <- lista vazia
Index, ou índices, são as posições dos elementos em uma lista. Na maioria das linguagens a contagem começa por 0. Nunca se esqueça disso!
Adicionar uma lista à outra se tornou mais simples a partir da versão 2.3 da linguagem, na qual introduziu-se o "spread operator". Caso o valor à direita possa ser null, utilize o null-aware spread operator (última linha do código) para evitar erros na execução do código.
List<int> lista1 = [4, 5];
List<int> lista2 = [1, 2, 3, ...lista1]; // spread operator ->...lista1 copia os elementos
List<int> lista3 = [...?lista2]; // null-aware spread operator 
Outras novidades são "collection if" e "collection for", onde as listas são criadas através de uma condição ou de um loop.
/// collection if
List<String> listaDeCompras = ['chocolate', 'refrigerante', if(estiveremEmPromocao)];
// collection for
List<int> primeiraListaDeNumeros = [3, 4, 5, 6];
List<int> segundaListaDeNumeros = [1, 2, for(int numero in primeiraListaDeNumeros) numero]; // [1, 2, 3, 4, 5, 6];
Uma propriedade útil das listas é "length", que retorna o número de elementos dentro delas.
List<int> listaDeNumeros = [1, 2, 3];
print(listaDeNumeros.length); // 3
Conjuntos (Sets)
Conjuntos em Dart são como conjuntos matemáticos: a ordem e a quantidade de vezes que um elemento aparece não possuem importância. Para utilizá-los, é necessário colocar um tipo antes de {}. Caso contrário, o interpretador entenderá que se trata de um Map.
var conjuntoDeNumeros = <int>{};
print(conjuntoDeNumeros is Set); // true
var outroConjuntoDeNumeros = {};
print(outroConjuntoDeNumeros is Set); // false
var letras = <String>{'a', 'a', 'b', 'b', 'c', 'c'};
print(letras); // {a, b, c}, pois elementos repetidos não importam para conjuntos
Mapas
Mapas são objetos que associam chaves, em strings, a valores que podem ser de qualquer tipo! Apesar dos valores poderem se repetir, as chaves devem ser únicas.
Map<String, dynamic> listaDeCompras = {
 'chocolates': 2,
 'refrigerantes': 4,
 'carne': 1.5 // quilos
};
// Ou através do construtor Map()
var listaDeCompras = Map();
listaDeCompras['chocolates'] = 2;
listaDeCompras['refrigerantes'] = 4;
listaDeCompras['carne'] = 1.5;
Os valores de um mapa podem ser acessados da mesma forma que construímos a variável com o construtor Map():
print(listaDeCompras['chocolates']); // 2
São utilizados na construção de objetos a partir de dados fornecidos por uma API ou query.
Runas
Strings em Dart são uma sequência de caracteres Unicode no formato UTF-16. Um caractere unicode é escrito da seguinte forma:
\u2665 // ♥
As runas expõe o unicode de um string e podem ser usadas para formar caracteres especiais como símbolos e emojis.
Runes coracao = Runes('\u2665');
print(String.fromCharCodes(coracao)); // ♥
Símbolos
Símbolos são nomes (strings) utilizados para refletir metadados de uma biblioteca de códigos ou API. Essas informações costumam ser otimizadas para que o computador as interprete, e os símbolos servem para torná-las mais legível para nós, humanos. Nome de classes, métodos e libs podem ser símbolos.
Confesso que não vejo a utilização deles com frequência e os exemplos em relação a eles são quase sempre iguais a esse aqui:
· Dart Symbol
Dynamic
Essa é uma declaração responsável por permitir que qualquer valor seja atribuído a uma variável. É preferível declarar uma variável com dynamic do que deixar a inferência falhar. Entretanto é recomendado utilizar "Object" em vez dele, deixando-o para quando for necessário dinamismo em uma parte do programa ou quando os valores virão de uma interoperabilidade entre JS/Kotlin, por exemplo.
Operadores
Operadores são como os sinais matemáticos: pegam dois ou mais valores e transformam em um resultado diferente. Há diversos tipos de operadores, então o uso deles não se restringe a números. Vimos, por exemplo, a concatenação de strings utilizando o sinal "+".
print("Hello" + " World!"); // Hello World!
Operadores aritméticos
São os mesmos que utilizamos em contas. Considere:
double umNumero = 10.0;
double outroNumero = 4.0;
então:
· + -- soma dois valores (podendo ser strings);
print(umNumero + outroNumero);// 14
· - -- subtrai o segundo valor do primeiro;
print(umNumero - outroNumero); // 6
· * -- multiplica ambos os valores;
print(umNumero * outroNumero); // 40
· / -- divide o primeiro valor pelo segundo;
print(umNumero / outroNumero); // 2.5
· ~/ -- divide o primeiro valor pelo segundo mas retorna um número inteiro, sem parte fracionada;
print(umNumero ~/ outroNumero); // 2
· % -- divide o primeiro valor pelo segundo e retorna o resto da divisão
print(umNumero % outroNumero); // 2, pois 10 / 4 = 8, com resto = 2
A ordem de precedência pode ser consultada nessa tabela, mas pode ser alterada usando parênteses "()", como em contas matemáticas.
1 + 2 * 3 
é diferente de
(1 + 2) * 3
Operadores de incremento
São aqueles que aumentam ou diminuem a variável em 1. São divididos em dois tipos: prefixo, que aumenta a variável e depois avalia a expressão, e postfix (como um sufixo), que só aumenta a variável após avaliar a expressão
· Postfix:
variavel++;
variavel--;
· Prefixo:
++variavel;
--variavel;
Operadores de igualdade e relacionais
São aqueles usados para comparar valores. Considere:
double umNumero = 10.0;
double outroNumero = 4.0;
Então:
· == -- retorna true se os valores forem iguais e falso se forem diferentes;
print(umNumero == outroNumero); // false
print(umNumero == umNumero); // true
· != -- retorna true se os valores forem diferentes;
print(umNumero != outroNumero); // true
print(umNumero != umNumero); // false
· > -- retorna true se o primeiro valor for maior que o segundo;
print(umNumero > outroNumero); // true
print(outroNumero > umNumero); // false
print(umNumero > umNumero); // false
· < -- retorna true se o primeiro valor for menor que o segundo;
print(umNumero < outroNumero); // false
print(outroNumero < umNumero); // true
print(umNumero < umNumero); // false
· >= -- retorna true se o primeiro valor for maior ou igual ao segundo;
print(umNumero >= outroNumero); // true
print(outroNumero >= umNumero); // false
print(umNumero >= umNumero); // true, pois os números são iguais
· <= -- retorna true se o primeiro valor for menor ou igual ao segundo;
print(umNumero <= outroNumero); // false
print(outroNumero <= umNumero); // true
print(umNumero <= umNumero); // true, pois os números são iguais
Operadores de teste de tipos
São úteis para converter ou checar tipos de dados. Considere:
int num = 10;
· as --- converte o tipo de um objeto. Pode ser utilizado como uma versão enxuta do operador "is";
if (emp is Person) {
 // Type check
 emp.firstName = 'Bob';
}
// com o operador "as"
(emp as Person).firstName = 'Bob';
· is --- retorna true se o valor for de um determinado tipo;
print(num is int); // true
· is! --- retorna true se o valor não for de um determinado tipo;
print(num is! String); // true
Operadores de atribuição
São aqueles que atribuem valores às variáveis:
· = --- utilizado para atribuir um valor a uma variável
int umNumero = 2;
· += --- utilizado para somar um valor ao valor já existente na variável
int umNumero = 2;
umNumero += 3;
print(umNumero); // 5, pois é o mesmo que umNumero = umNumero + 3
· -= --- utilizado para subtrair um valor ao valor já existente na variável
int umNumero = 2;
umNumero += 1;
print(umNumero); // 1
· *= --- utilizado para multiplicar um valor ao valor já existente na variável
int umNumero = 2;
umNumero *= 2;
print(umNumero); // 4
· /= --- utilizado para dividir um valor ao valor já existente na variável
double umNumero = 2.0;
umNumero /= 2;
print(umNumero); // 1
· ~/= --- utilizado para dividir arredondando para baixo um valor ao valor já existente na variável
int umNumero = 5;
umNumero ~/= 2;
print(umNumero); // 2
Operadores lógicos
Servem para trabalhar com operações booleanas, que podem inverter ou não o fluxo de execução de um programa.
Considere:
bool verdade = true;
bool mentira = false;
Então:
· !expressão --- inverte o valor da expressão.
print(!verdade); // false
· && --- retorna true se ambas as expressões forem verdadeiras.
print(verdade && verdade); // true
print(verdade && mentira); // false
· || --- retorna true se uma das expressões for verdadeira.
print(verdade || verdade); // true
print(verdade || mentira); // true
print(mentira || mentira); // false
Notação de cascata
A ideia da notação de cascata é evitar que seja necessária a criação de variáveis temporárias, permitindo o acesso a um mesmo objeto linha após linha.
querySelector('#confirm') // Pega um objeto
 ..text = 'Confirm' // Usa as propriedades dele
 ..classes.add('important')
 ..onClick.listen((e) => window.alert('Confirmed!'));
Outros operadores
São os responsáveis por chamadas de função e acesso a listas e propriedades de objetos.
· () -- chamada de função
// definir a função
void helloWorld() {
 print("Hello World!");
}
//chamar a função
helloWorld();
· [] -- acesso a elementos de lista
List<int> listaDeNumeros = [1, 2, 3, 4, 5];
int segundoNumeroDaLista = listaDeNumeros[1]; // pois os índices começam por 0
· . -- acesso a alguma propriedade de uma expressão. Um exemplo é o acesso à propriedade length de uma lista, que retorna o número de elementos nela
List<String> vogais = ['a', 'e', 'i', 'o', 'u'];
print(vogais.length); // 5
Sentenças de controle de fluxo
Condicionais
É possível testar as condições no seu programa para que ele rode pedaços de código diferentes para cada uma delas. Pense na seguinte sentença:
· (1) Se o usuário estiver logado, (2) então poderá acessar a página inicial. (3) Caso contrário, (4) será direcionado para a página de Login. Traduzindo para o código:
if(usuarioEstiverLogado) { // 1
 redirecionarParaPaginaInicial(); // 2
} else { // 3
 redirecionarParaPaginaDeLogin(); // 4
}
Perceba os números na frase e no código. Eles traduzem a forma na qual as condicionais funcionam:
1 - if -- se(condição a ser testada)
2 - código a ser executado se a condição for verdadeira
3 - else -- caso contrário
4 - código executado caso a condição do número 1 seja falsa.
Outro exemplo:
· Se o aluno tiver média maior ou igual a 7 e frequência maior que 75%, será aprovado.
if(media >= 7 && frequencia > 75) {
 print("O aluno foi aprovado!");
} else {
 print("O aluno foi reprovado!");
}
As palavras em negrito correspondem aos operadores '>=', && e >. Agora, e se eu quiser testar a seguinte condição:
· Se o aluno tiver média maior ou igual a 7, será aprovado. Caso a média seja maior ou igual a 5 e menor que 7, ficará em recuperação. Caso contrário, será reprovado. Há duas condições a serem testadas: se ele será aprovado, ou se ficará de recuperação (apenas duas, pois qualquer nota menor que 5 fará com que ele seja reprovado). Nesse tipo de situação, utilizamos as palavras reservadas "else if":
if(media >= 7) { // primeira condição
 print("O aluno foi aprovado");
} else if (media >= 5 && media < 7) { // segunda condição
 print("O aluno ficou em recuperação");
} else { // qualquer outra condição
 print("O aluno foi reprovado");
}
Ternário
O operador ternário funciona como o "if", mas a sintaxe dele é mais enxuta:
media >= 7 ? print("O aluno foi aprovado") : print("O aluno foi reprovado");
A sintaxe funciona da mesma forma:
condição a ser testada ? código caso seja verdadeira : código caso seja falsa. 
É possível encadear várias sentenças no ternário. O mesmo exercício do if-else ficaria assim:
media >= 7 ? print("O aluno foi aprovado") : media >= 5 ? print("O aluno ficou em recuperação") : print("O aluno foi reprovado"); 
Switch - case
Em vez de usar vários "ifs" e "elses", use switch-case (nem sempre é recomendado por questão de performance). A sintaxe funciona assim:
switch(variavel) {
 case('caso1'):
 print('caso1');
 break;
 default:
 print('qualquer mensagem padrão');
}
Os possíveis valores da variável devem ser escritos dentro dos casos (case), e na linha seguinte, as instruções para aquela determinada condição. Ao final há a palavra-chave "break", que indica que o programa pode executar as sentenças seguintes ao switch. Se essa palavra for esquecida, ocorrerá uma erro durante a execução.
Semais de um caso for executar as mesmas sequências, é possível utilizar "casos vazios":
switch(variavel) {
 case('caso1'):
 case('caso2'):
 case('caso3'):
 print('Qualquer mensagem');
 break;
 default:
 print('qualquer mensagem padrão');
}
Laços de repetição -- While
Partes de um código costumam ser repetidas durante a execução. Entretanto, não é necessário escrevê-las várias vezes! Para isso, utiliza-se os laços de repetição!
O while é um laço que executa um determinado bloco de código enquanto uma condição for verdadeira. Com ele é possível realizar algoritmos simples, como contar números, e até aplicações mais práticas, como por exemplo o loop de postagens do WordPress.
int contador = 0;
while(contador < 10) {
 print(contador);
 contador++;
}
Resultado:
0
1
2
3
4
5
6
7
8
9
Todo o código localizado entre "{}" será executado repetidas vezes até que a condição deixe de ser verdadeira. Preste muita atenção na última expressão.
contador++;
O contador aumentará em 1 toda vez que o código se repetir até que a condição seja falsa. O que acontecerá nos esquecermos de incrementá-lo?
Laços de repetição -- Do While
Esse laço é parecido com o anterior. A diferença é que o while checa a condição e depois executa o código. Já o do while executa o código e depois checa. Veja a diferença em um algoritmo com a mesma lógica:
int contador1 = 1;
while(contador1 < 1) {
 print("contador1: $contador1");
 contador1++;
}
Resultado: nada é exibido no console.
int contador2 = 1;
do {
 print("contador2: $contador2");
 contador2++;
} while(contador2 < 1);
Resultado:
contador2: 1
"
Laços de repetição -- For
Esse laço é comumente utilizado em conjunto com listas, pois pode usar o número de elementos delas como "contador" da repetição.
· Sintaxe tradicional:
List<int> listaDeNumeros = [1, 2, 3, 4, 5];
for(int i = 0; i < listaDeNumeros.length; i++) {
 print(listaDeNumeros[i]);
}
//Resultado:
1
2
3
4
5
· for...in:
List<int> listaDeNumeros = [1, 2, 3, 4, 5];
for(int i in listaDeNumeros) {
 print(i);
}
· forEach() -- um método das listas que realiza uma ação programada para cada elemento em uma determinada lista:
List<int> listaDeNumeros = [1, 2, 3, 4, 5];
listaDeNumeros.forEach((numero) {
 print(numero);
});
Pense que "numero" é como o nome de uma variável que, durante cada repetição terá um valor na lista seguindo uma ordem sequencial.
Funções
tipo nomeDaFuncao(parametros) {
 declarações a serem executadas
}
A parte mais interessante desse texto. As funções são blocos de código que executam alguma operação. Quando utilizadas sozinhas, são chamadas de funções. Quando associadas a algum objeto, são chamadas de métodos. Algumas delas foram demonstradas ao longo dessa postagem. "stringQualquer.toUppercase()" é uma delas.
A mais importante em programas Dart é a função main(), pois é a responsável por executar todo o código. Sempre que abrir o DartPad, o editor virá com ela preenchida.
void main() {
 // digite o código aqui
}
A palavra "void" significa que a função não possui um valor de retorno. As funções podem retornar algum valor, que é geralmente o resultado das instruções executadas dentro delas. No caso de uma função de soma, é comum que ela retorne o resultado da soma dos números passados como argumento.
int soma(int umNumero, int outroNumero) {
 return umNumero + outroNumero; // o valor de retorno (return) será o resultado da expressão.
}
Perceba que declarei duas variáveis dentro dos parênteses: int umNumero e int outroNumero, mas não atribui um valor a elas. Essas variáveis, chamadas de argumentos, terão seus valores atribuídos durante a chamada da função e separados por uma vírgula:
print(soma(1, 2)); // 3
É possível utilizar o valor de retorno de uma função como argumento de outra função. Essa é uma abordagem de um outro paradigma de programação, que trataremos em uma postagem próxima.
Introdução Estrutura de Projetos
Atualmente não se concebe um processo de desenvolvimento de software sério sem a utilização da orientação a objetos, pois esta permite agregar qualidades importantes aos sistemas desenvolvidos sob seus paradigmas, como a extensibilidade e a reusabilidade Mas somente por estar utilizando-a, não é garantia de se obter essas qualidades. Para criar as melhores soluções, é preciso seguir um processo detalhado para obter uma análise dos requisitos, funcionais ou não funcionais, e desenvolver um projeto que os satisfaça e que possibilite submetê-los a teste, para constatar eventuais falhas, além do mais se deseja que o projeto tenha uma arquitetura flexível para acomodar futuros problemas e requisitos sem a necessidade da realização do re-projeto.
Analisando o cotidiano do desenvolvimento de software é possível identificar que a procura por uma solução a um problema específico possui características idênticas, senão igual a encontrada em um projeto anteriormente desenvolvido, mas que devido à deficiência do processo, a solução e o problema não fora documentado e às vezes tão pouco compreendido em sua totalidade impossibilitando o reaproveitamento das idéias e soluções. Desta forma, problemas idênticos que se repetem em outros contextos não são reconhecidos como tal, consumindo tempo e recursos em busca de soluções que em tese,já haviam sido encontradas.
Uma coisa que os projetistas avançados sabem que não devem fazer é resolver cada problema a partir de princípios elementares ou do zero. Ao invés disso, eles reutilizam soluções que funcionaram no passado, e os utilizam repetidamente em seus projetos. É como resolver um problema matemático até então inédito, ou pelo, menos desconhecido a alguém. Primeiro utiliza-se todos os conhecimentos e princípios matemáticos que se tem conhecimento e que venha a ser útil na solução, depois de algumas tentativas e uso das teorias matemáticas, chega-se à solução e a partir daí tem-se um “algoritmo” (estrutura) montado que pode ser utilizado para resolver tantos problemas existirem e que sejam idênticos ao resolvido, e mesmo que não seja, é possível reaproveitar as idéias e conclusões do problema resolvido. É por isso que os padrões de projetos,design patterns, tem chamado a atenção e despertado o interesse dos projetistas de software, por proporcionar elementos que conduzem ao reaproveitamento de soluções para projetos, e não apenas a reutilização de código.
Os padrões de projetos tornam mais fácil reutilizar soluções e arquiteturas bem sucedidas para construir softwares orientados a objetos de forma flexível e fácil de manter. O uso de padrões de projeto pode reduzir a complexidade do processo de projetar software. Além disso, o software orientado a objetos bem projetado possibilita aos projetistas reutilizar e empregar componentes preexistentes em sistemas futuros.
Em software, os padrões de projeto não são classes nem objeto. Em vez disso, os projetistas usam esses padrões para construir conjuntos de classes e objetos. Para utilizá-los de maneira eficaz, os projetistas precisam se familiarizar com os padrões mais populares e eficazes utilizados pela engenharia de software e conhecer o seu contexto e escopo.
A ideia de projetar soluções a partir de algo já conhecido e documentado, não é nova e tão pouco teve origem na industria de software, apesar dela já demonstrar um interesse pelo tema. A ideia surgiu em 1977 quando Christopher Alexander publicou um catálogo com mais de 250 padrões para a arquitetura civil, que discutiam questões comuns da arquitetura, descrevendo em detalhe o problema e as justificativas de sua solução.
Christopher Alexander descobriu que diminuindo o foco, ou seja, procurando estruturas que resolvam problemas similares, ele pode discernir similaridades entre projetos de alta qualidade. Ele chamou essas similaridades de “padrões”.
Algum tempo depois ele formaliza seu método de descrição dos padrões, defendendo que seu uso não limitaria os arquitetos às soluções prescritas, mas garantiria a presença de elementos fundamentais, e a possibilidade de aperfeiçoa-la através das experiências adquiridas.Esse método chamou atenção da comunidade de software, fazendo que o tema ganhasse destaque nas conferências sobre orientação a objetos. Em 1995 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, conhecidos como os quatro amigos [Gang of Four - GoF], publicaram o livro sobre o título: “Design patterns – elements of reusable object-oriented software, Addison Wesley Longman”, que ganhou uma versão na língua portuguesa sobre o título de “Padrões de Projeto – Soluções reutilizáveis de software orientado a objetos. Bookman”. O livro é um catálogo que descreve 23 padrões de projeto cada um fornecendo uma solução para um problema de software, seu contexto, aplicação e suas eventuais conseqüências, dividindo-os em 3 categorias: padrões de criação, estruturais, e de comportamento.
Definindo padrões de projeto: Definir o que é um padrão de projeto de maneira clara e objetiva, tem sido o objetivo da comunidade de software, desde a década de 80.O primeiro a apresentar uma definição do que seria um padrão, foi o arquiteto de professor Christopher Alexander, no seu livro “A Times Way of Building” (Oxford University Press, 1979), que é: “Cada padrão é uma regra de três partes, que expressa uma relação entre um certo contexto, um problema e uma solução”. Sendo assim para entender a necessidade, existência, de um padrão é necessário estudar suas partes: o problema, a solução e o contexto sobre o qual ele é aplicável.
Dessa forma, resumidamente pode-se entender como padrão de projeto, como a solução recorrente para um problema em um contexto, mesmo que em projetos e áreas distintas. Observe que os termos chaves dessa definição são: contexto, problema e solução, o que torna obrigatório à compreensão inequívoca de cada um. Um contexto diz respeito ao ambiente, e as circunstâncias dentro do qual algo existe.O problema é a questão indefinida, algo que precisa ser investigado e solucionado. Normalmente, está atrelado ao contexto em que ocorre. Finalmente, a solução refere à resposta do problema que ajuda a soluciona-lo.
Entretanto, se tivermos uma solução para um problema em um certo contexto, ela não necessariamente pode constituir um padrão, pois é necessário que ela tenha como característica a regularidade, isto é, ela se constituirá como um padrão se puder ser utilizada repetidamente.
Segundo Christopher Alexander, “cada padrão descreve um problema no nosso ambiente e o núcleo da sua solução, de tal forma que você possa usar esta solução mais de um milhão de vezes, sem nunca faze-lo da mesma maneira”.
O grupo dos quatro amigos classificou os padrões de projeto por dois critérios. O primeiro critério é a finalidade - reflete o que um padrão faz. Os padrões podem ter finalidades de criação, comportamento e estrutural. Os padrões de criação descrevem as técnicas para instanciar objetos (ou grupos de objetos), e possibilitam organizar classes e objetos em estrutura maiores, os de comportamento se caracterizam pela maneira pelas quais classes ou objetos interagem e distribuem responsabilidades e os estruturais lidam com a composição de classes ou objetos. O segundo critério é o escopo - especifica se o padrão é aplicado à classe ou objeto.
Características de um padrão de projeto
Embora um padrão seja a descrição de um problema, de uma solução genérica e sua justificativa, isso não significa que qualquer solução conhecida para um problema possa constituir um padrão, pois existem características obrigatórias que devem ser atendidas pelos padrões: .
1. Devem possuir umnome, que descreva o problema, as soluções e conseqüências. Um nome permiti definir o vocabulário a ser utilizado pelos projetistas e desenvolvedores em um nível mais alto de abstração.
2. Todo padrão deve relatar de maneira clara a qual (is)problema(s) ele deve ser aplicado, ou seja, quais são os problemas que quando inserido em um determinado contexto o padrão conseguirá resolve-lo.Alguns podendo exigir pré-condições.
3. Soluçãodescreve os elementos que compõem o projeto, seus relacionamentos, responsabilidades e colaborações. Um padrão deve ser uma solução concreta, ele deve ser exprimido em forma de gabarito (algoritmo) que, no entanto pode ser aplicado de maneiras diferentes.
4. Todo padrão deve relatar quais são as suasconseqüênciaspara que possa ser analisada a solução alternativa de projetos e para a compreensão dos benefícios da aplicação do projeto.
Não pode ser considerado um padrão de projeto trecho de códigos específicos, mesmo que para o seu criador ele reflita um padrão, que soluciona um determinado problema, porque os padrões devem estar a um nível maior de abstração e não limitado a recursos de programação. Um padrão de projeto nomeia, abstrai e identifica os aspectos chaves de uma estrutura de projeto comum para torna-la útil para a criação de um projeto orientado a objetos reutilizável.
A importância dos padrõesde projeto
O mais importante sobre os padrões é que eles são soluções aprovadas. Cada catálogo inclui apenas padrões que foram considerados úteis por diversos desenvolvedores em vários projetos. Os padrões catalogados também são bem definidos; os autores descrevem cada padrão com muito cuidado e em seu próprio contexto, portanto será fácil aplicar o padrão em suas próprias circunstâncias. Eles também formam um vocabulário comum entre os desenvolvedores.
Quandoos padrões não o ajudarão
Os padrões são um mapa, não uma estratégica. Os catálogos geralmente apresentarão algum código-fonte como uma estratégica de exemplo, portanto eles não devem ser considerados como definitivos. Os padrões não ajudarão a determinar qual aplicação você deve estar escrevendo apenas como implementar melhor a aplicação assim que o conjunto de recursos e outras exigências forem determinados. Os padrões ajudam com o que e como, mas não com por que ou quando.
O conceito de utilizar os padrões de forma indiscriminada é conhecida como antipadrões (anti patterns). De acordo com Andrew Koenig, se um padrão representa a “melhor prática”, então um antipadrão representa uma “lição aprendida”.
Existem duas noções de antipadrões:
1. Aqueles que descrevem uma solução ruim para um problema que resultou em uma situação ruim;
2. Aqueles que descrevem como se livrar de uma situação ruim e como proceder dessa situação para uma situação boa.
Em suma um antipadrão constitui ao uso indevido dos padrões de projeto, ou o seu uso exagerado, o que pode ser constatado pela utilização de padrões impróprios para um determinado contexto, ou uso inadequado. A utilização dos padrões proporciona um aumento na flexibilidade do sistema, entretanto pode deixa-lo mais complexo ou degradar a performance. Algumas perdas são toleráveis, mas subestimar os efeitos colaterais da adoção dos patterns, é um erro comum, principalmente daqueles que tomam o uso como um diferencial e não pela real necessidade.
Como padrões de projeto solucionam problemas de projeto
O alvo principal do uso dos padrões de projeto no desenvolvimento de software é o da orientação a objetos. Como os objetos são os elementos chaves em projetos OO, a parte mais difícil do projeto é a decomposição de um sistema em objetos. A tarefa é difícil porque muitos fatores entram em jogo: encapsulamento, granularidade, dependência, flexibilidade, desempenho, evolução, reutilização e assim por diante. Todos influenciam a decomposição, freqüentemente de formas conflitantes.
Muito dos objetos participantes provêm do método de análise. Porém, projetos orientados a objetos acabam sendo compostos por objetos que não possui uma contrapartida no mundo real.
As abstrações que surgem durante um projeto são as chaves para torna-lo flexível. Os padrões de projeto ajudam a identificar abstrações menos óbvias bem como os objetos que podem capturá-las. Por exemplo, objetos que representam processo ou algoritmo não ocorrem na natureza, no entanto, eles são uma parte crucial de projetos flexíveis. Esses objetos são raramente encontrados durante a análise ou mesmo durante os estágios iniciais de um projeto; eles são descobertos mais tarde, durante o processode tornar um projeto mais flexível e reutilizável.
Como selecionar um padrão de projeto
Escolher dentre os padrões existentes aquele que melhor soluciona um problema do projeto, sem cometer o erro de escolher de forma errônea e torná-lo inviável, é uma das tarefas mais difíceis. Em suma, a escolha de um padrão de projeto a ser utilizado, pode ser baseada nos seguintes critérios:
1. Considerar como os padrões de projeto solucionam problemas de projeto.
2. Examinarqual a intenção do padrão, ou seja, o que faz de fato o padrão de projeto, quais seus princípios e que tópico ou problema particular de projeto ele trata (soluciona).
3. Estudar como os padrões se relacionam.
4. Estudar as semelhanças existentes entre os padrões.
5. Examinar uma causa de reformulação de projeto.
6. Considerar o que deveria ser variável no seu projeto, ou seja, ao invés de considerar o que pode forçar uma mudança em um projeto, considerar o que você quer ser capaz de mudar sem reprojetá-lo.
Como usar um padrão de projeto
Depois de ter sido feita a escolha do(s) padrão (ões) a ser (em) utilizado(s) no projeto é necessária conhecer como utilizá-lo(s). Uma abordagem recomendada pela gangue dos quatros amigos para aplicar um padrão a um projeto é:
1. Ler o padrão por completo uma vez, para obter sua visão geral. Conhecer o padrão principalmente a sua aplicabilidade e conseqüências são importantes para que ele realmente solucione o seu problema;
2. Estudar seções Estrutura, Participantes e Colaborações. Assegurando-se de que compreendeu as classes e objetos no padrão e como se relacionam entre si;
3. Escolher os nomes para os participantes do padrão que tenham sentido no contexto da aplicação;
4. Definir as classes. Declarar as interfaces, estabelecer os seus relacionamentos de herança e definir as variáveis de instância que representam dados e referências a objetos. Identifique as classes existentes na aplicação que serão afetadas pelo padrão e modifique-as;
5. Defina os nomes específicos da aplicação para as operações no padrão. Os nomes em geral dependem da aplicação. Use as responsabilidades e colaborações associadas com cada operação como guia;
6. Implemente as operações para suportar as responsabilidades e colaborações presentes do padrão. A seção de Implementação oferece sugestões para guiá-lo na implementação.
Essas são apenas diretrizes que podem ser utilizadas até que seja obtido experiência e conhecimento necessário para desenvolver uma maneira de trabalho particular com os padrões de projeto. Os padrões de projeto não devem ser aplicados indiscriminadamente. Freqüentemente eles obtêm flexibilidade e variabilidade pela introdução de níveis adicionais de endereçamento indireto, e isso pode complicar um projeto e/ou custar algo em termos de desempenho. Um padrão de projeto deverá apenas ser aplicado quando a flexibilidade que ele oferece for realmente necessária.
Principais padrões de projeto
Uma rápida leitura dos códigos da VCL (Visual Component Library) do Delphiconstata-se que ela fora construído fazendo uso intensivo dos padrões de projetos. O que é muito bom, pois constata o nível de excelência da ferramenta. Isto devido ao Delphiimplementar completamente as boas práticas da orientação a objetos – OOP, que auxiliam na implementação de projetos reutilizáveis.
Padrões de criação: Os padrões de criação são aqueles que abstraem e ou adiam o processo criação dos objetos. Eles ajudam a tornar um sistema independente de como seus objetos são criados, compostos e representados. Um padrão de criação de classe usa a herança para variar a classe que é instanciada, enquanto que um padrão de criação de objeto delegará a instanciação para outro objeto.
Os padrões de criação tornam-se importantes à medida que os sistemas evoluem no sentido de dependerem mais da composição de objetos do que a herança de classes. O desenvolvimento baseado na composição de objetos possibilita que os objetos sejam compostos sem a necessidade de expor o seu interior como acontece na herança de classe, o que possibilita a definição do comportamento dinamicamente e a ênfase desloca-se da codificação de maneira rígida de um conjunto fixo de comportamentos, para a definição de um conjunto menor de comportamentos que podem ser compostos em qualquer número para definir comportamentos mais complexos.
Há dois temas recorrentes nesses padrões. Primeiro todos encapsulam conhecimento sobre quais classes concretas são usadas pelo sistema. Segundo ocultam o modo como essas classes são criadas e montadas. Tudo que o sistema sabe no geral sobre os objetos é que suas classes são definidas por classes abstratas. Conseqüentemente, os padrões de criação dão muita flexibilidade no que é criado, quem cria, como e quando é criado. Eles permitem configurar um sistema com objetos “produto” que variam amplamente em estrutura e funcionalidade. A configuração pode ser estática (isto é, especificada em tempo de compilação) ou dinâmica (em tempo de execução).

Continue navegando