□
M I C R O FO CUS
Programmazione Orientata agli Oggetti per
Sviluppatori COBOL
Introduzione
ii
Micro Focus The Lawn
22-30 Old Bath Road Newbury, Berkshire RG14 1QN UK
http://www.microfocus.com
Copyright © Micro Focus 1984-2013. All rights reserved.
MICRO FOCUS, the Micro Focus logo and Visual COBOL are trademarks or registered
trademarks of Micro Focus IP Development Limited or its subsidiaries or affiliated
companies in the United States, United Kingdom and other countries.
All other marks are the property of their respective owners.
2013-05-09
2 | An Introduction to Object-Oriented Programming for COBOL Developers
Contents
An Introduction to Object-Oriented Programming for COBOL Developers
....................................................................................................................... 4
Classes and Methods ......................................................................................................... 4
Objects ............................................................................................................................... 7
Creating an Instance of a Class .......................................................................................... 7
Constructors ....................................................................................................................... 9
Properties ........................................................................................................................... 9
Method Visibility ............................................................................................................... 10
Local Data ........................................................................................................................ 10
Data Types ....................................................................................................................... 11
Inheritance ....................................................................................................................... 12
Interfaces ......................................................................................................................... 15
Class Names .................................................................................................................... 17
Intrinsic Types .................................................................................................................. 17
The .NET and JVM Frameworks ....................................................................................... 18
Reflection ......................................................................................................................... 20
Calling COBOL From Other Languages ........................................................................... 20
What Next? ...................................................................................................................... 24
Contents | 3
Una Introduzione alla
Programmazione Object-Oriented
per Sviluppatori COBOL
Panoramica
Questa guida fornisce una introduzione di base alla Object-Oriented Programming (OOP) per sviluppatori
COBOL che usano Micro Focus Visual COBOL o Micro Focus Enterprise. Nella guida ci sono sezioni per
ogni concetto chiave della programmazione ad oggetti.
Il COBOL gestito è il termine per indicare il COBOL procedural per .NET COBOL e JVM COBOL con le
estensioni per avvalersi delle caratteristiche dei frameworks gestiti. Questo include la sintassi objectoriented (OO) che consente l’accesso a librerie contenenti una gran quantità di funzioni da usare nelle
applicazioni e molto altro. Per ottenere il massimo risultato del COBOL gestito, bisogna capire i concetti
della programmazione object-oriented.
Codice di Esempio
Questa guida contiene frammenti di codice semplice per illustrare alcuni concetti della programmazione
orientate agli oggetti. In COBOL. Durante la lettura puoi scrivere e compilare il codice ed usare il debug
del Visual COBOL o dell’Enterprise Developer.
Devi aver installato uno dei seguenti prodotti:
•
•
•
•
Micro Focus Visual COBOL per Visual Studio 2010 o per Visual Studio 2012
Micro Focus Visual COBOL per Eclipse per Windows
Micro Focus Enterprise Developer per Visual Studio 2010
Micro Focus Enterprise Developer per Eclipse
Nota: Visual COBOL e Enterprise Developer non possono essere installati sulla stessa macchina.
Per eseguire gli esempi, crea un progetto JVM COBOL in Eclipse o una applicazione console .NET
COBOL gestito in Visual Studio.
Classi e Metodi
Il cuore della Programmazione Object-Oriented Programming è la nozione di una classe. Una classe è
definite per incapsulare le informazioni di una particola re entità. Una classe contiene dati associate con
l’entità e le operazioni, chiamate metodi, che consentono l’accesso e la manipolazione dei dati. A parte
l’incapsulamento dei dati, le classis ono molto utili per convertire I programmi procedurali alle nuove
tecnologie di codifica gestita.
Questo è un semplice esempio di classe COBOL:
class-id MyClass.
method-id SayHello
static. linkage section.
01 your-name pic x(10).
procedure division using by value your-name.
display "hello " & your-name
end method.
end class.
4 | An Introduction to Object-Oriented Programming for COBOL Developers
Prima di guardare ai dettagli della classe, vediamo come puoi invocare il singolo metodo contenuto in
questa classe:
program-id.
TestMyClass. procedure
division.
invoke type MyClass::SayHello(by value Scot")
end program.
Nota: Per eseguire questo esempio in Visual Studio, hai bisogno di specificare uno Startup object
per la tua applicazione di console gestita. Seguire i passi:
1.
2.
3.
4.
5.
6.
7.
In Visual Studio, click-destro la soluzione in Solution Explorer.
Click Add > New Item, e click COBOL class.
Specifica un nome come MyClass.cbl, e click Add.
Allo stesso modo, aggiungi un programma COBOL con il nome TestMyClass.cbl.
Aggiungi I due pezzi di codice esempio rispettivamente alla classe e al programma.
Click Project > ProjectName Properties e click la linguetta Application.
Imposta Startup object a TestMyClass:
8. Click Debug > Start Without Debugging per eseguire il programma.
Come ti puoi aspettare, il risultato del programma è:
Hello Scot
In questo esempio puoi vedere come un programma procedural COBOL può anche usare la semantica objectoriented semantics anche se esso non è una classe.
Diamo un’occhiata ai dettagli della classe, class-id MyClass.
MyClass è il nome della classe. Quando referenzi una classe, devi specificare il nome, nello stesso modo di
quando referenzi un programma COBOL.
An Introduction to Object-Oriented Programming for COBOL Developers | 5
La nostra clesse non contiene dati, ma ha un metodo chiamato SayHello: method-id
SayHello static.
Notare che c’è una clausola static associate a questo metodo. Questa keyword è importante perchè ci
consente di chiamare il metodo senza dover creare una istanza della classe. Le istanze della classe sono
chiamate objects e li vedremo tra poco.
Metodi statici e dati statici possono essere utili al momento, ma valgono solo per una istanza dei dati. Metodi
statici possono operare soltanto su dati statici.
Il resto della dichiarazione del metodo dovrebbe essere familiar perchè identico al programma procedurale che
prende un singolo parametron come argomento per il programma:
linkage section.
01 your-name pic x(10).
procedure division using by value your-name.
display "hello " & your-name
end method.
Diamo un’occhiata al programma procedural che invoca questo metodo:
invoke type MyClass::SayHello(by value "Scot")
In merito al c dice, notiamo quanto segue:
•
•
La keyword invoke è sinonimo di CALL ma è usata nel contesto di una chiamata ad un metodo di una
classe.
La keyword type ci permette di specificare il nome della classe a cui ci riferiamo.
•
La sintassi :: ci permette di riferirci al metodo specifico della classe che vogliamo invocare.
Prima di approfondire, vediamo altri aspetti della sintassi:
invoke type MyClass::SayHello(by value "Scot")
La keyword type è una parte nuova del linguaggio COBOL introdotta con il Visual COBOL e semplifica il modo
di riferirsi ed invocare I metodi.
Per illustrare quanto detto. Di seguito c’è il programma equivalente conforme alla sintassi ISO:
program-id. TestMyClass repository.
class MyClass as "MyClass". procedure division.
invoke MyClass "SayHello" using by value "Scot"
end program.
L’ISO richiede l’uso della sezione Repository e le virgolette ai nomi dei metodi. In questo semplice esempio la
sintassi addizionale, sebbene verbose, non degrada segnificativamente la leggibilità del programma. Comunque
nella realtà, la sistassi addizionale insieme ad altri requisiti dell’ISO, diventa molto velocemente illeggibile e
rende il COBOL difficile da usare per applicazioni COBOL gestite.
Il Visual COBOL semplifica anche altri aspetti del linguaggio – Diamo un’occhiata ad un paio di casi nel
nostro esempio:
invoke type MyClass::SayHello(by value "Scot")
Può diventare:
invoke type MyClass::SayHello("Scot")
Se il metodo contiene ulteriori argomenti, questi possono apparire come:
invoke type MyClass::SayHello("Scot", 37, "Bristol Street")
Anche se le virgole che separano i parametri sono opzionali.
Nei prossimi esempi, useremo questa sintassi abbreviata.
Il metodo può essere semplificato anche come segue:
6 | An Introduction to Object-Oriented Programming for COBOL Developers
method-id SayHello static. linkage section.
01 your-name pic x(10).
procedure division using by value your-name.
display "hello " & yourname end method.
Può diventare:
method-id SayHello static.
procedure division using by value your-name as string.
display "hello " & yourname end method.
Due cose importanti sono cambiate in questo esempio:
•
•
La linkage section hesplicita è stata rimossa e l’argomento della linkage è stato definito inline con
l’istruzione procedure division using.
L’argomento della pic x(10) è stato sostituito da una referenza alla stringa.
String è un tipo COBOL predefinito che troviamo nelle classi stringa di JVM e .NET. Strings contiene
una varietà di metodi e sono usate per contenere dati Unicode di lunghezza arbitraria. Il compilatore può
convertire molti dei tipi predefiniti come stringhe in tipi COBOL come pic x – questo lo vedremo in
dettaglio più avanti.
Per i prossimi esempi, adottiamo questa convenzione di definizione degli argomenti inline. Questo è
possibile solo quando usiamo tipi predefiniti gestiti. I record COBOL devono essere definiti sempre nel
modo usuale.
Oggetti
Il nostro semplice esempio fin’ora ci ha aiutato a dimostrare i concetti base di una classe, ma il valore della
Programmazione Orientata agli Oggetti non è ancora del tutto evidente. La potenza della OOP vien fuori
quando incapsuliamo dati in una classe, definiamo metodi che eseguono azioni su questi dati, e quindi
creiamo isanze della classe al momento dell’esecuzione.
Creare un’istanza della classe corrisponde alla creazione di un oggetto. Ogni oggetto mantiene un insieme
separato di dati sui quali agiscono i metodi..
Puoi creare molte istanze di una classe, e quindi puoi avere molti oggetti, ognuno distinto da tutti gli altri
definiti nel sistema. Questi dati sono chiamati dati istanza (instance data).
Per esempio, se consideriamo il tipo di dati di cui possiamo aver bisogno in una semplice classe per un
conto bancario, questo può essere il numero di conto, il saldo e qualche modalità per poter memorizzare le
transazioni. Al momento dell’esecuzione, possiamo creare un unico oggetto per ogni cliente su cui
dobbiamo operare, dove ogni oggetto ha dati distinti da tutti gli altri clienti della banca.
Creare una Instanza di una Classe
Cambiamo un po’ la nostra classe e vediamo come possiamo creare una istanza di un oggetto:
class-id MyClass.
working-storage section.
01 your-name pic x(10).
method-id SayHello. procedure division.
display "hello" & yourname end method.
An Introduction to Object-Oriented Programming for COBOL Developers | 7
end class.
La variabile your-name definite nella working-storage section è il dato instanza per questa
classe:
working-storage section.
01 your-name pic x(10).
Per invocare il metodo SayHello ora lo facciamo usando un oggetto piuttosto che la classe. Di seguito
vediamo come creiamo questa istanza:
program-id.
TestMyClass 01 an-obj
type
MyClass.
procedure division.
set an-obj to new MyClass invoke an-obj::SayHello
end program.
Questa è la dichiarazione dell’oggetto, formalmente nota come object reference:
01 an-obj type MyClass.
Se proviamo ad invocare il metodo SayHello a questo punto, otterremmo un errore di Sistema al runtime poichè l’oggetto non è stato ancora creato.
Questa è la line ache crea l’oggetto:
set an-obj to new MyClass
La parola chiave NEW crea il nostro oggetto. NEW richiede che noi specifichiamo il tipo di oggetto che
vogliamo crear. Questo può sembrare strano poichè abbiamo già detto il tipo dell’oggetto quando lo
abbiamo dichiarato, ma più Avanti potremo vedere che un oggetto può essere dichiarato di un certo tipo,
ma al momento dell’esecuzione referenziato come un tipo diverso.
L’istruzione SET è usata frequentemente in OOP ed è il sinonimo della move ma è applicata agli oggetti.
E’ possibile dichiarare un’altra referenza all’oggetto ed assegnargli il valore di an-obj come segue:
set another-obj to an-obj
In questo caso, another-obj ora contiene un riferimento a an-obj. E’ importante notare che mentre
abbiamo due riferimenti ad un oggetto, abbiamo soltanto una istanza di titpo MyClass, e entrambi gli
oggetti another-obj e an-obj si riferiscono ad esso. Se invochiamo il metodo SayHello su an-obj
e another- object, essi potrebbero operare entrambi in antagonismo sugli stessi dati nelle workingstorage section.
L’unico modo per creare un nuovo oggetto è di usare la NEW:
set another-obj to new MyClass
Al momento la nostra classe ha un problema. Se invochiamo il metodo SayHello, potrebbe stampare
Hello, poiché il dato your-name non ha ricevuto ancora alcun valore.
Ci son diverse soluzioni a questo problema. Una soluzione è durante la creazione dell’oggetto altrimenti
conosciuta come construction. La nostra classe non fa nulla in fase di costruzione, ma può farlo se creiamo
un metodo chiamato New.
Costruttori
method-id New.
procedure
your-name
method.
division
to
a-
set
endusing by value a-name as string. -name
8 | An Introduction to Object-Oriented Programming for COBOL Developers
Ogni volta che è creato un oggetto, il Sistema al run-time invoca automaticamente il metodo New sulla
classe. Se non lo codifichi, il compilatore lo crea automaticamente.
Nel nostro metodo di sopra, non solo abbiamo definito un costruttore, ma abbiamo anche specificato che
esso ha un parametro. Quindi dobbiamo cambiare il nostro codice che crea l’oggetto:
set an-obj to new MyClassC'Scot")
Questo codice potrebbe essere scritto anche come segue:
set an-obj to MyClass::New("Scot")
Quello che abbiamo fatto è stato di fornire un modo per poter inizializzare il nostro oggetto e di prendere
l’argomento passato al costruttore ogni volta che è creato un oggetto del tipo MyClass.
Overloading di Metodo
In ogni caso è possibile avere versioni multiple del metodo New, ognuna corrispondente a differenti
argomenti che possono essere passati quando è creato l’oggetto. Questo è chiamato overloading di
metodo poichè il nome del metodo rimane lo stesso, ma gli argomenti accettati da ogni metodo sono
differenti.
Possiamo usare questa caratteristica dell’overloading di metodo per reintegrare il cosiddetto default
constructor (costruttore di default), altrimenti conosciuto come il parameterless constructor (costruttore
senza parametri). Per ottenere ciò, dobbiamo codificare un nuovo metodo New.
method-id New.
procedure division.
move all 'x' to yourname end method.
Questo ci ha permesso di creare l’oggetto sia fornendo un parametron, sia usando il costruttore di default
che non ha argomenti, ma ci permette ancora di inizializzare I nostri dati in working-storage section.
Proprietà
La nostra classe ha alcuni dati ad essa associate, una stringa chiamata your-name. Questo dato non è
direttamente accessibile dal programma usando la classe, così come la working-storage di un
programma non è accessibile ad un altro programma.
Le Proprietà consentono di rendere visibile i dati all’utente della classe.
Attualmente il nostro unico dato è il seguente:
01 your-name pic x(10).
Possiamo trasformare il dato in Proprietà, come segue:
01 your-name pic x(10) property.
Come tale, puoi accedere a questa proprietà attraverso una referenza all’oggetto:
display an-obj::your-name
La parola chiave property ci consente non solo di prendere il valore del dato, ma anche di impostarlo:
set an-obj::your-name to "Scot"
Comunque possiamo impedire di far impostare il valore a chiunque scrivendo come segue:
01 your-name pic x(10) property with no set.
I caratteri maiuscolo/minuscolo sono importanti nel COBOL gestito. Il maiuscolo/minuscolo del nome della
proprietà è determinato nella dichiarazione. Attualmente è scritta tutta in minuscolo. Possiamo cambiare il
An Introduction to Object-Oriented Programming for COBOL Developers | 9
nome come segue:
01 your-name pic x(10) property as "Name"
display an-obj::Name
La clausola static si applica anche alle proprietà:
01 dataitem pic x(10) property as "DataItem" static.
Quando viene richiamato il dato, di esso esiste sempre e solo una istanza indipendentemente da quandi
oggetti sono stati create. I dati statici sono usati nella stessa classe, non abbiamo bisogno di una istanza
per potervi accedere:
set MyClass:DataItem to "some text"
Visibilità del Metodo
I metodi che abbiamo definito fin’ora sono tutti pubblici, e questo è il default in COBOL. Un metodo
pubblico significa che esso può essere invocato mediante la referenza all’oggetto. Comunque, per molte
classi abbiamo bisogno di metodi che non vogliamo che siano visibili all’esterno della classe. Questi netodi
son chiamati metodi privati.
Per dichiarare un metodo privato, si usa la parola chiave private:
method-id ProcessData private.
Questo metodo non può essere invocato tramite la referenza all’oggetto, il che genererebbe un errore del
compilatore.
E’ possibile richiamare questo metodo dall’interno della stessa classe, dentro ad un metodo pubblico:
method-id DoSomething public.
procedure division using by value a-name as
string. invoke self::ProcessData end method. end
class.
Da notare l’uso della parola chiave special self. In questo caso significa "richiamare un metodo chiamato
ProcessData che è definito in questa classe".
Notiamo inoltre che dichiariamo esplicitamente questo metodo come public nella sua dichiarazione. Ciò
non è richiesto poichè è la visibilità di default, ma può essere utile da specificare nelle fasi iniziali.
Dati Locali
Quando scriviamo programmi in COBOL procedurale dobbiamo dichiarare tutti I dati nella workingstorage section. Quando lavoriamo con le classi, usiamo ancora la working-storage section per I dati
associate alla classe, ma possiamo anche definire dati che sono usati solo dai metodi, o i cosiddetti dati
locali.
Ci sono tre modi per definire variabili locali:
Nella LocalSection
Nel seguente esempio, mylocalvar è una variabile locale ed esiste solo per questo
metodo:
method-id ProcessData private.
local-storage section.
01 mylocalvar binary-short.
10 | An Introduction to Object-Oriented Programming for COBOL Developers
procedure division.
Usando
l’istruzione
DECLARE
end method.
Nell’esempio seguente, mylocalvar è definito usando l’istruzione DECLARE. La
visibilità della variabile così definita è solo all’interno del metodo dopo la dichiarazione:
method-id ProcessData private.
local-storage section.
procedure division.
declare mylocalvar as binary-short
Definire una
variabile
inline
end method.
Con questo Sistema possiamo creare una variabile locale chiamata counter come
parte dell’istruzione PERFORM. Il tempo di vita e la visibilità di questa variabile sono
associati con l’esecuzione dell’istruzione PERFORM. In altre parole, non possiamo
riferirci a counter dopo l’istruzione END PERFORM.
method-id ProcessData private.
local-storage section.
procedure division.
perform varying counter as binary-long from 1 by 1 until
counter > 10
display counter
end perform.
end method.
Tipi di Dati
Fin’ora le nostre classi hanno usato i tipi di dati COBOL come ad esempio pic X. Tutti i tipi di dati che si
usano nel COBOL procedurale sono supportati nel COBOL gestito.
Alcuni tipi di dati, quali pic X o gruppi di record non sono riconosciuti in .NET e JVM. Per favorire la
transizione dai tipi COBOL algi altri linguaggi, c’è un insieme di tipi predefiniti che sono riconosciuti da
.NET e JVM, e mappati direttamente in tipi di .NET e JVM. Questi tipi sono elencati nell’articolo Type
Compatibility of Managed COBOL with Other Managed Languages nella Visual COBOL documentation.
Qui ci sono tre esempi di dichiarazione dello stesso tipo, un intero con segno a 16 bit:
01 vall binary-short.
01 val2 pic s9(4) comp-5.
01 val3 type System.Int16.
In C# e in Java, la dichiarazione equivalente potrebbe usare un tipo chiamato short.
Per specificare un intero senza segno a 16 bit, puoi usare:
01 val1 binary-short unsigned.
01 val3 type System.UInt16.
In C#, la dichiarazione equivalente potrebbe usare un tipo ushort. In Java, comunque questo non ha
equivalenti poichè Java non supporta i tipi senza segno.
La cosa da tener presente quando lavori con le classi, è che qualsiasi dato vuoi usare nelle chiamate alle
classi, che sia un argomento per un metodo o una proprietà, la cosa migliore da fare è di usare I tipi
predefiniti del COBOL riportati nella tabella dell’articolo Type Compatibility of Managed COBOL with Other
An Introduction to Object-Oriented Programming for COBOL Developers | 11
Managed Languages nella documentazione.
Comunque in uno degli esempi precedenti, non abbiamo fatto come spiegato prima. Infatti abbiamo
definito un item pic X come proprietà. Così facendo il compilatore la traduce in un tipo intrinseco
String, non nel campo pic X.
Quando un utente della proprietà legge o imposta il dato, questo è convertito implicitamente dal tipo nativo
COBOL al tipo .NET o JVM, in questo caso, un string.
Dichiarando un item di gruppo come proprietà, viene convertito l’intero gruppo come un tipo string di
.NET o JVM. I tipi numerici come comp-5 sono convertiti all’equivalente più prossimo.
Ereditarietà
L’Ereditarietà è una parte importante della OOP. Essa consente di creare una nuova classe estendendo le
funzionalità di una classe già esistente. Se lo desideriamo, possiamo anche modificare il comportamento
della classe che stiamo estendendo.
Consideriamo l’esempio del conto bancario precedente. Possiamo immaginare che conti di vario tipo
(conto corrente, libretto di risparmio, ecc.) condividono dati comuni come il numero di conto ed il saldo, ma
l’operazione di prelievo può essere diversa a seconda del tipo di conto. Un conto corrente può aver la
necessità di controllare se c’è un limite dell’importo che si può prelevare e un libretto di risparmio dovrà
controllare altri fattori che influenzano gli interessi maturati, come ad esempio la quantità di denaro che può
essere ritirata entro un determinato periodo.
Una considerazione importante vedremo più avanti è che tutto ciò che è l'utilizzo di questi oggetti, diciamo
il bancomat, non dovrebbe essere necessario per determinare il tipo di conto è affrontare e quindi eseguire
l'elaborazione diverso. Si vuole semplicemente eseguire un'azione di ritiro contro qualunque conto oggetto
che sta usando
Una considerazione importante che vedremo più Avanti sarà riferita al bancomat, il quale potrebbe non
avere la necessità di determinare il tipo di conto che sta facendo un prelievo per eseguire l’operazione in
modo diverso. Si vuole semplicemente eseguire l’operazione di prelievo indipendemente dal conto in
questione..
Per ora vediamo come estendere una classe e modificarne il comportamento.
Per testare questo in Visual Studio, creiamo un progetto Class Library gestita e aggiungiamo le classi
come file separate al progetto – click destro sul progetto in Solution Explorer e click Add > New Item >
COBOL Class.
Questa è una classe di conto bancario semplificato:
class-id BankAccount.
working-storage section.
01 account-number pic 9(8) property as "AccountNumber".
01 balance float-long property.
method-id Withdraw.
procedure division using amount as float-long
returning result as condition-value.
*> Process for general withdrawal from a bank account
end method. end class.
Questo tipo di classe chiamata BankAccount, è spesso riferita come base class poichè essa definisce la
base della gerachia di classi che nascono da essa.
Creiamo ora una nuova classe per rappresentare una specializzazione di un conto bancario, un libretto di
12 | An Introduction to Object-Oriented Programming for COBOL Developers
risparmio:
class-id SavingsAccount inherits type BankAccount.
method-id Withdraw override.
procedure division using amount as float-long
returning result as condition-value.
end method.
*> Specialized process for Savings withdrawal. end
class.
Nel definire una nuova classe per libretto di risparmio, abbiamo usato la clausola inherits per
estendere una classe esistente. Tutti I membri pubblici (metodi, proprietà, campi define come public)
della classe base diventano parte della nuva classe.
Quindi un oggetto che è di tipo SavingsAccount, ha anche la proprietà AccountNumber, e un metodo
Withdraw che sono stati ereditati dalla classe base BankAccount.
La nostra classe SavingsAccount ha anche un metodo Withdraw che gestirà il modo differente in cui
sarà effettuato il prelievo rispetto al conto corrente. Per ottenere queste differenze di comportamento del
metodo della classe base usiamo la parola chiave override. Il significato di questa parola chiave,
diventerà evidente più avanti.
Creiamo un’altra specializzazione della classe BankAccount, un conto di debito:
class-id DebitAccount inherits BankAccount.
method-id Withdraw override.
procedure division using amount as float-long
returning result as condition-value.
end method.
*> Specialized process for Debit withdrawal. end
class.
Questa classe per il conto di debito è creato esattamente nello stesso modo della classe per il libretto di
risparmio. Essa estende la classe conto bancario e tutti i membri pubblici della classe conto bancario
diventano parte della nuova classe.
La classe DebitAccount ha anche un metodo Withdraw che sovrascrive il metodo Withdraw della
classe BankAccount e definisce come eseguire il prelievo nel conto di debito. Questo metodo
comunque è completamente differente dal metodo Withdraw della classe SavingsAccount.
Ora abbiamo tre classe nella nostra gerarchia – una classe base (BankAccount) e due classi
(SavingsAccount and DebitAccount) che sono derivate da essa, ognuna delle quali ha un metodo
differente Withdraw.
Guardiamo gli effetti dell’istanziazione degli oggetti e dell’invocazione dei metodi:
program-id. TestBankAccounts.
01 accountl type BankAccount.
01 account2 type BankAccount.
procedure division.
set account1 to new SavingsAccount set
account1::AccountNumber to 12345678
set account1::balance to 500.00
An Introduction to Object-Oriented Programming for COBOL Developers | 13
set account2 to new DebitAccount
set account2::AccountNumber to 87654321
set account2::balance to 100.00
end program TestBankAccounts.
Notiamo la dichiarazione del nostro tipo di oggetto, BankAccount, e la creazione di esso una volta come
SavingsAccount ed un’altra come DebitAccount.
Possiamo fare ciò perchè sia SavingsAccount che DebitAccount ereditano (o discendono) da
BankAccount. La validità di questo modo di procedure non è molto evidente in questo esempio, ma il
prossimo esempio sarà di maggiore aiuto:
method-id PerformWithdrawal.
procedure division using by value amount as floatlong account as type BankAccount. if not
account::Withdraw(amount)
*> perform error condition display
"not true" else
display
"true" end-if
end method.
In questo caso, un metodo riceve un argomento di tipo BankAccount sul quale esegue un operazione di
prelievo. Il metodo non ha bisogno di conoscere I diversi tipi di conto, ma qualunque sia il tipo di conto ad
esso passato è eseguito il corretto tipo di metodo di prelievo (Withdraw) associato ad ogni tipologia di
conto, sia esso un conto corrente o un libretto di risparmio..
Possiamo chiamare quel metodo con un argomento oppure account1 o account2:
invoke
SomeClass::PerformWithdrawal(100,account1)
invoke
SomeClass::PerformWithdrawal(200,account2)
Abbiamo passato account1 e account2 e per ognuno di essi è eseguito il metodo Withdraw
appropriato con SavingsAccount e DebitAccount.
Questa è una caratteristica molto utile della OOP e separa i dettagli di implementazione per i clenti che
usano le classi. In alternative possiamo dire che ci permette di estendere il Sistema aggiungendo nuovi tipi
di conto bancari minimizzando l’impatto sul codice esistente.
Sia per JVM che per .NET, puoi ereditare da una classe base, ma ovviamente la classe base, a sua volta
può ereditare da un’altra classe e così via.
Se una classe derivata deve chiamare un metodo definito nella classe base, può farlo usando la parola
chiave super. Per esempio, possiamo chiamare il metodo BankAccount WithDraw da dentro la classe
SavingsAccount in questo modo:
invoke super::Withdraw(100)
super può essere usata non solo per chiamare un metodo che abbiamo sovrascritto nella classe
derivata, ma possiamo chiamare ogni metodo pubblico definito nella gerarchia delle classi che abbiamo
ereditato..
Interfaces
Le classi e l’ereditarietà ci consentono di separare I dettagli di implementazione dagli utenti della classe,
ma c’è un altro aspetto della OOP che ci può aiutare ulteriormente a separare l’implementazione -
14 | An Introduction to Object-Oriented Programming for COBOL Developers
l’interfaccia.
Una interfaccia, come una classe, definisce una serie di metodi e possibili dati, ma a differenza di una
classe, non fornisce l’implementazione nel metodo. Questo perchè lo scopo dell’interfaccia è
semplicemente quello di definire il comportamento che deve avere una classe – comportamento inteso nel
senso dei metodi e proprietà sulla classe.
Qui c’è un esempio di interfaccia:
interface-id ErrorHandler.
method-id notifyError.
procedure division using by value error-code as binary-short. end
method.
method-id notifyWarning.
procedure division using by value warning-code as binary-short.
end method.
end interface.
Questa interfaccia definisce giusto due metodi che, come possiamo intuire, possono essere usati per
gestire un qualche tipo di errore.
Per definire una classe che gestisce questa interfaccia, dobbiamo realizzare l’interfaccia:
class-id MyErrorHandler implements type ErrorHandler.
method-id notifyError.
procedure division using by value error-code as binary-short.
*> display message box to the user end method.
method-id notifyWarning.
procedure division using by value warning-code as binary-short.
*> depending on the configuration, ignore this or print *> it to
the console end method. end class.
La parola chiave implements definisce l’interfaccia che vogliamo realizzare per questa classe e il
compilatore controllerà che che tutti i metodi siano realizzati correttamente.
Diversamente dalla derivazione di una classe, che può essere effettuata su una singola classe, puoi
implementare tutte le interface che desideri in una singola classe.
Possiamo creare una istanza della nostra classe della nostra classe e, poichè abbiamo realizzato
l’interfaccia ErrorHandler, possiamo passare una referenza all’oggetto di questa classe ad ogni codice
che si aspetta di lavorare con l’interfaccia ErrorHandler.
class-id ProcessData. working-storage section.
01 error-handler-list List[type ErrorHandler] value null static.
An Introduction to Object-Oriented Programming for COBOL Developers | 15
method-id RegisterErrorHandler static.
procedure division using by value error-handler as type
ErrorHandler. if error-handler-list = null create error-handler-list
end-if.
write error-handler-list from error-handler end method.
method-id NotifyErrorHandlers static. local-storage section.
01 error-handler type ErrorHandler.
procedure division using by value error-code as binary-short. perform
varying error-handler thru error-handler-list invoke errorhandler::notifyError(error-code) end-perform end method.
method-id DoProcessing.
01 error-code binary-short value 1. procedure division.
*> do something and, possibly, call NotifyErrorHandlers when something
*> goes wrong
*> ...
invoke self::NotifyErrorHandlers(error-code)
*> ... end method. end class.
program-id. TestProgram.
working-storage section.
01 error-handler type MyErrorHandler.
01 processData type ProcessData.
procedure division.
set error-handler to new MyErrorHandler
invoke type ProcessData::RegisterErrorHandler(by value errorhandler)
set processData to new ProcessData invoke
processData::"DoProcessing"
end program TestProgram.
Guardiamo meglio questa codifica poichè ci sono alcuni concetti nuovi.
Prima di tutto, abbiamo una classe, ProcessData. Qualche volta, durante l’esecuzione del metodo
DoProcessing, ProcessData informeremo a chi è interessato che c’è stato un errore. Questo può
essere fatto chiamando l’interfaccia ErrorHandler usando il metodo NotifyErrorHandlers.
Questa classe può notificare a diversi clienti per registrar la loro implementazione di interfaccia usando il
metodo RegisterErrorHandler. Ogni interfaccia è memorizzata in una lista di oggetti. Non Andiamo a
vedere come è fatta questa lista, ma assumiamo che sia una classe fornitaci dalle classi del framework di
.NET o JVM.
Quando si verifica un errore ed è chiamato il metodo NotifyErrorHandlers, il codice usa le
caratteristiche della sintassi del COBOL gestito che consente di iterare attraverso la collezione delle
interfacce di gestione degli errori nella lista. Ogni iterazione risulta essere nel reference della memoria
locale error-handler impopstata al prossimo elemento della lista. Il codice semplicemente richiama il
metodo NotifyError e l’implementazione di questo decide cosa fare.
Il TestProgram costruisce una istanza di MyErrorHandler e lo passa come argomento al metodo
RegisterErrorHandler. Questa chiamata implica una conversion implicita dal tipo MyErrorHandler,
una classe, al tipo ErrorHandler, una interfaccia.
Nomi di Classe
Finora, le nostre classi hanno avuto nomi semplici ma questo può presto portare a scontri con le classi
16 | An Introduction to Object-Oriented Programming for COBOL Developers
create da altre persone. Per risolvere questo, creiamo semplicemente classi con nomi lunghi impiegando
l'uso di namespace (spazio dei nomi), che non è altro che una convenzione per la denominazione classi.
Qui c’è un nome di classe corretto
com.acme.MyClass
MyClass è una classe differente dalla seguente:
com.yourcompany.MyClass
Ogno cosa che riporti al nome della classe è considerate un namespace, se lavoriamo in .NET, o un
package name, se lavoriamo in JVM. In questo caso, i namespace sono com.acme e
com.yourcompany.
Questa convenzione ci consente di creare classi con lo stesso nome, ma senza conflitti tra di loro.
Mentre questa è una convenzione, il Compilatore fornisce regole e sintassi per poter lavorare con i
namespace in modo semplice e, infatti vi sono alcune regole che riguardano l’accessibilità delle classi
nell’ambito dei namespace.
Quando ti riferisci ad una classe che ha un namespace, devi usare il suo nome intero. Per esempio:
01 an-obj type com.acme.MyClass.
01 another-obj type com.yourcompany.MyClass.
Il compilatore COBOL fornisce la regola ILUSING che ti consente di usare il nome abbreviato della
classe:
$set ILUSING(com.acme)
Quando usi questa regola in un file sorgente, importi il namespace nel tuo progetto e quindi referenzi la
classe con il suo nome abbreviato, come segue:
01 an-obj type MyClass.
Mentre questo è generalmente una pratica accettata, poicè I nomi delle classi possono diventare molto
lunghi, dovresti evitare di importare namespace. Inoltre, se si verifica che due classi hanno lo stesso nome
è necessario distinguerle specificando il nome completo di esse.
Tipi Intrinsici
Il Compilatore COBOL riconosce molte classi in .NET e JVM e non è necessario specificare il loro
completo. Le due classi di cui parleremo sono Object
(System.Object in .NET e java.lang.Object in JVM) e String (System.String in .NET e
java.lang.String in JVM).
Object è importante percè in definitive tutte le classi ereditano da questo tipo, sia che lo specifichi o
meno. Perciò un oggetto può essere convertito a questo tipo.
String è comunemente usata per memorizzare dati Unicode. Sia in JVM che in .NET, la stringa, una
volta creata, è considerata immutabile. Qualunque metodo invochi sulla classe String, il risultato è un
nuovo oggetto stringa:
01 strl type System.String.
01 str2 String.
01 str string.
Tutte le dichiarazioni di sopra sono equivalenti in .NET.
Notiamo che non vi è la necessità di chiamare il metodo New quando creiamo una stringa:
set strl to "Hello World"
Puoi combinare l’uso delle stringhe con I campi pic X come segue:
01 a-pic-x pic X(10) value "something". display a-pic-x & str1 & "blah"
Qui c’è un esempio dell’uso di uno dei molti metodi sulle stringhe in .NET COBOL:
set str1 to str1::Replace("foo", "bar")
Notiamo che assegniamo il risultato di questo metodo all’oggetto originale. Se non lo facciamo, str1
potrebbe restare inalterata.
Lo stesso esempio, per JVM COBOL è il seguente:
set str1 to str1::replace("foo", "bar")
In .NET e JVM, la sola differenza tra questi metodi è il maiuscolo - String.Replace per .NET e
String.replace per JVM.
The .NET and JVM Frameworks
.NET e JVM sono entrambi ricchi di classi per ogni tipo di funzionalità. Conoscerle tutte è molto
impegnativo, anche in termini di tempo, ma ve ne sono alcune che dovresci conoscere facilemtente, in
particolare le classi collection.
Per aiutarci ad illustrare l’utilità di questi frameworks, vediamo come l’aritmetica per la data ed il tempo
diventa semplice nel seguente esempio in .NET COBOL:
working-storage section.
01 dt1 type System.DateTime.
01 dt2 type System.DateTime.
01 ts type System.TimeSpan.
set dt1 to type System.DateTime::Now invoke
System.Threading.Thread::Sleep(1000)
set dt2 to type System.DateTime::Now set ts
to dt2 - dt1
display ts
Vediamo cosa abbiamo realizzato.
Prima di tutto abbiamo dichiarato tre riferimenti ad oggetti, due oggetti DateTime e un oggetto
TimeSpan.
La classe DateTime fornisce un esteso insieme di routine per la manipolazione delle date e del tempo.
Per avere un’idea di queste possibilità, vedi la descrizione delle classi in Microsoft's MSDN.
La classe TimeSpan è usata quando calcoliamo la differenza tra due oggetti DateTime.
Nella prima linea di codice, inizializziamo l’oggetto dti usando un metodo statico sulla classe
System.DateTime, Now. Ci sono molti altri modi per inizializzare un oggetto DateTime ma questo è un
modo conveniente per ottenere la data e l’orario corrente:
set dti to type System.DateTime::Now
Nella linea seguente, usiamo di nuovo un metodo static, questa volta per fermare il thread corrente per un
periodo specificato. Puoi invocare la routine Micro Focus CBL_THREAD_SLEEP per ottenere lo stesso
risultato, come segue:
invoke System.Threading.Thread::Sleep(1000)
La linea successive inizializza il nostro secondo oggetto DateTime:
set dt2 to type System.DateTime::Now
La linea successive dimostra una caratteristica del compilatore del codice gestito COBOL Compiler
18 | An Introduction to Object-Oriented Programming for COBOL Developers
chiamata opertore di overloading:
set ts to dt2 - dt1
L’operatore di overloading è una caratteristica avanzata della ed è opportuno dargli un’occhiata. Quando
definiamo una classe, è anche possibile fornire una implementazione di alcuni operatori aritmetici, come
add e subtract. La classe DateTime definisce diversi operatori per le date ed il tempo ed il loro
confronto.
Mentre puoi eseguire operazioni aritmetiche sugli oggetti usando l’operatore di overload, le classe
generalmente fornisco metodi equivalenti che puoi usare direttamente, come nel caso di DateTime. La
linea seguente fornisce lo stesso risultato della precedente:
set ts to dt2::Subtract(dt1)
Entrambi gli approcci sono un oggetto TimeSpan. Questo oggetto contiene il risultato di una espressione
aritmetica. Infine, visualizziamo il risultato.
Ogni volt ache usi la parola DISPLAY in riferimento ad un oggetto, il COmpilatore invoca automaticamente il
metodo ToString che è definito nella classe base Object. Se ricordi, tutte le classi ultimamente
discendono da Object. Normalmente il metodo ToString ritorna semplicemente il nome del Type, ma la
classe TimeSpan sovrascrive il metodo ToString e ritorna una stringa significativa riguardante l’oggetto
TimeSpan:
display ts
Vediamo ora lo stesso esempio tradotto in JVM COBOL:
$set ilusing"java.util.concurrent"
program-id. Program1 as "Program1".
data division. working-storage section.
01 start-time binary-double.
01 end-time binary-double.
01 interval binary-double.
01
01
01
01
hr binary-double.
min binary-double.
sec binary-double.
ms binary-double.
procedure division.
*> get the start time
set start-time to type System::currentTimeMillis()
*> wait a second
invoke type Thread::sleep(1000)
*> get the end time
*> calculate the difference between start and end time set interval to
end-time - start-time
set hr to type TimeUnit::MILLISECONDS::toHours(interval) set min
to type TimeUnit::MILLISECONDS::toMinutes(interval - type
TimeUnit::HOURS::toMillis(hr))
set sec to type TimeUnit::MILLISECONDS::toSeconds(interval - type
TimeUnit::HOURS::toMillis(hr) - type TimeUnit::MINUTES::toMillis(min))
display String::format("%02d:%02d:%02d", hr, min, sec)
goback.
end program Program1.
Qui ci sono un po’ di cose diverse.
La classe equivalente di System.DateTime nel framework .NET è Date in JVM e non ha ubn metodo
subtract che può essere sovrascritto. Per questo dobbiamo usare set.
La classe .NET System.TimeSpan non ha una alternativa in JVM, perciò dobbiamo usare TimeUnit
per calcolare il tempo tra due date.
Riflessione
Quando compili un programma COBOL per .NET o per JVM, il Compilatore crea un eseguibile conforme
alle specifiche della piattaforma. Queste specifiche devono aderire ad ogni specifica di linguaggio che
supporta .NET o JVM. Poichè tutti i linguaggi sono conformi allo stesso protocollo, le classi scritte in ogni
linguaggio possono essere facilmente integrate.
Un altro vantaggio di questa conformità è qualcosa chiamata riflessione. La riflessione è la capacità di
esaminare i dettagli sottostanti di un determinato tipo. Con questa capacità è possibile esaminare I metodi
forniti da un tipo, gli argomenti per ogni metodo e anche il codice di un metodo. Questa è una caratteristica
molto potente del framework ed apre molte possibilità per lo sviluppo di applicazioni. Sebbene possa
sembrare non immediatamente ovvio come la riflessione possa essere valutabile, capiamo che essa puà
aiutare quando consideriamo come varie tecnologie nel codice gestito possono fare quello chef anno
senza conoscere come sono definite le classi.
Un esempio dell’uso delle riflessione è il cosidetto Intellisense che è una caratteristica di Visual Studio e
Eclipse IDEs che ti assiste mostrandoti una lista di metodi e proprietà disponibili su un dato oggetto.
20 | An Introduction to Object-Oriented Programming for COBOL Developers
Scarica

Introduzione - de Viti de Marco