Espero que a esta altura do campeonato você já tenha compreendido os fundamentos da análise e projeto orientados por objeto, além de tê-los experimentado na prática e acompanhado o nosso exemplo do estacionamento OOPark. O domínio do problema foi simplificado, mas ainda assim contém material suficiente para boas semanas de trabalho, dependendo do nível de detalhe exigido.
Como é impossível cobrir, em apenas um artigo, tudo o que foi necessário fazer para criar a aplicação completa, explicarei apenas os pontos essenciais, que ressaltam as vantagens de termos seguido o processo de MDD (Model-Driven Development, Desenvolvimento Dirigo pelo Modelo) oferecido pelo Delphi 2006 com o ECO III.
O Modelo Final(?)
Como já mencionei anteriormente, um modelo de classes sofre muitas alterações, em algumas fases mais do que em outras. Por exemplo, com relação ao modelo do artigo anterior foram acrescentados alguns atributos e métodos: Tipo (derivado) na classe Pessoa; Status, Entrar e Sair (no lugar de Finalizar) na classe Estada; Tipo (derivado) na classe TipoVeiculo; QtdeVagas e CalcVagasOcupadas na classe Estacionamento; Usuário e Senha na classe Funcionario; PlanoAvulso (método estático) na classe TabelaPreco.

Figura 1: Diagrama de classes
Essas alterações foram exigidas depois que iniciei o desenvolvimento da aplicação. Como foram feitas? Muito fácil: alterei o modelo, acrescentando os atributos e métodos, e pedi para que o ECO fizesse a evolução do banco de dados. Para isso, abri a unit EcoPersistenceMapperProvider e cliquei no botão Evolve Schema. Também podemos clicar com o botão esquerdo do mouse na superfície livre do designer e escolher a opção Evolve Schema. O wizard vai mostrar as alterações necessárias na tabela e ao clicar no botão Execute, o banco ficará sincronizado com o modelo! Mas isso só é necessário se foram acrescentados ou retirados atributos persistentes do modelo.
Especificando o Tipo de Cache
Nossa aplicação será em ASP.NET, portanto, múltiplas requisições podem ser feitas, o que exigirá a criação de diversos EcoSpaces, isto é, espaços de objetos, para atender às requisições. Para melhorar a performance e garantir a integridade de cada um deles, o ECO oferece algumas opções de cache e pooling. Na unit EcoSpaceProvider existe uma linha que deve ser alterada, para o nosso caso:
const
MODE: EcoSpaceStrategyHandler.SessionStateMode = EcoSpaceStrategyHandler.SessionStateMode.Always;
O valor default para esta constante é IfDirty, o que não nos ajuda muito aqui. Outro valor possível seria Never, o que tornaria o EcoSpace inútil para nossa aplicação, mas é utilizado em Web Services. Para maiores explicações sobre essa constante digite consulte o Help do Delphi 2006.
Atributos Derivados
Eu comentei que os atributos Tipo, das classes Pessoa e TipoVeiculo, eram derivados. Bom, essa é uma das mil maravilhas do ECO. É algo equivalente a um campo calculado de um DataSet, mas muito melhor. No caso desse atributo, ao adicioná-lo à classe TipoVeiculo altere a propriedade derived para True, e coloque a expressão OCL para derivar o valor, que nesse caso é apenas a concatenação dos atributos Fabricante e Modelo, com um espaço entre eles. Portanto, a propriedade DerivationOCL ficou assim: Fabricante + ‘ ’ + Modelo. Observe também que o ECO já ajustou a propriedade persistence para transient, pois esse valor não será armazenado no banco. Para indicar que esse atributo é derivado aparece uma barra ‘/’ no lado esquerdo do nome dele no diagrama.
O atributo Tipo da classe Pessoa é para indicar se trata-se de uma pessoa física (F) ou jurídica (J). A DerivationOCL ficou assim:
if self.oclIsTypeOf(PessoaJuridica) then
'J'
else
'F'
endif
Legenda, por favor!
O if da OCL não serve para desviar fluxo de execução, como é comum nas linguagens de programação, mas para definir um valor de retorno. É similar a um iif no C (ou a macro (condição ? valorTrue : valorFalse)), ou um IfThen, no Delphi, onde você passa como parâmetros uma condição booleana e dois valores. Se a condição for verdadeira, a função devolve o primeiro valor; se for falsa, devolve o segundo valor. Assim, nossa expressão OCL está testando se um objeto é da classe PessoaJuridica. Se for, o valor do atributo será a string ‘J’, caso contrário, será a string ‘F’.
Por que um atributo derivado é melhor que um campo calculado? Porque a expressão de derivação só é avaliada quando os atributos que a compõe são alterados. Nesse caso, somente se houver alteração no Fabricante e/ou no Modelo é que o atributo Tipo será recalculado. Isso economiza bastante tempo de CPU, ao mesmo tempo que garante que o atributo está sempre atualizado!
O Coração da Aplicação
A utilização de arquétipos e do Componente Genérico de Modelo (CGM, explicado na edição anterior) permite identificar rapidamente quais são os pontos principais do negócio, que são os Momentos-Intervalos (MI, classes com a cor rosa). Em volta deles gravitam as entidades complementares, mas não menos importantes.
Como em nosso caso temos apenas um MI (Estada, ver Figura 1), podemos dizer que essa classe é o coração do nosso sistema gerenciador de estacionamentos. Assim sendo, vamos dedicar bastante atenção a ela.
Existem dois momentos onde essa entidade é envolvida: na entrada do veículo e na sua saída. Geralmente um MI passa por diversos estados, que devem ser mapeados para que os controles devidos possam ser exercidos sobre o objeto. Para especificar esse comportamento dinâmico usamos o diagrama de máquina de estados, mostrado na Figura 2.
Para criar esse diagrama, clique com o botão direito sobre a classe Estada e escolha a opção Add | ECO State Machine. Note que o diagrama possui uma propriedade State attribute que define qual atributo da classe contém o estado atual. O nome padrão é State_1, mas volte ao diagrama de classes e altere o nome desse atributo para Status. Note que esse atributo tem sua propriedade Is state attribute como True.
Agora volte à máquina de estados e, na Tool Palette escolha o item Initial (estado inicial) e coloque um no diagrama. A seguir, coloque os três estados, com seus respectivos nomes. Também desenhe as transições entre eles.
Cada transição entre estados deve ter um método gatilho (trigger) que a dispare. Assim, certifique-se que os métodos Entrar, Sair e Cancelar, da classe Estada, estejam com a propriedade Is trigger como True. A seguir, clique na transição entre VeiculoEstacionado e VeiculoSaiu, e na propriedade Trigger escolha o método Sair. Na outra transição (para o estado Cancelar), escolha o método Cancelar como gatilho.
Selecione o estado VeiculoEstacionado e clique no editor da propriedade Entry Action. No editor de OCL abra o nó Triggers e escolha o método Entrar. Isso fará com que esse método seja executado quando o objeto “entrar” neste estado, o que acontecerá quando ele for criado, pois este é o estado inicial da máquina.
Como ler esta máquina de estados? Ela nos diz que a Estada inicia com o Status=’VeiculoEstacionado’, quando o veículo entra no estacionamento, e que esse estado não é um estado final, ou seja, ele é transitório (objeto instável). Desse estado pode-se ir para o Status=’Cancelada’, quando a Estada for cancelada (por algum erro ou outra condição especial), ou para o Status=’VeiculoSaiu’, quando o veículo sair do estacionamento. Essas duas situações são consideradas finais, pois nenhum processamento posterior é necessário nesse objeto. Dizemos que o objeto está estável.

Figura 2: Diagrama da máquina de estados da classe Estada
Com isso podemos codificar os métodos de negócio relacionados a esse comportamento. Lembre-se, para inserir o código abaixo abra o diagrama de classes, clique com o botão direito do mouse no método desejado e escolha a opção Go to Definition. O cursor será posicionado na declaração do método na classe. Aperte a tecla Ctrl e clique sobre o nome do método para ser levado até o corpo do mesmo.
procedure Estada.Entrar();
begin
DataEntrada := DateTime.Today;
HoraEntrada := DateTime.Now.TimeOfDay;
end;
procedure Estada.Sair();
begin
DataSaida := DateTime.Today;
HoraSaida := DateTime.Now.TimeOfDay;
Valor := CalcValor;
end;
procedure Estada.Cancelar();
begin
DataSaida := DateTime.Today;
HoraSaida := DateTime.Now.TimeOfDay;
end;
Também podemos entender esse mapeamento do comportamento como um workflow, isto é, um fluxo de trabalho que deve ser seguido pelo objeto para executar suas funções. Nosso workflow é bastante simples, mas poderíamos incrementá-lo de forma a verificar as condições anteriores para permitir uma transição de estado, por exemplo.
O Projeto da Aplicação
A Figura 3 mostra o mapa de navegação da nossa aplicação.

Figura 3: Mapa navegacional da aplicação
Basicamente existe uma página para edição de cada entidade do modelo, às vezes em formato de lista, às vezes em forma de mestre-detalhe. A página de login basicamente faz a verificação do usuário, que deve ser um funcionário. O menu principal fica em um controle de usuário, chamado Menu.ascx, que é uma tabela cheia de botões que, ao serem clicados, redirecionam para a respectiva página. Todas as páginas incluem o menu, permitindo navegar diretamente de uma para outra. Também coloquei um banner no topo das páginas, criado por minha filha Stella Maris, de 12 anos (isso é que é pai coruja, hein!). Todas elas também utilizam um arquivo chamado estilos.css, que contém a definição básica da cor do fundo e da fonte.
Não teremos espaço suficiente para explicar a criação de todas elas, infelizmente. Porém, com as orientações dadas neste artigo e um estudo minucioso do projeto, facilmente você entenderá como a mágica acontece.
Para guardar os dados do usuário eu criei uma classe TUsuario, declarada na unit UUsuario.pas. O processo de login identifica o funcionário e guarda também o estacionamento onde ele trabalha, além do seu nome e da data e hora de início da sessão. Essa classe não faz parte do modelo de classes, sendo utilizada apenas pela aplicação durante a sessão do usuário.
Também criei uma unit com alguns utilitários, chamada Heptagon.Utils.pas. São funções de uso comum e freqüente, então agrupei todas na classe ECOGadgets e estão disponíveis diretamente através da classe, pois são métodos de classe e não de instância.
A Página de Entrada do Veículo
Nossa tela para registrar a entrada do veículo chama-se entrada.aspx. Para criá-la, use o menu File | New | Other..., escolha a categoria Delphi for .Net Projects | New ASP.NET Files, e o item ECO ASP.NET page. Como usaremos esse item muitas vezes, recomendo colocá-lo no menu File | New, através da personalização disponível em File | New | Customize.... Localize o item na categoria acima e arraste-o para a lista no lado direito da janela, na posição mais adequada. Ao clicar Ok, veja que o item está no menu File | New.
A página criada contém diversos métodos para simplificar a utilização do framework, como por exemplo o método UpdateDatabase, que é chamado para fazer a persistência dos objetos que foram manipulados na página.
Uma boa técnica é salvar a página já com seu nome real. Escolha TWebForm1 no Object Inspector e renomeie a propriedade Name para TfEntrada. Agora salve a unit com File | Save As... com o nome entrada.aspx.
Nesse instante do negócio precisamos registrar a data e hora da entrada e qual o veículo. Conforme os requisitos especificados (ver a edição 59), a identificação do veículo é feita pela placa. A data e hora serão obtidas diretamente do sistema, então sobrou somente a placa para ser digitada. Ao identificarmos o veículo, queremos mostrar para o recepcionista o modelo, a cor, o cliente e o plano (mensal, avulso, ...), para confirmação.
Então precisaremos de 7 controles do tipo TextBox, juntamente com 8 do tipo Label (9, se quiser colocar a identificação da página). Um desses Labels será usado como mensagem de erro, no caso do usuário querer confirmar uma entrada com dados inválidos. Chamei esse de lblErro, coloquei a propriedade Text como “Dados incompletos!” e a Visible como False.
E para realizar a busca do veículo, um Button vai bem. Também precisaremos de um botão para confirmar a entrada e um para cancelar.
Pergunta: e se o veículo não estiver cadastrado, como será o caso de muitos que são clientes avulsos? Então precisaremos indicar o modelo e a cor também. Bom, a cor pode ser digitada no próprio TextBox, mas o modelo terá que ser escolhido da lista já cadastrada. Para isso, vamos colocar um DBWebGrid e um Button, para que o funcionário possa escolher um modelo.
Coloque os componentes no formulário, seguindo a sugestão da Figura 4. O único TextBox que deve permanecer editável é o da placa; os demais devem ter a propriedade ReadOnly ajustada para True.

Figura 4: Página de entrada do veículo (entrada.aspx)
Para validar se o usuário digitou a placa antes de apertar o botão Buscar Veículo usaremos o controle RequiredFieldValidator. Coloque um ao lado do botão, ajuste a propriedade ErrorMessage para “Digite a placa!” (sem as aspas, claro) e aponte a propriedade ControlToValidate para txtPlaca (o TextBox da placa). Essa validação é feita no lado do cliente, no navegador, não exigindo uma chamada ao servidor.
Preparando o Grid
Antes de mexer com o grid e codificar, vamos configurar o componente rhRoot. O mais importante é apontar a propriedade EcoSpaceType para o EcoSpace da aplicação, que é a classe OOParkEcoSpace.TOOParkEcoSpace. Para que ela apareça no combo é necessário compilar a aplicação. Dessa forma, esse ReferenceHandle está referenciando o nosso modelo de classes. Iremos utilizá-lo para fazer consultas no espaço de objetos.
Agora coloque um ExpressionHandle e um EcoDataSource (vide Figura 4). Chamei o ExpressionHandle de ehTiposVeiculos, apontei a propriedade RootHandle para o rhRoot e digitei na propriedade Expression a expressão OCL TipoVeiculo.allInstances->orderby(Tipo) (aconselho você a usar o editor de OCL para montar a expressão).
O EcoDataSource eu chamei de edsTiposVeiculos e cliquei no botão da propriedade EcoHandles para incluir o ehTiposVeiculos na sua lista de data sources. Com isso agora podemos utilizar o ExpressionHandle, que conterá uma lista de objetos, como uma fonte de dados para o grid.
Clique no grid e aponte a propriedade DBDataSource para o edsTiposVeiculos, e a propriedade TableName para o ehTiposVeiculos. Com isso as colunas Tipo, Fabricante e Modelo já devem ter aparecido no grid. Mas nós só precisamos da coluna Tipo, então vamos personalizar o grid.
Clique no botão da propriedade Columns do grid. O editor de propriedades da Figura 5 aparecerá. Desmarque a caixa “Create columns automatically at run time”. Abra o item Button Column, selecione o item Select e clique o botão “>” para criar uma coluna com um botão de seleção da linha. Na caixa “Text” digite apenas “>” (sem as aspas), e no combo Button type escolha a opção PushButton.
Agora selecione o item Bound Column e clique o botão “>”, para criar uma coluna de dados. Na caixa Header text digite “Tipo” e na caixa Text field digite “Tipo”, que é o nome do atributo. Ainda nesta janela, clique na opção Paging (do lado esquerdo) e ajuste a caixa Page size para 5, para mostrar apenas cinco linhas no grid por vez.
Clique o botão OK. Agora o grid deve conter apenas uma coluna sem cabeçalho e uma coluna “Tipo”. Ajuste a propriedade ReadOnly do grid para False, pois só queremos listar aqui. Coloque também Visible como False, fazendo o mesmo no botão “Escolher Modelo”.

Figura 5: Personalizando o grid.
Para dar um toque profissional, ainda com o grid selecionado clique no Object Inspector, logo abaixo das propriedades, no item Auto Format.... Aparecerá uma janela com diversas opções de esquemas de cores para o grid. Eu costumo usar a Professional 1. Escolha uma e clique OK.
O Código da Página de Entrada
Com a interface gráfica pronta, vamos botar um pouco a mão na massa, quero dizer, no código. Preencheremos os dados iniciais no evento Page_Load. Dê um duplo clique no formulário e digite o código a seguir:
procedure TfEntrada.Page_Load(sender: System.Object; e: System.EventArgs);
begin
EcoSpace.Active := True;
txtDataEntrada.Text := DateTime.Today.ToString('dd/MM/yyyy');
txtHoraEntrada.Text := System.String.Format('{0:HH:mm}', DateTime.Now);
end;
O código para o botão Buscar Veículo ficou assim:
procedure TfEntrada.btnBuscarVeiculo_Click(sender: System.Object; e: System.EventArgs);
var
V: Veiculo;
El : IElement;
begin
lblErro.Visible := False;
txtPlaca.Text := txtPlaca.Text.ToUpper;
// Consulta o veículo pela placa
El := EcoSpace.OclService.GetDerivedElement(rhRoot.Element,
'Veiculo.allInstances->select(Placa=''' +
txtPlaca.Text + ''')->first');
try
V := El.AsObject as Veiculo; // Achou o veículo
Session['Veiculo'] := V; // Guarda-o na sessão para uso posterior
// Preenche os campos da página
txtCor.Text := V.Cor;
txtCor.ReadOnly := True;
if Assigned(V.TipoVeiculo) then
txtModelo.Text := V.TipoVeiculo.Tipo
else
txtModelo.Text := 'Não informado';
if Assigned(V.Cliente) then
txtCliente.Text := V.Cliente.Pessoa.Nome
else
txtCliente.Text := 'Desconhecido';
if Assigned(V.Preco) then
txtPlano.Text := V.Preco.Item
else
txtPlano.Text := 'Avulso';
except
// Não achou o veículo
V := Veiculo.Create(EcoSpace);
txtModelo.Text := 'Escolher';
// Permite a digitação da cor
txtCor.ReadOnly := False;
txtCor.Text := '';
txtCliente.Text := 'Desconhecido';
txtPlano.Text := 'Avulso';
Session['Veiculo'] := V;
// Mostra o grid e o botão de escolha do modelo
btnEscolherModelo.Visible := True;
grdTiposVeiculos.Visible := True;
end;
end;
Para confirmar a entrada, o botão Confirmar faz o seguinte:
procedure TfEntrada.btnConfirmar_Click(sender: System.Object; e: System.EventArgs);
var
Est: Estada;
U : TUsuario;
V : Veiculo;
begin
V := Session['Veiculo'] as Veiculo;
// Verifica se os dados estão completos
if Assigned(V) and (txtCor.Text <> '') and Assigned(V.TipoVeiculo) then begin
// Cria um novo objeto Estada
Est := Estada.Create(EcoSpace);
// Chama o método de negócio para preparar o objeto
Est.Entrar;
Est.Veiculo := V;
if Est.Veiculo.Placa = '' then begin
Est.Veiculo.Placa := txtPlaca.Text;
Est.Veiculo.Cor := txtCor.Text;
end;
// Pega os dados armazenados no usuário
U := Session['Usuario'] as TUsuario;
Est.FuncionarioEntrada := U.Funcionario;
Est.Estacionamento := U.Estacionamento;
// Grava o objeto no banco
UpdateDatabase;
// Limpa a sessão
Session.Remove('Veiculo');
// Redireciona para a página inicial
Response.Redirect('inicio.aspx');
end
else
lblErro.Visible := True;
end;
Para cancelar é mais fácil ainda:
procedure TfEntrada.btnCancelar_Click(sender: System.Object; e: System.EventArgs);
var V: Veiculo;
begin
V := Session['Veiculo'] as Veiculo;
// Verifica se o objeto foi criado agora
if Assigned(V) and (V.Placa = '') then
V.AsIObject.Delete;
// Limpa a sessão
Session.Remove('Veiculo');
// Redireciona para a página inicial
Response.Redirect('inicio.aspx');
end;
E o código do botão Escolher Modelo:
procedure TfEntrada.btnEscolherModelo_Click(sender: System.Object; e: System.EventArgs);
var
TV: TipoVeiculo;
V : Veiculo;
begin
lblErro.Visible := False;
// Obtém o objeto atualmente selecionado no grid
TV := ECOGadgets.GetCurrentObject(grdTiposVeiculos, ehTiposVeiculos) as TipoVeiculo;
if Assigned(TV) then begin
V := Session['Veiculo'] as Veiculo;
V.TipoVeiculo := TV;
txtModelo.Text := TV.Tipo;
end;
end;
Saída Pela Esquerda...
A página de saída foi chamada de saida.aspx, e é invocada a partir da página estadas.aspx, que contém apenas um grid com as estadas em aberto do estacionamento (com Status=1). O funcionário identifica o veículo que está saindo pela placa e clica na linha correspondente. Aí, a identificação do objeto é passada pela URL, através do parâmetro RootId. Na página de saída, pegamos esse RootId e obtemos o objeto real novamente! Tudo automático!
Para construí-la, novamente criamos uma nova ECO ASP.NET Page. Insira uma tabela com duas colunas e oito linhas, para distribuir os controles. Na coluna 1 escreva diretamente na página os rótulos dos campos. Na coluna 2, coloque os Labels correspondentes à Figura 6. Não se esqueça dos botões Confirmar e Cancelar.
No rhRoot, como é de costume, aponte a propriedade EcoSpaceType para OOParkEcoSpace.TOOParkEcoSpace. Mas desta vez ajustaremos a propriedade StaticValueTypeName para indicar que essa é uma referência para um objeto da classe Estada. Clique no combo da propriedade e uma janelinha aparecerá embaixo. Dê um duplo clique na pasta amarela Types in the system, depois um duplo clique no item Classes, e escolha a classe Estada.

Figura 6: Página de saída (saida.aspx)
Para ligar os labels nos atributos do objeto é necessário configurar as colunas do rhRoot, de forma que estejam disponíveis para o databinding. Deixo esta parte como exercício para o leitor verificar o que foi feito no projeto.
O código do início da página ficou assim:
procedure TfSaida.Page_Load(sender: System.Object; e: System.EventArgs);
var
Id: string;
Est: Estada;
begin
EcoSpace.Active := True;
Id := Request.Params['RootId'];
if Assigned(Id) and (Id <> '') then
rhRoot.SetElement(ObjectForId(Id));
Est := rhRoot.Element.AsObject as Estada;
Est.DataSaida := DateTime.Today;
Est.HoraSaida := DateTime.Now.TimeOfDay;
lblValor.Text := System.&String.Format('{0:c}', Est.CalcValor);
lblDuracao.Text := Est.CalcTempo.ToString + ' h';
if not IsPostBack then
DataBind;
end;
O botão Confirmar:
procedure TfSaida.btnConfirmar_Click(sender: System.Object; e: System.EventArgs);
var
Est: Estada;
U : TUsuario;
begin
U := Session['Usuario'] as TUsuario;
Est := rhRoot.Element.AsObject as Estada;
Est.FuncionarioSaida := U.Funcionario;
Est.Sair;
UpdateDatabase;
Response.Redirect('inicio.aspx');
end;
E o botão Cancelar:
procedure TfSaida.btnCancelar_Click(sender: System.Object; e: System.EventArgs);
var
Est: Estada;
U : TUsuario;
begin
U := Session['Usuario'] as TUsuario;
Est := rhRoot.Element.AsObject as Estada;
Est.FuncionarioSaida := U.Funcionario;
Est.Cancelar;
UpdateDatabase;
Response.Redirect('inicio.aspx');
end;
E o resto das páginas?
Bom, como já expliquei, a maioria é apenas um grid para manipular a lista de objetos. Algumas são mais sofisticadas, como a cliente.aspx, que tem um mestre-detalhe com os veículos do cliente. Mais um exercício para o leitor entusiasmado!
A página inicio.aspx mostra as informações do usuário atual e do estacionamento, com a quantidade de vagas, quantas estão ocupadas e quantas estão disponíveis (Figura 7).

Figura 7: A tela principal (inicio.aspx)
Veja um exemplo da tela de entrada de um veículo (Figura 8).

Figura 8: A tela de entrada de veículo (entrada.aspx)
Conclusão
Nosso objetivo nesta série de artigos foi compartilhar alguns conceitos fundamentais para uma boa utilização da análise e projeto orientados por objeto, chegando até à construção da aplicação, aplicando os conceitos de programação orientada por objeto. Vimos que a abordagem OO desde o início facilita bastante o entendimento do domínio do problema, a especificação do modelo de classes e a programação das regras de negócio. Com um framework que cuide da persistência, do ciclo de vida dos objetos na memória e do contato com a interface gráfica, podemos nos concentrar no que realmente interessa para o negócio.
O Delphi 2006, com o ECO III, proporciona um ambiente robusto para o desenvolvimento de qualquer tipo de aplicação, seja Windows Forms, ASP.NET ou Web Services. Podemos (e até devemos) utilizar alguns design patterns para melhorar ainda mais a arquitetura da aplicação, mas isso é assunto para outra conversa.
Sugiro que você abra cada unit, estude os métodos, explore cada componente (propriedades e eventos), faça alterações e veja o resultado. Não digo que esse é um bom modelo a ser seguido de aplicação, mas oferece um parâmetro para sua própria criação. Ainda falta muita coisa para torná-la uma aplicação “de verdade”, mas como exemplo didático creio que foi suficiente. O resto é com você!
Desejo que essas dicas sejam sementes que possam germinar e frutificar na cabeça de cada leitor. Continuarei participando com outros artigos. Bom estudo e até a próxima!
Connect with Us