Welington Pereira
Desenvolvedor Full Stack & Consultor
Especialista em linguagens de programação e banco de dados. Compartilhando dicas de programação práticas, tutoriais e soluções para o dia a dia do desenvolvedor.
Desenvolvedor Full Stack & Consultor
Especialista em linguagens de programação e banco de dados. Compartilhando dicas de programação práticas, tutoriais e soluções para o dia a dia do desenvolvedor.
Em Dart todos os tipos são objetos. Isso nos permite ter acesso a um grande conjunto de funções para processamento de dados, mesmo em objetos de tipos mais básicos, fornecidos pelo ambiente de execução do Dart. Esses tipos são:
Dart oferece três tipos para armazenar valores numéricos.
int, utilizado para o armazenamento de qualquer número inteiro, seja ele negativo ou positivo.
double, que é utilizado para o armazenamento de números de pontos flutuantes.
Ambos, int e double, são subtipos de num. Ao declarar uma variável como num ela pode ser tanto um inteiro quanto um número de ponto flutuante:
num pi = 3;
pi = 3.14;Se declarar uma variável como um inteiro, ela não poderá receber um decimal. Vejamos:
int pi = 3;
pi = 3.14;
Error: A value of type 'double' can't be assigned to a variable of type 'int'.Quando encontramos um número ou texto no código, como no exemplo acima, esse valor é chamado literal.
Um literal é um valor que não precisa de avaliação por parte do compilador para ser atribuído a uma variável. Assim sendo, o compilador cria uma instância de num adequada a esse valor. Naturalmente, o mesmo também ocorrerá com o resultado de uma expressão.
Além disso, o tipo num, int e double também fornecem diversos métodos e propriedades que podem ser utilizados para a transformação e checagem de dados. Eles também dispõem de capacidades para expressões utilizando os operadores + (adição), - (subtração), * (multiplicação), / (divisão) e outros.
Métodos para operações matemáticas:
Método | Descrição |
abs() | Retorna o valor absoluto do número. |
ceil() | Retorna o último inteiro imediatamente superior. |
ceilToDouble() | Retorna o último número imediatamente superior com o tipo double. |
clamp(num limiteInferior, num limiteSuperior) | Se o número estiver dentro do limite, retorna o número. Se não, retorna o limite o qual ele extrapolou. |
compareTo(num outro) | Compara com outro número, retornando 1 quando forem diferentes e 0 quando forem iguais. |
floor() | Arredonda o número para o inteiro anterior. |
floorToDouble() | Arredonda o número para o número inteiro anterior no tipo double. |
remainder(num outro) | Retorna a sobra da divisão com outro número. |
round() | Arredonda o número para o inteiro mais próximo. |
roundToDouble() | Arredonda o número para o valor inteiro mais próximo no tipo double. |
toDouble() | Converte o número para Double. |
toInt() | Converte o número para Int. |
toString() | Converte o número em uma String. |
toStringAsExponential([int digitos]) | Converte para string com exponencial. |
toStringAsFixed(int decimais) | Converte para String contendo N casas decimais. |
toStringAsPrecision(int digitos) | Converte para String contendo N dígitos. |
truncate() | Retira as casas decimais, retornando um inteiro. |
truncateToDouble() | Retira as casas decimais, retornando um double. |
Propriedades que permitem realizarmos checagens de valores:
isFinite (retorna true quando o número for finito)
isInfinite (retorna true quando o número for infinito)
isNegative (retorna true quando o número for negativo)
double euler = 0.5772156649;
print(euler.isFinite); // true
print(euler.isInfinite); // false
print(euler.isNegative); // falseStrings são cadeias de caracteres que podemos representar com aspas duplas ou aspas simples.:
String nome_usuario = "Ton";
String sobrenome_usuario = 'Fontes';
print(nome_usuario); // Ton
print(sobrenome_usuario); // FontesEsse objeto String fornece diversos atributos e métodos para a verificação e transformação de cadeias de caracteres, tais como:
toUpperCase, que transforma os caracteres do texto em maiúsculas
toLowerCase, que transforma todos os caracteres em letras minúsculas
trim, que remove os espaços vazios do início e fim do texto, e muitos outros.
Podemos juntar duas strings através da concatenação ou através da interpolação.
String nome = "Ton";
String sobrenome = "Fontes";
int idade = 22;
// Concatenação de duas Strings
String nome_completo = nome+" "+sobrenome;
//Interpolação com String e valor do tipo inteiro.
String mensagem = "O usuário $nome_completo possui $idade anos de idade.";
print(mensagem); // O usuário Ton Fontes possui 22 anos de idade.Diferentemente do Java, por exemplo, que necessitamos utilizar o método equals para realizar a comparação de duas Strings, no Dart podemos compará-las utilizando o operador de igualdade ==.:
String nome_usuario_1 = "Ton";
String nome_usuario_2 = "Ton";
if(nome_usuario_1 == nome_usuario_2) {
print("Ambos os usuários possuem o mesmo nome.");
}O Dart utilizao tipo bool que pode ser representado por dois valores: true (verdadeiro) e false (falso).
Quando utilizamos propriedades de checagem vistas anteriormente, como isFinite, isInfinite e isNegative por exemplo, o valor retornado por estas é do tipo bool.
O Dart fornece diversos operadores de comparação com resultados booleanos. Podemos, inclusive, checar por tipos durante a execução do software:
Operador | Descrição |
>= | Maior ou igual |
> | Maior |
<= | Menor ou igual |
< | Menor |
is | É do mesmo tipo |
is! | Não é do mesmo tipo |
== | Igual |
!= | Diferente |
&& | E lógico (AND) |
|| | OU lógico (OR) |
Exemplo:
String papel = "ADMIN";
bool esta_logado = true;
String nome_usuario = "Ton";
if(papel == "ADMIN" && esta_logado) {
print("O usuário $nome_usuario é um Admin e está logado.");
}Neste tipo podemos atribuir valores de todos os outros tipos, e até mesmo modificar esses valores em tempo de execução:
dynamic nome = "Ton Fontes";
dynamic idade = 22;
print(nome); // Ton Fontes
print(idade); // 22
idade = "22 anos";
print(idade); // 22 anosUma função é um trecho de código, um processo ou rotina, responsável pela execução de uma tarefa específica, podendo ser executado múltiplas vezes durante a execução do programa.
No Dart, funções são um tipo de dado e podemos tratá-las como qualquer outro dado: podemos passá-las como parâmetro, atribuir a variáveis e mais.
Uma função pode receber parâmetros (dados necessários para sua execução) e retornar outros valores gerados a partir de um processamento qualquer.
exibirMensagemDeErro(){
print("Falhou!!!");
}No trecho de código anterior nós declaramos nossa função, mas esta não irá executar de imediato. Para que esta seja executada, precisamos explicitamente ordenar a execução:
exibirMensagemDeErro(); Funções também podem ter seu retorno tipado. Se a mesma não tiver retorno, podemos tipá-la como void:
void exibirMensagemDeErro(){
print("Desculpa, encontramos um erro.");
}Para recebermos parâmetros declararmos cada uma das variáveis que a função pode receber, e seus tipos, dentro dos parênteses:
void exibirNomeECargo(String nome, String cargo) {
print("Nome: $nome ; Cargo: $cargo");
}
exibirNomeECargo("Ton Fontes", "Desenvolvedor"); // Nome: Ton Fontes; Cargo: DesenvolvedorO exemplo acima utiliza passagem de parâmetros. Cuidado para não alterar a ordem de passagem dos parâmetros, pois irá influenciar a execução da função.
Outra coisa, precisa passar todos os parâmetros, pois se não for passado na execução da função, isto irá gerar um erro.
Porám, no Dart é possível definir parâmetros opcionais posicionais e nomeados, mas não ambos.
Para definir parâmetros opcionais posicionais, devemos agrupar esses parâmetros entre colchetes.
NOTA: Parâmetros opcionais posicionais precisam ser declarados após os parâmetros obrigatórios:
void exibirNomeECargo(String nome, [String cargo]) {
if(cargo != null) {
print("Nome: $nome ; Cargo: $cargo");
} else {
print("Nome: $nome ;");
}
}
exibirNomeECargo("Ton", "Desenvolvedor"); // Nome: Ton ; Cargo: Desenvolvedor
exibirNomeECargo("Ton"); // Nome: Ton;Para declarar valores padrões para os parâmetros opicionais, é bem simplesão:
void exibirNomeECargo(String nome, [String cargo = "Web Master"]) {
print("Nome: $nome ; Cargo: $cargo");
}
exibirNomeECargo("Ton Fontes", "Desenvolvedor"); // Nome: Ton Fontes; Cargo: Desenvolvedor
exibirNomeECargo("Ton Fontes"); // Nome: Ton Fontes ; Cargo: DesconhecidoNesse caso, no momento em que executamos a função, precisamos conhecer os nomes dos parâmetros que serão recebidos, não sua posição.
Da mesma forma que parâmetros opcionais posicionais, os parâmetros nomeados também precisam ser declarados após a declaração dos parâmetros obrigatórios. Contudo, neste caso, utilizamos chaves na declaração:
void exibirNomeECargo(String nome, {String cargo}) {
if(cargo != null) {
print("Nome: $nome ; Cargo: $cargo");
} else {
print("Nome: $nome ;");
}
}
exibirNomeECargo("Ton Fontes", cargo: "Desenvolvedor"); // Nome: Ton Fontes ; Cargo: Desenvolvedor
exibirNomeECargo("Ton Fontes"); // Nome: Ton Fontes;Se você precisar retornar valores de uma função utilize a palavra reservada return, que retorna o valor gerado e interrompe a execução da função.
String gerarMsgNomeECargo(String nome, String cargo) {
return "Nome: $nome ; Cargo: $cargo";
}
print(gerarMsgNomeECargo("Ton", "Desenvolvedor")); // Nome: Ton ; Cargo: DesenvolvedorNo Dart, podemos tratar uma função como qualquer outro valor. Podemos, por exemplo, atribuir uma função a uma variável e passá-la como parâmetro para outra função, que executará essa variável:
Function printarErro = (String erro) {
print("Falha: $erro");
};
void checarIgualdade(String valor1, String valor2, Function callback) {
if(valor1 != valor2){
callback("Os valores são diferentes");
}
}
checarIgualdade("Ton", "Ton", printarErro); // Não printa nada
checarIgualdade("Ton", "Fontes", printarErro); // Encontramos um erro: Os valores são diferentesTambém como funções que executam apenas um comando
exibirMsg(String msg) => print(msg);
exibirMsg("Essa é minha menssagem."); // Essa é minha menssagem.Quando necessitamos trabalhar com arrays (estrutura de dados que armazena valores identificado por um index),
Podemos criar uma lista:
// Criando instância do List
var lista_usuarios = List(); // Também poderia ser new List()
// Usando o método add do objeto para adicionar novos valores a lista
lista_usuarios.add("Ton");
lista_usuarios.add("Matheus");
lista_usuarios.add("Felipe");
// Lista literal
var lista_usuarios_literal = ["Ton", "Matheus", "Felipe"];Os exemplos anteriores são iguais em seu resultado:
print(lista_usuarios[0]); // Ton
print(lista_usuarios[1]); // Matheus
print(lista_usuarios[2]); // Felipe
print(lista_usuarios_literal[0]); // Ton
print(lista_usuarios_literal[1]); // Matheus
print(lista_usuarios_literal[2]); // FelipeNote que utilizamos [número] após a variável para recuperar os valores. Esse número, chamamos de index, o qual é a posição dos valores na lista (que começa em 0).
Nota: No Dart, um List é um genérico. Isto é: passamos o tipo do dado que o mesmo irá armazenar como parâmetro.
O Objeto List nos fornece diversos métodos e propriedades que facilitam a interação, manipulação e a análise de arrays.
List lista_numeros = List();
lista_numeros.add(1);
lista_numeros.add(2);
lista_numeros.add(3);
lista_numeros.add(4);
lista_numeros.add(5);
lista_numeros.forEach((numero) {
print("Número: $numero");
});
// Resultado
// Número: 1
// Número: 2
// Número: 3
// Número: 4
// Número: 5O método forEach, irá iterar o array e executar a função de callback uma vez para cada valor.
Também, podemos combinar diversos métodos para gerar diferentes resultados:
lista_numeros.map((numero) {
return numero * 10;
}).toList().forEach((numero) {
print("Número: $numero");
});
// Resultado
// Número: 10
// Número: 20
// Número: 30
// Número: 40
// Número: 50Assim, o método map irá modificar cada um dos valores da List e, ao final do processo, exibimos o valor de cada um dos números na tela com o método forEach.
Podemos utilizar o atributo length para saber a quantidade de valores em uma List
print("Quantidade de valores: ${lista_numeros.length}"); // Quantidade de valores: 5Também, podemositerar um array, utilizando for
for(int index = 0; index < lista_numeros.length; index++) {
print("Linha ${lista_numeros[index]}");
}Map é uma estrutura similar à uma List. Porám, diferente da List (possui um index (tipo inteiro), um Map cada valor terá uma chave (que pode ser um objeto qualquer) correspondente.
Portanto, tanto List, como Map pode ser criado através de uma instância ou de forma literal.
NOTA: se criarmos um Map de forma literal, a chave só pode ser do tipo String
var nome_sobrenome = {
'Ton': 'Fontes',
'Matheus': 'Silva',
'Felipe': 'Freitas',
};
print(nome_sobrenome['Ton']); // Fontes
print(nome_sobrenome['Matheus']); // Silva
print(nome_sobrenome['Felipe']); // FreitasSe criarmos uma instância, qualquer objeto pode ser utilizado como chave:
var chave_valor = Map();
chave_valor[10] = 'Valor 1';
chave_valor[true] = 'Valor 2';
chave_valor["Chave"] = 'Valor 3';
print(chave_valor[10]); // Valor 1
print(chave_valor[true]); // Valor 2
print(chave_valor["Chave"]); // Valor 3Retorna um valor que indica se o item especificado está selecionado.
public bool GetSelected (int index);
Obter o índice:
foreach (int blah in listBox_TipoPeca.SelectedIndices)
{ var indice = blah.ToString(); }
Obter o nome dos itens selecionados:
string tmpStr2 = "";
foreach (var item in listBox_TipoPeca.SelectedItems)
{
tmpStr2 += listBox_TipoPeca.GetItemText(item) + "\n";
var num = listBox_TipoPeca.SelectedValue;
}
MessageBox.Show(tmpStr2);
O objetivo deste bog, não é mostrar como criar a codificação de um repository parttner ou service parttner, mas sim o conceito de forma breve e direta.
REPOSITORY
Martin Fowler define um repositório como:
Um Repositório faz a mediação entre o domínio e as camadas de mapeamento de dados, agindo como uma coleção de objetos de domínio na memória. Objetos de cliente constroem especificações de consulta de forma declarativa e as enviam ao Repositório para satisfação. Objetos podem ser adicionados e removidos do Repositório, assim como de uma simples coleção de objetos, e o código de mapeamento encapsulado pelo Repositório realizará as operações apropriadas nos bastidores.
Conceitualmente, um Repositório encapsula o conjunto de objetos persistidos em um armazenamento de dados e as operações realizadas sobre eles, fornecendo uma visão mais orientada a objetos da camada de persistência. O repositório também suporta o objetivo de alcançar uma separação clara e dependência unidirecional entre as camadas de mapeamento de domínio e de dados.
Um padrão de repositório é um padrão de design que medeia dados de e para o domínio e as camadas de acesso a dados (como Entity Framework Core / Dapper). Repositórios são classes que ocultam as lógicas necessárias para armazenar ou recuperar dados. Assim, nossa aplicação não se importará com o tipo de ORM que estamos usando, pois tudo relacionado ao ORM é tratado dentro de uma camada de repositório. Isso permite que você tenha uma separação mais limpa de preocupações. O padrão de repositório é um dos padrões de design mais usados para criar soluções mais limpas.
O padrão Repository, nos permite criar uma camada de abstração entre o acesso aos dados e a camada de lógica de negócios de uma aplicação. Ao usá-lo, estamos promovendo uma abordagem mais flexível para acessar nossos dados do banco de dados. Além disso, o código é mais limpo e mais fácil de manter e reutilizar. A lógica de acesso a dados está em uma classe separada, ou conjuntos de classes chamados de repositório, com a responsabilidade de persistir o modelo de negócios do aplicativo.
Observe, o argumento é fraco, mas não irreal, como foi muito bem ressaltado por Shawn McCool
“Any sufficiently designed object-oriented application automatically comes with this type of advantage. A very central concept to object-orientation is encapsulation. You can expose an API and hide implementation”
Em outras palavras, o que ele defende é que esse não deve ser o motivo real para a utilização / aplicação do padrão em seus projetos, já que a troca de ORM não acontece assim… num estalar de dedos… e o encapsulamento está ai pra facilitar essa troca, quando esta necessidade realmente existir.
Não podemos afirmar sobre o assunto. Mas pode ser usado quando desejar deixar as coisas mais organizadas e separadas e criar uma barreira (de controle e segurança) entre a aplicação e os seus dados, utilize repositórios. Mas, caso o projeto não necessite usar, não use, pois somente irá irão gerar desperdícios (de tempo, esforço, etc).
Repositório é ser um serviço de domínio, que abstrai a camada de persistência da sua aplicação e atua como API para os serviços de aplicação (controllers, CLI, etc). Ou seja, deve ser a única API de acesso confiável aos objetos de domínio e não deve ser responsável por conexões ao banco, envio de e-mail, etc…
Este é um dos tópicos mais debatidos na comunidade .NET Core. A Microsoft construiu o Entity Framework Core usando o Repository Pattern e Unit of Work Patterns. Então, por que precisamos adicionar outra camada de abstração sobre o Entity Framework Core, que é mais uma abstração do Data Access. A resposta para isso também é dada pela Microsoft.
A própria Microsoft recomenda (conselhor, aviso, advertir) o uso de Repository Patterns em cenários complexos para reduzir o acoplamento e fornecer melhor Testabilidade de suas soluções. Nos casos em que você deseja o código mais simples possível, deve-se evitar o Repository Pattern.
SERVICES
A ideia básica é a seguinte:
As classes Service são projetadas para fazer duas coisas:
O padrão Repository-Service divide a camada de negócios do aplicativo em duas camadas distintas.
O padrão Repository-Service depende da injeção de dependência para funcionar corretamente. As classes em cada camada da arquitetura terão classes de que precisam das camadas "inferiores" injetadas nelas.
Portanto, nossas classes Service serão classes simples de "passagem" para retornar as informações em seus respectivos repositórios.
Por último a Controller fará a Injeção de Dependência da classe de serviços.
O Repository-Service Pattern é uma ótima maneira de arquitetar um aplicativo complexo do mundo real. Cada uma das camadas (Repositório e Serviço) tem um conjunto bem definido de preocupações e habilidades e, mantendo as camadas separadas, podemos criar uma arquitetura de programa de fácil modificação e manutenção.
Um forte abraço a todos e até a próxima