Mostrando postagens com marcador Introdução a Programção. Mostrar todas as postagens
Mostrando postagens com marcador Introdução a Programção. Mostrar todas as postagens

segunda-feira, 15 de dezembro de 2008

Delphi Intraweb 8 – Acessando dados – BDS2006

Neste artigo pretendo demonstrar como usar os componentes dataware do framwork Intraweb 8.
Tenho observado muitos problemas com essa versão da Intraweb. Graças a Deus parece que ninguém ousou usar esse framework, com algumas raras exceções. Se você veio aqui procurando saber se deve ou não iniciar um projeto com essa tecnologia, minha sugestão é: Procure outra coisa, ou na falta de alternativa use Delphi 7 com a versão 5.o do Intraweb.

Vamos construir uma aplicação que vai acessar a base de dados Northwind do SQL Server. Northwind, é uma base criada por default no SQL server, na instalação, cujo objetivo é didático. Ela é usada nos treinamentos oficiais da Microsoft, e exemplifica integralmente um banco de dados usado em um cenário real, profissional



Explicando sobre a base de dados

OBS:
Caso você já conheça a base Northwind, pode pular essa parte indo direto para “Iniciando um projeto Intraweb”.


Vamos trabalhar com as tabelas Categories (Refere-se as categorias dos produtos) e Products (Produtos).
Com o botão direito do mouse sobre “Diagrams”, na tree view, dentro do nó “Northwind”, podemos criar um DER (Diagram Entidade Relacionamento) das tabelas e entender como os dados se relacionam neste data base.



Nosso exemplo estará restrito ao contexto demonstrado na figura abaixo:



O relacionamento demonstrado acima indica que uma “Categoria” pode classificar muitos produtos. Sendo que o “Produto” somente pode ser classificado por uma “Categoria”. Este tipo de relacionamento chama-se “Um para N” (1-N). No relacionamento “Um para N” a chave (campo de relacionamento) aponta para a tabela “N”, no caso “Produtos”.
Isso quer dizer que na tabela Producs, existe uma coluna que referencia a tabela “Categories”. Em outras palavras: A coluna “CategoryID”, de “Producs” aponta para a “CategoryID” de “Categories”.


Iniciando um projeto Intraweb

Inicie um novo projeto do DBS2006: No menu File ►New ► Other ►Intraweb, selecione “Intraweb Application Wizard”.



No diálogo seguinte, “Intraweb Application Wizard”, definiremos alguns parâmetros da nova aplicação que criaremos. Configure conforme ilustrado na imagem abaixo:



Adicione na Unit1 os seguintes componentes:

IWDBEdit1: TIWDBEdit
IWDBListbox1: TIWDBListbox
IWDBNavigator1: TIWDBNavigator
IWDBLookupComboBox1: TIWDBLookupComboBox
IWButton1: TIWButton
IWCheckBox1: TIWCheckBox
ADOConnection1: TADOConnection
ADODataSet: TADODataSet, altere a propriedade “Name” para: AdsCategotias.
DataSetProvider: TdataSetProvider , altere a propriedade “Name” para: DspCategotias
ClientDataSet: TClientDataSet, altere a propriedade “Name” para: CdsCategotias
DataSource: TDataSource, altere a propriedade “Name” para: DsCategotias
ADODataSet: TADODataSet, altere a propriedade “Name” para: AdsProdutos
DataSetProvider: TDataSetProvider, altere a propriedade “Name” para: DspProdutos
ClientDataSet: TClientDataSet, altere a propriedade “Name” para: CdsProdutos
DataSource: TDataSource, altere a propriedade “Name” para: DsProdutos
OBS: A partir do DBS 2005 foi inroduzido na IDE do Delphi um novo conceito para a “Tool Palette”. Para vizualizá-la acesse o menu Vew ► Tool Palette. Hot key “Ctrl + Alt + P” ou digite “Alt” seguido de “V” seguido de “L”.



Uma forma fácil de adicionar um novo componente ao seu projeto é clickar sobre o ícone de filtro na Tool Palette e digitar o nome do componente. Veja imagem abaixo:



Veja o meu Form como ficou em tempo de projeto:




Conecte os componentes de acesso a dados da seguinte forma:

Selecione o IWEdit1, acesse o Objetc Inspector (F11). Associe IWDbEdit1, através da propriedade DataSource, ao DsProdutos. Defina a coluna (campo) que será associada ao IWEdit na propriedade “DataField”. Selecione “ProductName”.

Selecione o DsProdutos, acesse o Objetc Inspector (F11). Associe DsProdutos, através da propriedade DataSet, ao CdsProdutos.

Selecione o CdsProdutos, acesse o Objetc Inspector (F11). Associe CdsProdutos, através da propriedade ProviderName, ao DspProdutos.

Selecione o DspProdutos, acesse o Objetc Inspector (F11). Associe DspProdutos, através da propriedade “DataSet”, ao AdsProdutos.

Selecione o AdsProdutos, acesse o Objetc Inspector (F11). Associe AdsProdutos, através da propriedade “Connection”, ao ADOConnction1.


Conectando o acesso a categoria de produtos:

Selecione o DsCategotias, acesse o Objetc Inspector (F11). Associe DsCategotias, através da propriedade DataSet, ao CdsCategotias.

Selecione o CdsCategotias, acesse o Objetc Inspector (F11). Associe CdsCategotias, através da propriedade ProviderName, ao DspCategotias.

Selecione o DspCategotias, acesse o Objetc Inspector (F11). Associe DspCategotias, através da propriedade “DataSet”, ao AdsCategotias.

Selecione o AdsCategotias, acesse o Objetc Inspector (F11). Associe AdsCategotias, através da propriedade “Connection”, ao ADOConnction1.


Conectando o IWDBLookupComboBox1:


Selecione o IWDBLookupComboBox1, acesse o Objetc Inspector (F11). Associe IWDBLookupComboBox1, através da propriedade DataSource, ao DsProdutos. Defina a coluna (campo) que será associada ao IWDBLookupComboBox1 na propriedade “DataField”. Selecione “CategoryID”. A associação ao CdsCategorias deve ser feita através das propriedades:

1 – “ListSource”, nela selecione “DsCategotias”.
2 - “KeyField”, nela selecione “CategoryID”.
3 - “ListField”, nela selecione “CategoryName”

Esses três campos são do CdsCategorias. Veja mais detalhes sobre a técnica de Lookup ...




Conectando o IWDBListbox1:

Selecione o IWDBListbox1, acesse o Objetc Inspector (F11). Associe IWDBListbox1, através da propriedade DataSource, ao DsProdutos. Defina a coluna (campo) que será associada ao IWDBListbox1 na propriedade “DataField”. Selecione “Discontinued”. Este campo indica se o produpo foi descontinuado ou não.


Definindo os valores no DBListBox:

Na propriedade “Items”, click para acessar o editor. Digite “True”, em seguida, na linha de baixo, digite “False”. Como o CdsProdutosDiscontinued é um “TbooleanField” isso vai funcionar.





Na propriedade ItemsHaveValues, Selecione “False”.

Para a propriedade Height digite 41.


Conectando o IWDBNavigator:


Selecione o IWDBNavigator1, acesse o Objetc Inspector (F11). Associe IWDBNavigator1, através da propriedade DataSource, ao DsProdutos.


String connection no ADOConnection


Provider=SQLOLEDB.1;Persist Security Info=True;User ID=sa;Initial Catalog=Northwind;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=Landjah;Use Encryption for Data=False;Tag with column collation when possible=False

Conectei no SQL Server, usarei as tabelas Categories e Products do banco “Northwind”. Ele é o banco padrão do SQL Server para exemplo e estudo.

Comando SQL nos Ads:
AdsCategorias
Propriedade CommandText:
select CategoryID, CategoryName from Categories

AdsProdutos
Propriedade CommandText:
select * from Products


Codificando o evento OnClick do IWButton1:
procedure TTADo.IWButton1Click(Sender: TObject);
begin
CdsCategotias.Open;
CdsProdutos.Open;
end;


Podemos também trabalhar com o evento OnCreate do Form.
procedure TTADo.IWAppFormCreate(Sender: TObject);
begin
IWButton1Click(IWButton1);
end;

Agora você pode executar a aplicação e testar, navegando, alterando, inserindo, ou excluindo dados.


Veja a imagem ilustrando as conexões data ware no Delphi:



Para rodar o exemplo pressione F9, quando aparecer o diálogo referente ao servidor, pressione F9 novamente.



No IWCheckBox1, defina para a propriedade Caption = 'Editar Produto'. No evento OnClick programe:
procedure TTADo.IWCheckBox1Click(Sender: TObject);
begin
if IWCheckBox1.Checked then
CdsProdutos.Edit;
end;


Agora no evento AfterPost do CdsProdutos sicronise o comportamento do IWCheckBox:
procedure TTADo.CdsProdutosAfterPost(DataSet: TDataSet);
begin
IWCheckBox1.Checked := False;
end;


Testando o comando do IWCheckBox



Salvando as alterações realizadas




Sucesso!

Artigo completo (View Full Post)

domingo, 24 de agosto de 2008

Definido Parâmetros com valor default

Podemos definir argumentos de sub-rotinas possuindo valores pré-definidos. Esse recurso, os parâmetros de valor default, foi introduzidos no Delphi na versão 4. Desde então os progrmadores Delphi desfrutaram da capacidade de fornecer um valor default para um parâmetro de procedimento ou função sem a obrigatoriedade de passar esse parâmetro quando a rotina é chamada.
Para declarar um procedimento ou função que contenha parâmetros de valor default, coloque um sinal de igualdade e o valor default depois do tipo de parâmetro, como mostrado no exemplo a seguir:

procedimento EstacaoZNDefaultTeste(const TextValue: string; NumValue: Integer = 0);


Veja que é possível chamar o procedimento “EstacaoZNDefaultTeste ( )” de duas formas:

Uma delas, digamos que seja a primeira, você pode atribuir valor para ambos os parâmetros:

EstacaoZNDefaultTeste (‘hello ZN World’, 22);

Outra forma, digamos seja a segunda, você pode atribuir apenas o parâmetro “TextValue” e usar o valor default para “NumValue”:

EstacaoZNDefaultTeste (‘Hello Zn Landjah!!’); // valor default será usado pelo compilador para NumValue.

Devemos atender ao seguintes pontos para respeitar regras ao usar parâmetros (argumentos) de valor default:
1 - Os parâmetros com valores default devem aparecer no final da lista de parâmetros.
2 - Os parâmetros sem valores default não devem vir depois dos parâmetros com valores default em uma lista de parâmetros da função ou procedimento.
3 - Os parâmetros de valor default devem ser de um tipo ordinal, ponteiro ou conjunto.
4 - Os parâmetros de valor default devem ser passados por valor ou como constante. Eles não devem ser parâmetros não-tipificados ou de referência (out).

Um dos maiores benefícios dos parâmetros de valor default é poder criar versões mais diferentes de funcionalidades (funções e procedimentos) pré-existentes sem sacrificar a compatibilidade com versões anteriores. Ou seja, é possível durante a manutenção de um sistema recodificar determinada função sem sofrer reflexos am partes antigas do sistema definindo o novo parâmetro com valor default. Isso lhe poupará de fazer refactoring no sistema todo.


Artigo completo (View Full Post)

domingo, 10 de agosto de 2008

O princípio da caixa preta

Caixa Preta: O segundo principal objetivo que devemos buscar ao construir sub-rotinas é o comportamento de uma caixa preta. O mundo externo a caixa preta não conhece o que existe dentro dela, não sabe como ela realiza uma determinada tarefa. O mundo externo sabe apenas o que ela é capaz de fazer. Logo, considerando esse raciocínio é correto afirmar que: Uma sub-rotina deve ser como uma Caixa Preta, onde entram dados que serão processados (veja seobre parâmetros em) objetivando a obtenção de um valor de retorno específico, o qual atenderá alguma demanda do usuário.


A síndrome da fobia da Caixa Preta

Existe um tipo de profissional de TI que não se constrange em demonstrar seu incômodo quando estão trabalhando em programas muito modularizados. Eles reclamam do fato da falta de visibilidade quando uma rotina chama outra sub-rotina, ou várias. Será que ele não sabe que o objetivo é justamente esse? Algumas vezes não resisti e perguntei: Qual o problema no fato de determinada tarefa estar sendo realizada por uma dun-rotina, sedo que para ver o que ela faz basta debuggar fazendo um trace? A resposta em todas as vezes foi: “Não tenho certeza sobre o que a sub-rotina faz ...” . Para mim, fica parecendo que no fundo eles tem medo da caixa preta ... hehehheh. E conhecido que o ser humano teme o desconhecido, mas espera um pouco, amigo!?!?!??! Fala sério! Vc ta com medinho da caixa preta 02?
Esses programadores são conhecidos como “Jaspion”. Porque, no menor sinal de dificuldade apelam pro gigante guerreiro
Daileon sem o menor pudor. Seus códigos são verdadeiros gigantes muilti-funcionais. A rotina do cara faz de tudo, se bobear até serve cafezinho. Cruz credo! Eu já vi um Jaspion legitimo justificar-se que tinha ficado grande pois os trechos passiveis de modularização só seriam chamados apenas um vez, portanto, segundo ele, não deviam ser modularizados. Conclusão, o código do cara ficou parecendo um miojo bizonho, grudento e nojento.
Fico pensando quais seriam as causas para tanto medo de caixa preta. Será que é porque não conhecem o conceito? Bem, todas as vezes que perguntei a resposta foi: “Conheço, claro!”. Mas, não parece tão claro. O Jaspion só demonstra ter fé no código escrito por ele. Que irônico .... heheh!!!! Será que é arrogância, pura e simplesmente? Provavelmente são as duas coisas, pois não existe arrogância sem ignorância. Bem, posso não ter a resposta para essa questão, mas creio que ficará difícil negar que os Jaspoins têm medo da caixa preta.




Artigo completo (View Full Post)

Sub-Rotina - Programação

Comumente chamamos as sub-rotinas de “Procedimentos” (procedure) ou “Funções” (function). Podemos entendê-las como um trecho de programa escrito separadamente. Como conseqüência temos um trecho menor de código que executa uma única ação específica, isso facilita o entendimento (interpretação) aumentando, portanto a manutenibilidade do código.
Logo, uma sub-rotina pode ser usada em várias partes do sistema, dessa maneira podemos ter aproveitamento em diversos momentos diferentes de uma determinada ação, com a vantagem de que o gerenciamento da lógica dessa ação estar centralizado. Além de poder ser usada em várias partes do programa, a mesma sub-rotina pode, e deve, ser reaproveitada em outros sistemas ou programas. Em reflexo do reaproveitamento, devemos pensar no acoplamento que isso gera. Por isso, é muito importante estar atento e obstinado em alcançar um alto grau de COESÃO para a sub-rotina que estamos codificando.
Um ponto muito sensível nesta abordagem é que geralmente as variáveis que uma sub-rotina vai usar quase sempre são as variáveis do programa que a chamou. Isso é um grau de acoplamento que devemos evitar a todo custo. Devemos criar nossas sub-rotinas com argumentos. Eles são os parâmetros, através deles a sub-rotina pode receber e retornar valores garantindo uma comunicação entre ela e o mundo externo. Ou seja, o programa que a chamou, com uma baixo grau de acoplamento e um alto grau de COESÃO. Ela deve funcionar como uma caixa preta (Wikipédia).
Parâmetros – São os argumentos da sub-rotina. Seguindo a linha de raciocínio acima, os parâmetros são a comunicação da sub-rotina com os demais módulos que a chamarão em algum momento. Podemos entendê-los como a porta de entrada e a porta de saída da Caixa Preta.
Portanto, uma “function” ou “procedure” (em basic “Sub”), sub-rotinas que são, podem receber um conjunto de dados, que serão processados para um objetivo final. Entretanto, no momento de se declarar a sub-rotina é necessário que o programador defina quais, de que tipo, e quantas serão as entradas e saídas necessárias para que a mesma funcione corretamente. Ou seja, podemos e devemos declarar quem são os parâmetros de entrada, quem são os de saída. Podemos também definir valores default para alguns argumentos.

procedure SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean = False);

implementation

procedure TManagerGridZn.SetPopupMenu(const Value: TPopupMenu;
const nAllowCreate: Boolean);
begin
if (FPopupMenu = Value) and (Value <> nil) then Exit;

(* "nAllowCreate" define se o popup menu será sempre criado.
Considerando o caso de a mesma instância da classe manipular,
alternadamente, vários DBGrids que compartilham o mesmo popup menu. *)
If not Assigned(Value) or (nAllowCreate) then
begin
FPopupMenu := TPopupMenu.Create(Self);
FGrid.PopupMenu := FPopupMenu;
(* Avisa a classe que o popup menu foi criado *)
FIsPopupExistent := peInternalCreated;
end
else
begin
FPopupMenu := Value;
(* Avisa a classe que o popup menu já existia,
portanto possui itens. Ele foi associado com esses itens *)
FIsPopupExistent := peExternalAssociation;
end;

(* notifica a classe que o popup menu foi desassociado *)
if FPopupMenu <> nil then
FPopupMenu.FreeNotification(Self);
end;


O exemplo acima refere-se a um trecho de código usado nos artigos Construção de Componentes - VI e Construção de Componentes V a sub-rotina em questão trata da construção dinâmica de um Popup menu num DBGrid. Os valores atribuídos ao campo “FIsPopupExistent” é um tipo que criei para controlar se o Grid já possuía um menu Popup ou não.

 
TPopupExistent = (peExternalAssociation, peInternalCreated, peInexistent);


O argumento "nAllowCreate" possui um valor default definido igual a false, veja na linha 02. A Palavra resrvada "const" indica que é um parâmetro de entrada. Ou seja, a presença da palvra reservada "const" inica que "nAllowCreate" é uma porta de entrada da caixa preta. Para definir o parämetro de saída devemos usar a palavra reservada "var" antes do identificador.


Veja mais sobre o assunto em "Modularização em Programação: Coesão X Acoplamento, OO X Estruturação, Portugol X UML - a partir de um algoritmo simples".

Artigo completo (View Full Post)

Modularização em Programação: Coesão X Acoplamento, OO X Estruturação, Portugol X UML - a partir de um algoritmo simples

Algoritmo III – Um passo além ... continuação

Esse artigo é continuação de Algoritmo III – Um passo além da teoria. Onde ficou faltando exemplificarmos a estrutura de desvio condicional aninhado. Pretendo agora completar o que ficou pendente e, na mesma linha de abordagem que venho adotando ao falar sobre algoritmos, aproveitar também para destacar alguns pontos importantes que implicam na aplicação pratica do conhecimento sobre algoritmos. Portanto, além de mostrar exemplos da implementação do algoritmo em diferentes linguagens, criarei versões diferentes do mesmo exemplo onde demonstrarei o efeito da modularização, suas vantagens, bem como apontar uma caminho de como fazer isso. Que lógica seguir, qual o alvo buscar na hora de pensar em modularizar um programa.
Agora que está claro o que iremos fazer, falta explicar o “como”. Usarei um único algoritmo. Um que seja muito simples. Ao ponto de a única coisa nele que vale apena ser mencionada é o fato que ele implementa a estrutura de desvio condicional aninhado. Desta forma estamos focados no que tange ao conceito lógico que desejamos abordar. Por isso o algoritmo escolhido será o tradicional e simplório algoritmo de verificação do maior e o menor valor entre três números. Delphi e Java, a princípio, são as linguagens que usarei. Quanto a modularização, iniciaremos com o um primeiro exemplo bem primário onde codificarei o programa de forma seqüencial, todo num único fluxo. Forma mais comumente adotada (infelizmente). Em seguida, numa segunda versão evoluiremos o primerio exemplo aplicando um nível de modularização sob uma perspectiva estruturada. Para finalizar, construiremos uma terceira versão aumentando o grau de modularização sob uma perspectiva OO. O fato de neste terceiro exemplo o grau de modularização ser maior que o anterior não está diretamente relacionado com a abordagem OO. Ou seja, também é possível alcançar o mesmo grau usando estruturação.
Partiremos agora para a construção do primeiro exemplo do algoritmo que determina o Maior e o Menor valor entre três valores digitados.

Algoritmo não modularizado – aplicando a estrutura de decisão “desvio condicional”: Esse é o nosso ponto de partida ...


PROGRAMA DeterminaMaiorMenor;
VARIÁVEIS
Num1Zn, Num2Zn, Num3Zn: NUMÉRICO;
INÍCIO
LEIA (Num1Zn, Num2Zn, Num3Zn);

// Determinando o maior valor
SE (Num1Zn > Num2Zn) E (Num1Zn > Num3Zn) ENTÃO
ESCREVER (“Maior = ” + Num1Zn)
SENÃO SE (Num2Zn > Num1Zn) E (Num2Zn > Num3Zn) ENTÃO
ESCREVER (“Maior = ” + Num2Zn)
SENÃO
ESCREVER (“Maior = ” + Num3Zn);

// Determinando o menor valor
SE (Num1Zn < Num2Zn) E (Num1Zn < Num3Zn) ENTÃO
ESCREVER (“Menor = ” + Num1Zn)
SENÃO SE (Num2Zn < Num1Zn) E (Num2Zn < Num3Zn) ENTÃO
ESCREVER (“Menor = ” + Num2Zn)
SENÃO
ESCREVER (“Menor = ” + Num3Zn)

FIM.



O algoritmo não deve apresentar detalhes específicos da tecnologia definida pela linguagem. Por isso você vai perceber que na implementação do algoritmo numa linguagem vai aparecer linhas de código a mais, que objetivam lidar com detalhes exclusivos da tecnologia. Entretanto, o algoritmo pode e deve apresentar uma modularização adequada. Veremos adiante.

Implementando o algoritmo não modularizado em Delphi


procedure TForm1.Button1Click(Sender: TObject);
var
Num1Zn, Num2Zn, Num3Zn, ZnMaior, ZnMenor: Double;
StrNum1Zn, StrNum2Zn, StrNum3Zn, MsgSaida: String;
SaoIguais: boolean ;
begin
// Entrada de dados
StrNum1Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor', 'Digite o número', '');
StrNum2Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor', 'Digite o número', '');
StrNum3Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor', 'Digite o número', '');

// Processamento
Num1Zn := StrToFloat(StrNum1Zn);// convertendo de string para Double
Num2Zn := StrToFloat(StrNum2Zn);// convertendo de string para Double
Num3Zn := StrToFloat(StrNum3Zn);// convertendo de string para Double

// Comparando para encontrar o maior
(***********************************)
(* Antes, testo se eles são iguais *)
SaoIguais := (Num1Zn = Num2Zn) and (Num1Zn = Num3Zn);
if SaoIguais then
begin
msgSaida := 'Os números são iguais - %f';
ZnMaior := Num1Zn;
end
else
begin
// Determinando o maior valor
msgSaida := 'O maior número é: %f';
if ((Num1Zn > Num2Zn) and (Num1Zn > Num3Zn)) then
ZnMaior := Num1Zn
else if ((Num2Zn > Num1Zn) and (Num2Zn > Num3Zn)) then
ZnMaior := Num2Zn
else
ZnMaior := Num3Zn;

// Determinando o menor valor
msgSaida := msgSaida + ' | O menoor número é: %f';
if ((Num1Zn < Num2Zn) and (Num1Zn < Num3Zn)) then
ZnMenor := Num1Zn
else if ((Num2Zn < Num1Zn) and (Num2Zn < Num3Zn)) then
ZnMenor := Num2Zn
else
ZnMenor := Num3Zn;
end;

(*Saída de dados*)
Self.Canvas.Refresh;
Self.Canvas.TextOut(12, 70, 'www.estacaozn.blogspot.com');
Self.Canvas.TextOut(12, 100, Format(MsgSaida, [ZnMaior, ZnMenor]));
end;



Observe que temos código demais para uma tarefa tão simples ...

Implementando o algoritmo não modularizado em Java

package verificamaiorzn;

import javax.swing.JOptionPane;

/**
*
* @author GMottaZn
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String strNum1Zn, strNum2Zn, strNum3Zn,msgSaida;
double Num1Zn, Num2Zn, Num3Zn, ZnMaior, ZnMenor;
boolean saoIguais;
// Entrada de dados
strNum1Zn = JOptionPane.showInputDialog("Digite o primeiro zn número");
strNum2Zn = JOptionPane.showInputDialog("Digite o segundo zn número");
strNum3Zn = JOptionPane.showInputDialog("Digite o terceiro zn número");

// processamento
Num1Zn = Double.parseDouble(strNum1Zn); // convertendo de string para Double
Num2Zn = Double.parseDouble(strNum2Zn); // convertendo de string para Double
Num3Zn = Double.parseDouble(strNum3Zn); // convertendo de string para Double
// Comparando para encontrar o maior
// Antes, testo se eles são iguais
saoIguais = (Num1Zn == Num2Zn) && (Num1Zn == Num3Zn);
if (saoIguais){
msgSaida = "Os números são iguais - ";
ZnMaior = Num1Zn;
}// então procuro o maior
else {
msgSaida = "O maior número é: ";
if ((Num1Zn > Num2Zn) &&(Num1Zn > Num3Zn)){
ZnMaior = Num1Zn;
}
else {
if ((Num2Zn > Num1Zn) &&(Num2Zn > Num3Zn)){
ZnMaior = Num2Zn;
}
else
ZnMaior = Num3Zn;
};
//Determina o menor valor
msgSaida = msgSaida + " | O menor número é: ";
if ((Num1Zn < Num2Zn) &&(Num1Zn < Num3Zn)){
ZnMenor = Num1Zn;
}
else {
if ((Num2Zn < Num1Zn) &&(Num2Zn < Num3Zn)){
ZnMenor = Num2Zn;
}
else
ZnMenor = Num3Zn;
};
};
// Saída de dados
JOptionPane.showMessageDialog(null, " Estação Zn - " + msgSaida + ZnMaior,
"www.estacaozn.blogspot.com", JOptionPane.INFORMATION_MESSAGE);

// Finaliza programa
System.exit(0);

}
}



Modificando o Algoritmo para que ele apresente o primeiro nível de modularização. Vamos transformar o fluxo que determina o maior valor e transformar numa sub-rotina (um sub-programa). Faremos o mesmo para o segundo fluxo, o que determina o menor valor. Logo, teremos nosso algoritmo modularizado em duas sub-rotinas independentes, cada uma com um objetivo específico.


PROGRAMA DeterminaMaiorMenor;
VARIÁVEIS
Num1Zn, Num2Zn, Num3Zn: NUMÉRICO;
// primeira sub-rotina
Função EncontraMaior(Num1Zn, Num2Zn, Num3Zn: Double): String;
Constatnte
MsgSaida = 'O maior número é: ;
var
ZnMaior: Double;
Início
// Determinando o maior valor
SE (Num1Zn > Num2Zn) E (Num1Zn > Num3Zn) ENTÃO
ZnMaior := Num1Zn
SENÃO SE (Num2Zn > Num1Zn) E (Num2Zn > Num3Zn) ENTÃO
ZnMaior := Num2Zn)
SENÃO
ZnMaior := Num3Zn);

EncontraMaior := MsgSaida + ZnMaior;
Fim;

// segunda sub-rotina
Função EncontraMenor(Num1Zn, Num2Zn, Num3Zn: Double): String;
Constatnte
MsgSaida = 'O maior número é: ;
var
ZnMenor: Double;
Início
// Determinando o menor valor
SE (Num1Zn < Num2Zn) E (Num1Zn < Num3Zn) ENTÃO
ZnMenor := Num1Zn
SENÃO SE (Num2Zn < Num1Zn) E (Num2Zn < Num3Zn) ENTÃO
ZnMenor := Num2Zn)
SENÃO
ZnMenor := Num3Zn);

EncontraMenor := MsgSaida + ZnMenor;
Fim;

função SaoIguais(Num1Zn, Num2Zn, Num3Zn: Double): Boolean;
Início
SaoIguais := (Num1Zn = Num2Zn) and (Num1Zn = Num3Zn);
Fim;
//Programa principal
INÍCIO
LEIA (Num1Zn, Num2Zn, Num3Zn);
SE SaoIguais(Num1Zn, Num2Zn, Num3Zn) ENTÃO
ESCREVA(“Os números são iguais”)
SENÃO
INÍCIO
ESCREVA(“O menor número é: ” + EncontraMenor(Num1Zn, Num2Zn, Num3Zn));
ESCREVA(“O maior número é: ” + EncontraMaior(Num1Zn, Num2Zn, Num3Zn));
FIM;

FIM.


Sub-Rotina – Comumente chamamos as sub-rotinas de “Procedimentos” (procedure) ou “Funções” (function). Podemos entendê-las como um trecho de programa escrito separadamente.


Implementando o algoritmo modularizado em Delphi
Primeiro as sub-rotinas


function EncontraMaior(const Num1Zn, Num2Zn, Num3Zn: Double): String;
const
MsgSaida = 'O maior número é: %f';
var
ZnMaior: Double;
begin
// Determinando o maior valor
if ((Num1Zn > Num2Zn) and (Num1Zn > Num3Zn)) then
ZnMaior := Num1Zn
else if ((Num2Zn > Num1Zn) and (Num2Zn > Num3Zn)) then
ZnMaior := Num2Zn
else
ZnMaior := Num3Zn;
Result := Format(MsgSaida, [ZnMaior]);
end;



function EncontraMenor(const Num1Zn, Num2Zn, Num3Zn: Double): String;
const
MsgSaida = 'O menor número é: %f';
var
ZnMaior: Double;
begin
// Determinando o menor valor
if ((Num1Zn < Num2Zn) and (Num1Zn < Num3Zn)) then
ZnMaior := Num1Zn
else if ((Num2Zn < Num1Zn) and (Num2Zn < Num3Zn)) then
ZnMaior := Num2Zn
else
ZnMaior := Num3Zn;
Result := Format(MsgSaida, [ZnMaior]);
end;

Verificar se os valores são iguais é um plus ... então vai ...

function SaoIguais(const Num1Zn, Num2Zn, Num3Zn: Double; var MsgSaida: String): Boolean;
const
AMsgSaida = 'Os números são iguais: %f';
begin
Result := (Num1Zn = Num2Zn) and (Num1Zn = Num3Zn);
if Result then
MsgSaida := Format(AMsgSaida, [Num1Zn]);
end;

A seguir codificaremos o programa principal "DeterminaMaiorMenor"


procedure TForm1.Button2Click(Sender: TObject);
var
Num1Zn, Num2Zn, Num3Zn: Double;
StrNum1Zn, StrNum2Zn, StrNum3Zn, MsgSaida: String;
begin
// Entrada de dados
StrNum1Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');
StrNum2Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');
StrNum3Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');

// Processamento
Num1Zn := StrToFloat(StrNum1Zn);// convertendo de string para Double
Num2Zn := StrToFloat(StrNum2Zn);// convertendo de string para Double
Num3Zn := StrToFloat(StrNum3Zn);// convertendo de string para Double
if not (SaoIguais(Num1Zn, Num2Zn, Num3Zn, MsgSaida)) then
begin
MsgSaida := EncontraMaior(Num1Zn, Num2Zn, Num3Zn);
MsgSaida := MsgSaida + ' | ' + EncontraMenor(Num1Zn, Num2Zn, Num3Zn);
end;

(*Saída de dados*)
Self.Canvas.Refresh;
Self.Canvas.TextOut(12, 70, 'www.estacaozn.blogspot.com');
Self.Canvas.TextOut(12, 100, MsgSaida);
end;


Observe que o número de linhas do programa principal diminuiu sensivelmente. Entretanto, o que é pouco comentado a respeito de modularização é que a quantidade de variáveis também tende a reduzir. Isso é um ganho muito importante na qualidade do seu código. Note que reduzimos de dez variáveis no primeiro exemplo para sete no segundo.


Implementando o algoritmo modularizado em Java

package verificamaiormenor;

import javax.swing.JOptionPane; // Importando pacote de extenção Java API
/**
*
* @author GMottaZn
*/
public class Main {
// Atributos privados
private double num3Zn;
private double num2Zn;
private double num1Zn;
private String znMsgSaida;

public double getNum3Zn() {
return num3Zn;
}

public void setNum3Zn(double num3Zn) {
this.num3Zn = num3Zn;
}

public double getNum2Zn() {
return num2Zn;
}

public void setNum2Zn(double num2Zn) {
this.num2Zn = num2Zn;
}

public double getNum1Zn() {
return num1Zn;
}

public void setNum1Zn(double num1Zn) {
this.num1Zn = num1Zn;
}

public String getZnMsgSaida() {
return znMsgSaida;
}

public void setZnMsgSaida(String znMsgSaida) {
this.znMsgSaida = znMsgSaida;
}

// Metodos privados
private String encontraMaior() {
double znMaior;
if ((getNum1Zn() > getNum2Zn()) && (getNum1Zn() > getNum3Zn())) {
znMaior = getNum1Zn();
}
else {
if ((getNum2Zn() > getNum1Zn()) && (getNum2Zn() > getNum3Zn())) {
znMaior = getNum2Zn();

}
else {
znMaior = getNum3Zn();
}
}
return "O Maior Número é: " + znMaior;
}

private String encontraMenor() {
double znMenor;
if ((getNum1Zn() < getNum2Zn()) && (getNum1Zn() < getNum3Zn())) {
znMenor = getNum1Zn();
}
else {
if ((getNum2Zn() < getNum1Zn()) && (getNum2Zn()< getNum3Zn())) {
znMenor = getNum2Zn();

}
else {
znMenor = getNum3Zn();
}
}
return "O Menor Número é: " + znMenor;
}

private boolean saoIguais(){
return (getNum1Zn() == getNum2Zn()) && (getNum1Zn() == getNum3Zn());
}

// Método Público
public void executaVerificacao(){
String saidaMaior, saidaMenor;
if (saoIguais()){
saidaMaior = "Os números são iguais: " + getNum1Zn();
setZnMsgSaida(saidaMaior);
}
else{
saidaMaior = encontraMaior();
saidaMenor = encontraMenor();
setZnMsgSaida(saidaMaior + " | " + saidaMenor);
}

}

public static void main(String[] args) {
String strNum1Zn, strNum2Zn, strNum3Zn, msgSaida;

// Entrada de dados ...
strNum1Zn = JOptionPane.showInputDialog("Digite o primeiro zn número");
strNum2Zn = JOptionPane.showInputDialog("Digite o segundo zn número");
strNum3Zn = JOptionPane.showInputDialog("Digite o terceiro zn número");
/**
* Instanciando um objeto cujo o tipo é a classe "Main".
* O construtor "Main()" retorna um referencia de memória para a variável "MyZnClass"
*/
Main MyZnClass = new Main();

// Atribuindo valor aos atributos
MyZnClass.setNum1Zn(Double.parseDouble(strNum1Zn));
MyZnClass.setNum2Zn(Double.parseDouble(strNum2Zn));
MyZnClass.setNum3Zn(Double.parseDouble(strNum3Zn));
//PROCESSA a verificação para encontrar o maior e o menor valor
MyZnClass.executaVerificacao();

// Saída de dados
JOptionPane.showMessageDialog(null, " Estação Zn - " +
MyZnClass.getZnMsgSaida(), "www.estacaozn.blogspot.com",
JOptionPane.INFORMATION_MESSAGE);

// Finaliza programa
System.exit(0);


}
}

Este tipo modularização, em linguagens como o Java apresnta pouca melhoria em termos visuais como, por exemplo, comparado ao Delphi. Isso se olharmos o todo do programa. Entretanto, se considerarmos apenas o programa principal, que se inicia na linha 101 (public static void main(String[] args)) é nítido o ganho na qualidade do código.

Chegamos a um nível de complexidade mais avançado do tema proposto. Caminhamos progressivamente no sentido de fornecer conteúdo pratico e teórico(veja sub-rotinas e O princípio da caixa preta) para fortalecer o ponto de vista que estou tentando apresentar. Vamos aumentar o grau de modularização do nosso simplório algoritmo usando a orientação a objetos.


Implementando o algoritmo modularizado em Delphi
primeiro codificaremos a classe que encontrará o maior e o menor valor...


unit DeterminaMaiorMenorZN;

interface

uses
classes, SysUtils;

(*********************************************************************
* Nesta unit definimos uma Classe que possui um comportamento capaz *
* de determinar qual o maior e o menor valor entre três valores *
* atribuidos a ela. (GMttaZn - 07/2008) *
********************************************************************)

type
TDeterminaMaiorMenorZN = Class(TComponent)
private
(* Atributos Privados *)
FNum3Zn: Double;
FNum1Zn: Double;
FNum2Zn: Double;
FZnMsgSaida: String;
(* métodos setters *)
procedure SetNum1Zn(const Value: Double);
procedure SetNum2Zn(const Value: Double);
procedure SetNum3Zn(const Value: Double);
procedure SetZnMsgSaida(const Value: String);

(* Metodos privados *)
function EncontraMaior: Double;
function EncontraMenor: Double;
function SaoIguais: boolean;
public
procedure ExecutaVerificacao;
(* Propriedades - são os getters e setters em Delphi *)
property Num3Zn: Double read FNum3Zn write Setnum3Zn;
property Num2Zn: Double read FNum2Zn write Setnum2Zn;
property Num1Zn: Double read FNum1Zn write Setnum1Zn;
property ZnMsgSaida: String read FZnMsgSaida write SetznMsgSaida;
end;
implementation

const
// Definindo constantes
MSG_MAIOR_ZN = 'Maior Número = %f';
MSG_MENOR_ZN = 'Menor Número = %f';
MSG_IGUAIS_ZN = 'São Iguais = %f';

{ TDeterminaMaiorMenorZN }

(**********************************************************
* Inmplementa um algoritmo para determinar o maior entre *
* três valores - http://estacaozn.blogspot.com *
**********************************************************)
function TDeterminaMaiorMenorZN.EncontraMaior: Double;
begin
if ((FNum1Zn > FNum2Zn) and (FNum1Zn > FNum3Zn)) then
Result := Num1Zn
else if ((FNum2Zn > FNum1Zn) and (FNum2Zn > FNum3Zn)) then
Result := FNum2Zn
else
Result := FNum3Zn;
end;

(**********************************************************
* Inmplementa um algoritmo para determinar o menor entre *
* três valores - http://estacaozn.blogspot.com *
**********************************************************)
function TDeterminaMaiorMenorZN.EncontraMenor: Double;
begin
if ((FNum1Zn < FNum2Zn) and (FNum1Zn < FNum3Zn)) then
Result := Num1Zn
else if ((FNum2Zn < FNum1Zn) and (FNum2Zn < FNum3Zn)) then
Result := FNum2Zn
else
Result := FNum3Zn;
end;

(*****************************************************************************
* Evoca os métodos que determinam o maior e o menor valor dos atributos *
* da classe. Testando, primeiramente se os valores não são iguais. Desta *
* forma então, definido um valor adequado para a saída. *
* - http://estacaozn.blogspot.com *
*****************************************************************************)
procedure TDeterminaMaiorMenorZN.ExecutaVerificacao;
var
SaidaMaior, SaidaMenor: String ;
begin
if (SaoIguais) then
SetZnMsgSaida(Format(MSG_IGUAIS_ZN, [Num1Zn]))
else
begin
SaidaMaior := Format(MSG_MAIOR_ZN, [EncontraMaior]);
SaidaMenor := Format(MSG_MENOR_ZN, [EncontraMenor]);
SetZnMsgSaida(SaidaMaior + ' | ' + SaidaMenor);
end;
end;

(* Determina se o valor dos atributos são guais - http://estacaozn.blogspot.com*)
function TDeterminaMaiorMenorZN.SaoIguais: boolean;
begin
Result := (Num1Zn = Num2Zn) and (Num1Zn = Num3Zn);
end;

procedure TDeterminaMaiorMenorZN.Setnum1Zn(const Value: double);
begin
FNum1Zn := Value;
end;

procedure TDeterminaMaiorMenorZN.Setnum2Zn(const Value: double);
begin
FNum2Zn := Value;
end;

procedure TDeterminaMaiorMenorZN.Setnum3Zn(const Value: double);
begin
FNum3Zn := Value;
end;

procedure TDeterminaMaiorMenorZN.SetznMsgSaida(const Value: String);
begin
FZnMsgSaida := Value;
end;

end.

Program principal - no contexto da nossa modularização


procedure TForm1.Button3Click(Sender: TObject);
var
MyDeterminaMaiorMenorZn: TDeterminaMaiorMenorZN;
StrNum1Zn, StrNum2Zn, StrNum3Zn, MsgSaida: String;
begin
// Entrada de dados
StrNum1Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');
StrNum2Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');
StrNum3Zn := InputBox('www.estacaozn.blogspot.com | Encontra o Maior Valor',
'Digite o número', '');

// Processamento
MyDeterminaMaiorMenorZn := TDeterminaMaiorMenorZN.Create(Self);
try
MyDeterminaMaiorMenorZn.Num1Zn := StrToFloat(StrNum1Zn);// convertendo de string para Double
MyDeterminaMaiorMenorZn.Num2Zn := StrToFloat(StrNum2Zn);// convertendo de string para Double
MyDeterminaMaiorMenorZn.Num3Zn := StrToFloat(StrNum3Zn);// convertendo de string para Double

MyDeterminaMaiorMenorZn.ExecutaVerificacao;

(*Saída de dados*)
Self.Repaint;
//Self.Canvas.Refresh;
Self.Canvas.TextOut(250, 90, 'www.estacaozn.blogspot.com');
Self.Canvas.TextOut(250, 110, MyDeterminaMaiormenorZn.ZnMsgSaida);
finally
MyDeterminaMaiorMenorZn.Free;
end;
end;

Note no exemplo acima que a quantidade de varaáveis foi reduzido ainda mais.

Implementando o algoritmo modularizado em Java
Primeiro a classe responsável por determinar o maior e o menor valor


package modularizandoverificamaiormenoroo;

/**
*
* @author GMottaZN
* www.estacaozn.blogspot.com
* Este exemplo foi construido com objetivo de demostrar
* as vantagens de mudularizar um programa. Aplicamos uma abordagem OO
* para alcançar este objetivo.
*/
public class DeterminaMaiorMenorZN {

// Atributos privados
private double num3Zn;
private double num2Zn;
private double num1Zn;
private String znMsgSaida;

// Definindo constantes
final static String MSG_MAIOR_ZN = "Maior Número = ";
final static String MSG_MENOR_ZN = "Menor Número = ";
final static String MSG_IGUAIS_ZN = "São Iguais = ";

// getters e setters
public double getNum3Zn() {
return num3Zn;
}

public void setNum3Zn(double num3Zn) {
this.num3Zn = num3Zn;
}

public double getNum2Zn() {
return num2Zn;
}

public void setNum2Zn(double num2Zn) {
this.num2Zn = num2Zn;
}

public double getNum1Zn() {
return num1Zn;
}

public void setNum1Zn(double num1Zn) {
this.num1Zn = num1Zn;
}

public String getZnMsgSaida() {
return znMsgSaida;
}

public void setZnMsgSaida(String znMsgSaida) {
this.znMsgSaida = znMsgSaida;

}

// Metodos privados
private double encontraMaior() {
/**
* Inmplementa um algoritmo para determinar o maior entre
* três valores - http://estacaozn.blogspot.com
*/
double znMaior;
if ((getNum1Zn() > getNum2Zn()) && (getNum1Zn() > getNum3Zn())) {
znMaior = getNum1Zn();
}
else {
if ((getNum2Zn() > getNum1Zn()) && (getNum2Zn() > getNum3Zn())) {
znMaior = getNum2Zn();

}
else {
znMaior = getNum3Zn();
}
}
return znMaior;
}

private double encontraMenor() {
/**
* Inmplementa um algoritmo para determinar o menor entre
* três valores - http://estacaozn.blogspot.com
*/
double znMenor;
if ((getNum1Zn() < getNum2Zn()) && (getNum1Zn() < getNum3Zn())) {
znMenor = getNum1Zn();
}
else {
if ((getNum2Zn() < getNum1Zn()) && (getNum2Zn()< getNum3Zn())) {
znMenor = getNum2Zn();

}
else {
znMenor = getNum3Zn();
}
}
return znMenor;
}

private boolean saoIguais(){
/**
* Verifica se os valores atribuidos aos atributos da classe
* são iguais - http://estacaozn.blogspot.com
*/
return (getNum1Zn() == getNum2Zn()) && (getNum1Zn() == getNum3Zn());
}

// Método Público
public void executaVerificacao(){
String saidaMaior, saidaMenor;

if (saoIguais()){
setZnMsgSaida(MSG_IGUAIS_ZN + getNum1Zn());
}
else{
saidaMaior = MSG_MAIOR_ZN + encontraMaior();
saidaMenor = MSG_MENOR_ZN + encontraMenor();
setZnMsgSaida(saidaMaior + " | " + saidaMenor);
}

}

}



Programa principal Java


package modularizandoverificamaiormenoroo;

import javax.swing.JOptionPane;
/**
*
* @author Gerson
*/
public class Main {
private static DeterminaMaiorMenorZN MyDeterminaMaiormenorZn;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String strNum1Zn, strNum2Zn, strNum3Zn, msgSaida;

// Entrada de dados ...
strNum1Zn = JOptionPane.showInputDialog("Digite o primeiro zn número");
strNum2Zn = JOptionPane.showInputDialog("Digite o segundo zn número");
strNum3Zn = JOptionPane.showInputDialog("Digite o terceiro zn número");
/**
* Instanciando um objeto cujo o tipo é a classe "Main".
* O construtor "Main()" retorna um referencia de memória
* para a variável "MyDeterminaMaiormenorZn"
*/
MyDeterminaMaiormenorZn = new DeterminaMaiorMenorZN();

// Atribuindo valor aos atributos
MyDeterminaMaiormenorZn.setNum1Zn(Double.parseDouble(strNum1Zn));
MyDeterminaMaiormenorZn.setNum2Zn(Double.parseDouble(strNum2Zn));
MyDeterminaMaiormenorZn.setNum3Zn(Double.parseDouble(strNum3Zn));
//PROCESSA a verificação para encontrar o maior e o menor valor
MyDeterminaMaiormenorZn.executaVerificacao();

// Saída de dados
JOptionPane.showMessageDialog(null, " Estação Zn - " +
MyDeterminaMaiormenorZn.getZnMsgSaida(), "www.estacaozn.blogspot.com",
JOptionPane.INFORMATION_MESSAGE);

// Finaliza programa
System.exit(0);
}

}


Agora sim podemos dizer que temos um programa em Java. Veja que na "public class Main {", aquele aspecto de código macarrônico sumiu. Logo, estamos mais perto da luz!


A seguir, veja abaixo, através da UML, como as classes codificadas se relacionam. Usei o Jude para elaborar um diagrama de classes que modelasse o algoritmo.


Na ilustração abaixo adaptei para o contexto Delphi o diagrama de Classes



Mas qual foi a causa da terceira modularização? Será que você conseguiu perceber? Está última modularização foi a que alcançou de fato o grau máximo de funcionalidade atômica para cada sub-rotina. No exemplo anterior, as sub-rotinas “EncontraMenor” e “EncontraMaior” além de determinarem o maior e o menor, cada uma respectivamente, elas no final ainda ficaram encarregadas de fornecer o output para o usuário. Elas convertem o valor e possuem como saída a mensagem pro usuário. Isso não é legal, cada uma delas está acumulando funções diferentes e esse tipo de deslize lógico é que acaba sendo o tempero das macarronadas mais substanciosas e pesadas. Então, recodificamos cada uma das sub-rotinas para que elas possuam apenas uma funcionalidade, apenas uma responsabilidade. E transferimos para o que chamamos de programa principal a responsabilidade de comunicar ao usuário o resultado. Assim estamos mais perto da Luz!!!


Conclusões

O primeiro objetivo que devemos desejar alcançar quando fazemos uma mudularização é desenvolver um módulo com uma funcionalidade única irredutível, atômica. Quando você consegue isso está mais perto da luz, ponto pra vc.

O segundo objetivo é tornar esse módulo, que possui uma, apenas uma, funcionalidade única irredutível, o mais coeso possível e menos acoplado possível. Ou seja, conseguir fazer dele uma caixa preta. Quando você consegue isso está mais perto da luz, ponto pra vc.

O terceiro objetivo é conseguir o reaproveitamento da sub-rotina. Esse objetivo é ocupa um plano secundário em relação aos dois primeiros. O reaproveitamento de um módulo só traz benefícios efetivamente quando ele conseguiu antes atingir os dois anteriormente descritos. Do contrário ele será prejudicial, visto que o acoplamento gerado pelo reaproveitamento tornará seu sistema sensível a mudanças. Conseqüentemente, o processo de manutenção tanto evolutiva, quanto corretiva, será extremamente custoso. Em muitos casos tornando-se até mesmo inviável. Portanto, a argumentação, que defende a idéia de que a modularização não deve ser feita quando o reaproveitamento não será necessário, não se sustenta.

Concluindo, numa análise final, esse artigo também serviu para uma demonstração e entendimento da relação existente entre uma implementação OO e uma implementação estruturada. Aliais, a relação entre esses dois paradigmas de programação tem ganhado uma atenção especial aqui no estação ZN. Você pode ter certeza que isso não se deve ao acaso. Neste artigo podemos ver que não existe uma dualidade antagonistica entre esses paradigmas. Na minha opinião, não existe OO sem estruturação. Ou seja, um programador ou analista só fará bem OO se ele tiver muito bem consolidados os conceitos da estruturação. A conveniência de usar um ou outro vai depender de algumas variáveis, como por exemplo: A tecnologia que você estiver usando, a metodologia de engenharia adotada no projeto, ou a formação e cultura da equipe. Por exemplo, se você pretende conjugar estruturação e UML acho que encontrará alguma dificuldade para harmonizar o processo de analise com o de desenvolvimento. Presumo ... hehehhe!


Artigo completo (View Full Post)

domingo, 15 de junho de 2008

Identificadores e Palavras Reservadas - Java

Identificadores e Palavras Reservadas

Identificadores é o que usamos para nomear variáveis, instâncias de classe (que também são variáveis), constantes, métodos e propriedades. Em outras palavras, identificador é tudo que não seja um literal, nem símbolos ou números, nem palavras reservadas. Existem regras que determinam como devem ser declarados os identificadores permitidos em Java (igualmente como em qualquer outra linguagem de programação. Pelo menos, até os dias de hoje). Além dessas regras de sintaxe, a Sun define uma convenção para nomeação de métodos, variáveis e classes. Semelhantemente as demais linguagens de programação, em Java existe um conjunto de palavras reservadas para a linguagem. Isso implica que não é possível usá-las para nomearmos identificadores. Mais tarde falarei detalhadamente sobre a convenção para criação de identificadores para métodos, propriedades e classes em Java.

Obs: Ok, anteriormente escrevi sobre a mesma coisa com uma outra abordagem acho que se vc ainda tem alguma dúvida sobre esse tema pode encontrar ....

Cabe destacar que a adoção de uma padronização quanto a codificação é comum em muitas linguagens de programação, falaremos sobre isso mais tarde.

Regras para nomeação de Identificadores



A sintaxe da linguagem Java define as seguintes regras para se declarar identificadores:

Os identificadores devem começar com uma letra, ou cifrão ($), ou Underscore ( _ ). Não é permitido caracteres numéricos para iniciar um identificador.

Palavras reservadas não podem ser usadas como identificadores.

A linguagem Java é case sensitive (sensível a caixa) para identificadores. Exemplo: Cliente e cliente são, para o compilador, identificadores diferentes.

Não há limite para o número de caracteres usados para compor um identificador.

Exemplo de identificadores grafados de forma correta:

int _a;
String $Coisas;
double _$;
double um_Exemplo_de_nome_bastanteZn_Prolixo_para_um_identificadorZn;

Em seguida veja exemplos de identificadores declarados de forma não aceitável para o compilador.

int :b;
int -di
int e#;
int .f;
int 79;


Exemplo de um programa em Java para testarmos as regras sobre identificadores citadas:
Primeiro veremos um programa “dummy” cujo os identificadores criaremos de forma convencional.


import javax.swing.JOptionPane;
/**
*
* @author Gerson
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String znSaida = "";
int EstacaoZn = 2;

for (int i = 1; i <= 10; i++){ if (i == 5) continue; znSaida += i + " "; } znSaida += "\n bleng Zn! Skip --> 5";
JOptionPane.showMessageDialog(null, znSaida);
System.exit(0);
}

}



Segundo veremos o mesmo programa “dummy”, contudo alteraremos os nomes dos identificadores para as situações específicas definidas pelo Java 5.

Criando um novo projeto no NetBeans





Definindo o diretório onde o projeto seja salvo





/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package exemploidentificadoreszn2;

import javax.swing.JOptionPane;
/**
*
* @author GMottaZn
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String _znSaida = "";
int $EstacaoZn = 2;
String um_Exemplo_de_nome_bastanteZn_Prolixo_para_um_identificadorZn;
for (int i = 1; i <= 10; i++){ if (i == 5) { $EstacaoZn = (4 + $EstacaoZn); continue; } else{ $EstacaoZn = (10 + $EstacaoZn); um_Exemplo_de_nome_bastanteZn_Prolixo_para_um_identificadorZn = "Estaçao ZN = "; } ; _znSaida += i + " " + um_Exemplo_de_nome_bastanteZn_Prolixo_para_um_identificadorZn; } _znSaida += "\n bleng Zn! Skip --> 5 - $EstacaoZn = " + $EstacaoZn;
JOptionPane.showMessageDialog(null, _znSaida);
System.exit(0);
}

}




Artigo completo (View Full Post)

Abordagem OO Java - Introdução

Recorrentemente temos abordado POO aqui no blog. Nosso foco maior tem sido Delphi e C#, contudo sempre que podemos procuramos ampliar nossos exemplos experimentando outras linguagens. Vamos falar um pouco desse contexto no mundo Java.

Breve revisão sobre Java

Um programa em Java é sempre uma coleção de objetos comunicando-se com outros. Comunicação essa que acontece mediante a chamada de métodos (evocação de métodos) uns dos outros. Nunca esquecendo, de ter sempre em mente que cada objeto é de um determinado tipo, definido por uma classe ou interface. Não desprezado o fato dessa coleção de objetos, a qual mencionei anteriormente, ser quase sempre composta por objetos de uma variedade diferente de tipos. Vejamos as seguintes definições sobre OO (Estou usando como referência para este artigo o livro da Kathy Sierra, certificação Sun (Java 5), primeiro capítulo):


Classe:
Um modelo que descreve os tipos, de estado e de comportamento, que os objetos do seu tipo podem ter.

Permita-me um comentário: Particularmente, eu acho essa definição satisfatória para quem já conhece bem orientação a objetos. Contudo, para quem esta aprendendo programação ou vem de uma cultura de programação estruturada, duvido que o parágrafo anterior agregue algum valor, no que tange o processo cognitivo de quem o está lendo. Em outros artigos eu e o Felipe usamos outras abordagens para este conceito. Sugiro que você complemente o entendimento lendo as demais definições, principalmente se você ainda esta dando os primeiros passos em OO.

Objeto:
No momento da execução do programa, quando a JVM (Java Virtual Machine) encontra a palavra reservada “new” ela cria uma instância da classe, referenciada no trecho de código corrente. A isso chamamos de criar um objeto (em outras palavras: instanciar um objeto). Esse objeto recém criado terá seu próprio estado, bem como um comportamento. O qual será expressado como reflexo de todas as funcionalidades, definidas por métodos na classe, que são dinamicamente acessíveis ao objeto.
Quem são essas classes acessíveis ao objeto??? Isso se refere a herança. Um objeto cujo tipo é, por exemplo, uma classe “X” terá acesso ao comportamento de todos os ancestrais dela. Ou seja, ele poderá se comportar como a classe “X”, a classe “XPai”, bem como a classe “XAvo” e assim sucessivamente até o ancestral mais alto (o qual chamamos de super classe). Falaremos mais sobre herança adiante.

Estado:
Variáveis de instância – Cada objeto (que por sua vez é uma instância de uma classe) terá seu conjunto único de variáveis de instância conforme definido na classe (definidos pelos atributos da classe). Em contra partida, coletivamente os valores atribuídos as variáveis de instância de um objeto, expressam o estado do mesmo. Ou, os valores persistidos pelas propriedades do objeto, expressam o estado do mesmo.
Ou seja, o conjunto de atributos do objeto definirá, a partir dos valores definidos neles, o estado desse objeto. Por exemplo: Suponha que temos um objeto “Lâmpada”, esse objeto é possuidor de um conjunto de atributos: Potência, Voltagem, Cor e etc. Devemos considerar que este objeto, além desses, possua outro atributo que definirá se “Lâmpada” esta acesa ou não. Esse atributo pode se chamar “Ativa”, ele indica se ela está acessa (ou seja, ativa) ou não. Logo, quando todos esses atributos possuírem valor, exemplo: Lâmpada.Cor = Amarela, Lâmpada.Voltagem = 110, Lâmpada.Potência = 100W e Lâmpada.Activa = Verdadeiro. Posso agora então conhecer o estado desse objeto.

Comportamento:
Quando codificamos uma classe, e implementamos os métodos dela, estamos definido a lógica da classe. “É nos métodos que a lógica da classe é armazenada” (Achei essa definição muito apropriada). Através dos métodos, o verdadeiro trabalho da classe é realizado. Ou seja, a razão de ser da classe, onde seu principal objetivo é definido. Neles e por meio deles, algoritmos são executados e os dados são manipulados.

Herança:
Característica da orientação a objetos que permite o reaproveitamento do código de uma classe por outra. Em Java é possível definir uma superclasse “genérica” (mais abstrata) e em seguida estendê-la criando outras classes mais específicas. A superclasse não tem conhecimento algum sobre as classes que descendem dela. Em contra partida, essas classes herdeiras precisam declarar explicitamente a relação de herança. Como efeito disso, uma subclasse que herda de uma superclasse “recebe automaticamente”, digamos assim, as variáveis de instância acessíveis, bem como os métodos definidos na superclasse. Além disso possui autonomia para redefinir os métodos herdados da superclasse, a fim de expressar um comportamento mais específico (este fenômeno chamamos de sobrescrita de método. Ou seja uma subclasse pode sobrescrever os métodos herdados).

Identificando Classes (Análise)
Em concordância com o que mencionei anteriormente, é extremamente importante ser obstinado em alcançar um grau de coesão alto na classe que você está definindo. Isso implica diretamente que toda classe deve ter muito bem definido o conjunto de responsabilidades pertinentes ao conceito do mundo real o qual ela representará para um sistema. Por exemplo, imagine um programa cujo objetivo principal é simular uma orquestra: Certamente encontraremos a representação de instrumentos musicais por algumas classes, bem como músicos, partituras e composição musical por outras tantas. Concluir isso é bastante elementar. Contudo, um erro fatal (e na minha opinião muito freqüente) seria definir, em virtude de um equívoco de análise, o comportamento (ou parte dele) de uma classe em outra, ou vice-versa. Por exemplo: A classe instrumentista possuir um método que retorne o compasso corrente. Creeeeeddddooooo!??!?!?!? Visto que, compasso é um atributo da classe partitura. Um programa em Java é constituído tanto por classes construídas pelo programador, quanto por e outras que não (por exemplo, classes Java API). Em Java as classes são organizadas em pacotes, os quais devemos referenciar mediante a palavra reservada “import” a fim de usarmos suas classes em nossos programas. Dessa forma a linguagem fornece uma forma consistente do desenvolvedor nomear, bem como obter acesso, as classes que necessitar. No exame de certificação Java a avaliação aborda muitos dos conceitos relacionados aos pacotes e acesso a classes. Posteriormente detalharemos mais esses temas.

Interfaces:
É uma espécie de superclasse cem por cento abstrata que define os métodos que uma subclasse deve suportar, mas não define como esse método deve se comportar (ou seja, não define come deve ser implementado). Exemplificando, uma interface "Instrumento Musical" pode declarar que todas as as classes de implementação de "Instrumento Musical" (estendidas dela) possuirão um método "reproduzirSom()". Contudo, a interface "Instrumento Musical" não fornece nenhuma lógica para para este método. Isso implica que a classe que implementar "Instrumento Musical" terá a responsabilidade de definir o comportamento dos métodos definidos na interface. Determinando então, desta forma, como cada instrumento irá se comportar particularmente.
Venho sentindo a necessidade de escrever sobre interface não é de hoje, entretanto ainda não consegui preparar um material que me agrade. Esse assunto esta na fila e considero extremamente pertinente.

Artigo completo (View Full Post)

quinta-feira, 24 de abril de 2008

Configuração de um DataSet de para Select, Insert, Update e Delete- CRUD - For Dummies

Num cadastro padrão (CRUD), como configurar os DataSets de Manipulação de dados:

1 - Adicione o DataSet ao DataModule.

2 - Nomeie o DataSet. Exemplo: AdsClientes.

3 - Associe o DataSet ao conector do banco de dados: Através da propriedade “Connection” (Caso esteja trabalhado com ADO), ou “SQLConnection” (caso trabalhe com DBExpress), ou “DataBase” (caso a tecnologia de acesso a dados seja DBE ou IBExpress).

4 - Defina o comando SQL genérico na propriedade específica para isso:
  • TADODataSet, TSQLDataSet – propriedade “CommandText”.
  • TQuery, TADOQuery, TIBQuery – propriedade “SQL”.

Obs: TQuery deve trabalhar associado ao TUpdateSQL (o TIBQuery, igualmente com o seu correlato). Isso porque, eles não possuem a capacidade de gerar sozinhos os comandos de “insert”, “Update”, “Delete”.

O Comando SQL padrão para um dataset que fará inclusão, alteração e deleção deve ser:

  • Selecionar todas as colunas da tabela (a qual se deseja construir o cadastro). Filtrando sempre pela chave primária usando para isso o operador relaciona de igualdade “=”. Exemplo genérico:

SELECT
*
FROM
[Nome da Tabela]
WHERE
[“ColunaPK”] = :parametro



Exemplo específico:


SELECT
*
FROM
Clientes
WHERE
ClienteID = :pID


OBS: O símbolo : determina um parâmetro, o qual, no programa Delphi, receberá dinamicamente valor (muitas vezes da interface) , para então executar comando SQL retornando para a aplicação os dados desejados.

5 - Configurar o tipo do parâmetro na propriedade “Params”, ou “Parameters” caso trabalhe com ADO.

6 - Adicionar os campos persistentes: Botão direito sobre o dataset, no Fields Editor, Add all fields

7 - Configurar as propriedades “DisplayLabel”, “DisplayFormat” ou “EditMasck” caso deseje formatar o dado para exibí-lo. Exemplo:
AdsClientesConsDataCadastro.DiplayLabel = ‘Data de Cadastro’;
AdsClientesConsDataCadastro.DiplayFormat = ‘DD/MM/YYYY’;
AdsClientesConsValorComprado.DiplayLabel = ‘Valor Total em Compras:’;
AdsClientesConsValorComprado.DiplayFormat = #,##0.00;

OBS1: Esses DataSets configurados para inclusão, alteração e deleção não são propícios para utilização de “JOINS” (uniões entre tabelas). Visto que, o resultset retornado por este tipo de select não é atualizável. Contudo, através da utilização da tecnologia MIDAS, é possível fazer com que dados possam ser atualizados a partir dessa abordagem. Todavia, isso requer o emprego de uma técnica específica, a qual emprega uma configuração apropriada no TDataSetProvider para este fim. Veremos sobre está técnica adiante.

OBS2: No cado dos componentes da ADO altere a propriedade “LockType” para “ltBatchOptimistic”. Para habiltá-lo a trabalhar com Cache.
“Use the LockType ltBatchOptimistic to open a dataset in batch update mode.”

8 - Valores default deverão ser atribuídos no evento “OnNewRecord” do DataSet, caso seja sua opção fazer isso pelo front-end. Exemplo:


procedure TDM.AdsClientesNewRecord(DataSet: TDataSet);
begin
AdsClientesID_Cliente.AsInteger := GeraID(AdsClientesID_Cliente.FieldName);
AdsClientesStatus.AsString := 'S';
AdsClientesTipo.AsString := 'F';
AdsClientesData.AsDateTime := Now;
end;


Considerações finais:

Fiz referência, neste post a classe “TDataSet” que é a classe da qual derivam todos os objetos de seleção e manipulação de dados da VCL (biblioteca de componentes visuais, do Delphi), porque meu intuito é apresentar uma forma padronizada, genérica, para configuração de componentes usados em programas de cadastro. Ou seja se você procurar na palheta decomponentes por “DataSet” não vai encontrar nada. Uma questão quanto a isso seria que, nem todos os componentes que derivam de TDataSet são adequados para as configurações listadas por mim neste exemplo. Mas, apesar disso, os DataSets aos quais me refiro aqui são adequados para a funcionalidade de cadastro.

Outra coisa importante a destacar é que este exemplo não aborda uma metodologia definitiva, completa para todos os casos de CRUD. Meu objetivo é mostrar a importância de se definir um procedimento padrão, através de um exemplo, mais próximo da realidade que um desenvolvedor encontra no dia-a-dia, que serve como ponto de partida didaticamente formulado.

Artigo completo (View Full Post)

sábado, 15 de março de 2008

Algoritmo III – Um passo além da teoria

Prosseguindo com a série sobre Introdução a Programação, especificamente ao tema Algoritmos, vamos dar mais um passo no sentido de aprofundarmos mais nesse assunto.


Usando estrutura de decisão:

Segue um Algoritmo que lê o nome e as 4 notas bimestrais de um aluno. Em seguida o Algoritmo calcula e escreve a média obtida pelo aluno escrevendo também se o aluno foi aprovado ou reprovado.


Programa MEDIA_FINAL;
Constantes
MediaAprovacaoZN = 6;
Variaveis
NOTA1Zn, NOTA2Zn, NOTA3Zn, NOTA4Zn, MEDIAZn: REAL;
NOMEZn : CARACTERE [35]
INICIO
LER (NOMEZn);
LER (NOTA1Zn, NOTA2Zn, NOTA3Zn, NOTA4Zn);
MEDIAZn := (NOTA1Zn + NOTA2Zn + NOTA3Zn + NOTA4Zn) / 4;
SE MEDIAZn (>= 6) ENTÃO
ESCREVER (‘APROVADO’)
SENÃO
ESCREVER (‘REPROVADO’);

ESCREVER (NOMEZn, MEDIAZn)
FIM.


Desvio condicional aninhado:

Usados para tomadas de decisões para mais de 2 opções.

Exemplo:

SE <<CONDIÇÃO>> ENTÃO
inicio
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;
fim
SENÃO SE <<CONDIÇÃO>> ENTÃO
inicio
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;
fim
SENÃO
início
<<COMANDO 1>>;
<<COMANDO 2>>;
.
.
<<COMANDO N>>;

fim;


Implementação em PL/SQL:


create or replace procedure CalculaMedia(
Nota1 in out NUMBER,
Nota2 in NUMBER,
Nota3 in NUMBER,
Nota4 in NUMBER,
ValorAprovacao IN NUMBER,
Media out NUMBER,
ResultadoAprovacao OUT Varchar2) is
begin
Media := (Nota1 + Nota2 + Nota3 + Nota4)/ 4;
IF (Media >= ValorAprovacao) THEN
ResultadoAprovacao := 'Aprovado';
ELSE
ResultadoAprovacao := 'Reprovado';
END IF;
end CalculaMedia;


PL/SQL é a linguagem de programação do Oracle. Para debuggar no Developer, no object browser, selecione a procedure criada, “CalculaMedia”, com o botão direito do mouse sobre ela selecione “Add debugger information”, em seguida, ainda no mesmo menu popup, selecione “Test”. Passe valores para os argumentos (parâmetros) e pressione F9, para ir para o próximo comando pressione “ctrl + n”.




Implementação em Javascript:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title> Estação ZN - Algoritmos III </title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type= "text/javascript">
var ZnNota1 = 0;
var ZnNota2 = 0;
var ZnNota3 = 0;
var ZnNota4 = 0;
var ZnMediaAprovacao = 6;
var Aprovacao, MediaCalculadaZn;

// Input de Dados
// Lendo o valor da primeira nota
ZnNota1 = window.prompt( "Digite a primeira Nota:", "0" );
// Lendo o valor da segunda nota
ZnNota2 = window.prompt( "Digite a Segunda Nota:", "0" );
//Lendo o valor da 3° nota
ZnNota3 = window.prompt( "Digite a Terceira Nota:", "0" );
//Lendo o valor da 4° nota
ZnNota4 = window.prompt( "Digite a Quarta Nota:", "0" );

<!-- Processamento - Calculando a Média | convertendo os valore para numérico -->
MediaCalculadaZn = (parseInt(ZnNota1) + parseInt(ZnNota2) +
parseInt(ZnNota3) + parseInt(ZnNota4)) / 4;
//MediaCalculadaZn = (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

alert("Calculo media = " + MediaCalculadaZn);

<!-- Verificando Aprovação do Aluno -->
if (MediaCalculadaZn >= ZnMediaAprovacao) {
Aprovacao = "Parabéns, aluno Aprovado!!!!!"}
else { Aprovacao = "Lamento, aluno Reprovado."};

alert(" Verificando Aprovação do Aluno = " + Aprovacao);
// Saída - Exibindo o resultado formatado numa tabela
document.writeln( "<h1> <center>Estação ZN - Resultado </center></h1>" );
document.writeln("<table border=2>" );
document.writeln("<table border =1 width =100%>" );
document.writeln( "<tr><td> Nota na 1° prova = " + ZnNota1 + "</td></tr>" );
document.writeln( "<td> Nota na 2° prova = " + ZnNota2 + "</td></tr>" );
document.writeln( "<td> Nota na 3° prova = " + ZnNota3 + "</td></tr>" );
document.writeln( "<td> Nota na 4° prova = " + ZnNota4 + "</td></tr>" );
document.writeln( "<tr><td> Média = " + MediaCalculadaZn + "</td></tr>" );
document.writeln( "<tr bgcolor=#000066> <td> <font face=Courier New, Courier, mono color=#FFFFFF> Resultado : " + Aprovacao + "</font> </td></tr>" );
document.writeln( "</table>" );

</script>
</head>
<body>

</body>
</html>




Inicie uma nova aplicação. Na seção implementation da página de código digite conforme ilustrado abaixo:


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm;
ZnNota1, ZnNota2, ZnNota3, ZnNota4, ZnMedia: Double;
ResultadoAprovacaoZN: string;

implementation

{$R *.dfm}
const
ValorAprovacao = 6;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

initialization
CalculaMediaZN;
end.

No exemplo acima, note que implementei de uma forma completamente diferente de outros programas que construímos no Estação Zn. Meu principal objetivo, além de mostrar como o algoritmo pode ser implementado, foi fazer com que uma pessoa iniciante no Delphi possa perceber alguns detalhes difíceis de explicar e de fazer entender. Vamos agora chamar a tenção para a relação entre o que é declarado na unit, de uma forma independente, e a unit como um todo. Por exemplo, existe uma tendência das pessoas interpretarem que a unit inteira é uma área de definição exclusiva ao form1. Isso não é verdade, o form é somente mais uma declaração dentre outras declarações possíveis numa unit. Observe, o form1 não conhece a procedure “CalculaMediaZN”. Tente fazer um teste, no evento “FormPaint” experimente chamar a execução dela. Veja o exemplo abaixo, a segunda linha da procedure “FormPaint”,você acha que vai compilar?




procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
CalculaMediaZN;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;


Você esta correto se respondeu sim. Mas, isso não prova que o que acabei de afirmar está errado. Só está compilando porque a “CalculaMediaZN” está declarada primeiro que o evento “FormPaint” na unit. Logo, basta você inverter a ordem dos procedimentos para perceber que o form1 não conhece a procedure que está declarada separadamente na unit. Tente compilar novamente para receber a mensagem “Undeclared identifier: ‘CalculaMediaZN’”. Exemplo:


procedure TForm1.FormPaint(Sender: TObject);
begin
CalculaMediaZN;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 102, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
.
.
.



Mesmo que você referencie a “unit1” ao chamar a procedure que calcula a média não adiantará, visto que o ela não está declarada na seção “interface”.




procedure TForm1.FormPaint(Sender: TObject);
begin
unit1.CalculaMediaZN;// Ainda assim não vai compilar
.
.


Estou forçando uma barra somente para ilustrar o que estou tentando explicar, mas não faz o menor sentido chamar a procedure “CalculaMediaZN” novamente. Finalizando argumentação, para a provar de uma vez por todas, declare na seção “interface”, em qualquer lugar antes da palavra reservada “implementation”, desde que não seja dentro do escopo da seção “type” onde a classe TForm1 esta declarada, codifique conforme exemplificado abaixo:



type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
.
.

Agora, codifique o corpo do porcedimento “DesenhaPoligono” na seção “implementation”.


procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

Bem, agora o Form1 ainda não conhece esse novo procedimento, contudo neste momento se você fizer referência a unit1 ele estará visível ao Form1. O compilador, desta forma, não irá reclamar. Segue o código completo da unit1 com as últimas alterações:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
.
.


Agora, codifique o corpo do porcedimento “DesenhaPoligono” na seção “implementation”.

procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

Prosseguindo, o Form1 ainda não conhece esse novo procedimento, contudo neste momento se você fizer referência a unit1 ele estará visível ao Form1. O compilador, desta forma, não irá reclamar. Segue o código completo da unit1 com as últimas alterações:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
(* Ao ter declarado o cabeçalho deste procedimento na interface ele passou
a estar visível não só para toda unit, mas também para outras que a referenciarem.*)
procedure DesenhaPoligono;

var
Form1: TForm;
ZnNota1, ZnNota2, ZnNota3, ZnNota4, ZnMedia: Double;
ResultadoAprovacaoZN: string;

implementation

{$R *.dfm}
const
ValorAprovacao = 6;

procedure TForm1.FormPaint(Sender: TObject);
begin
unit1.DesenhaPoligono;
(* Sída de dados - Outpu*)
Form1.Canvas.TextOut(12, 12, Format('1° Nota: %f', [ZnNota1]));
Form1.Canvas.TextOut(12, 32, Format('2° Nota: %f', [ZnNota2]));
Form1.Canvas.TextOut(12, 52, Format('3° Nota: %f', [ZnNota3]));
Form1.Canvas.TextOut(12, 72, Format('4° Nota: %f', [ZnNota4]));
Form1.Canvas.TextOut(12, 92, Format('Média: %f', [ZnMedia]));

Form1.Canvas.TextOut(12, 112, '*******************************');

Form1.Canvas.TextOut(12, 132, Format('Resultado: %s', [ResultadoAprovacaoZN]));
end;

procedure CalculaMediaZN;
const
Aprovado = 'Parabéns!!! Aluno Aprovado.';
Reprovado = 'Lamento!!! Aluno Reprovado. Vai estudar e RAFM.';
begin
(* Entrada de dados - Input*)

{Preciso converter o valor retornado pela função "InputBox" de string para float}
ZnNota1 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 1° Nota do Aluno:', '0'));
ZnNota2 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 2° Nota do Aluno:', '0'));
ZnNota3 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 3° Nota do Aluno:', '0'));
ZnNota4 := StrToFloat(InputBox('Estação Zn | Algoritmo III',
'Digite a 4° Nota do Aluno:', '0'));
ZnMedia := (ZnNota1 + ZnNota2 + ZnNota3 + ZnNota4) / 4;

(* Processamento *)
if (ZnMedia >= unit1.ValorAprovacao) then
ResultadoAprovacaoZN := Aprovado
else
ResultadoAprovacaoZN := Reprovado;
end;

procedure DesenhaPoligono;
var
X, Y, DX, DY: Integer;
begin
Randomize;
X := Random(Form1.Width - 10);
Y := Random(Form1.Height - 250);
Randomize;
Form1.Canvas.Pen.Color := Random(65535);
Form1.Canvas.Pen.Width := Random(7);
DX := Random(400);
DY := Random(400);
Form1.Canvas.RoundRect(X, Y, X + DX, Y + DY, DX div 2, DY div 2);
end;

initialization
CalculaMediaZN;
end.


Execute o programa e teste.

Artigo completo (View Full Post)

 
BlogBlogs.Com.Br