Documente Academic
Documente Profesional
Documente Cultură
PROIECTAREA CLASELOR 3
După cum s-a putut constata din capitolul anterior, modelul obiectual
are la bază, drept element de construcţie formală, clasa din care pot fi
obţinute, prin instanţiere, obiectele concrete. În capitolul de faţă vom
încerca să conturăm, fără a avea pretenţia unei abordări exhaustive, câteva
principii-ghid în descoperirea şi formarea claselor. Cu alte cuvinte, vom
prezenta câteva elemente legate de proiectarea claselor de obiecte,
deocamdată la nivel general, fără a intra serios pe domeniul ingineriei
software (sau ingineriei aplicaţiilor software). Pentru a rămâne neutri faţă
de un anume specific tehnologic concret, modelul exemplificat va fi unul
din contextul afacerii, principiile enunţate şi dezbătute însă se pot aplica
în aceeaşi măsură şi interfeţelor grafice sau soluţiilor de integrare între
diferite contexte.
4 Capitolul 2
Unul din cele mai simple şi mai liniare (deci mai simplu de prezentat)
modele pentru ciclul de dezvoltare al unei aplicaţii este cel, aşa-numit, în
cascadă. Denumirea acestui model provine de la modul de înlănţuire a
diferitelor etape. Mai important, la acest moment, decât această
modalitate de înlănţuire a etapelor, sunt activităţile în sine, aşa cum au
fost delimitate:
culegerea sau enunţarea specificaţiilor;
proiectarea elementelor constitutive;
elaborarea codului de implementare;
testarea componentelor şi proiectului în ansamblu;
revizuirea elementelor proiectate sau codului de implementare
a componentelor.
Se observă că, deşi aparent liniar, de fapt, ultima etapă este mai
degrabă o etapă de tranziţie către o altă re-construcţie recursivă a
proiectului. Acesta este şi motivul pentru care se vorbeşte despre un ciclu
de dezvoltare. De altfel, diversitatea asamblării ciclice sau recursive pe
activităţi, etape şi faze s-a materializat într-o adevărată pleiadă de modele
ale ciclului de viaţă software. Interesant este însă faptul că, indiferent de
model, activităţile mai sus-menţionate nu au putut fi niciodată evitate sau
„escamotate”, ele fiind, de fapt, mai nuanţate sau re-asamblate în faze-
componente mai elaborate funcţie de complexitatea crescândă a
proiectelor software. Acest lucru reprezintă de fapt materia primă a unei
discipline de sine-stătătoare: ingineria software1.
1
Eng.: software engineering
Proiectarea claselor 5
Figura 2.3 Diagrama de clase UML: Obiecte generale ale modelului contabil
Figura 2.4 Diagrama de clase UML: detalii ale obiectelor modelului contabil
Proiectarea claselor 11
3. Ce algoritm va fi utilizat?
a. Ce variabile locale vor fi necesare?
b. Ce structuri de control vor fi utilizate?
După cum am sugerat mai sus, în general, clasa este văzută din
perspectiva implementării: adică un mecanism concret de definire
(internă, în primul rând, dar şi externă) a obiectelor. Din acest punct de
vedere, trebuie să stabilim de la început că există o distincţie destul de
clară între clasă şi tip, pe care, într-o formă sau alta, mai explicit sau
implicit, o afirmă sau o „recunoaşte” şi o formalizează orice limbaj de
programare.
// implementare conventie
// - incapsulare reprezentare interna
// - reprezentare externa prin proprietati JavaBean
public String getCod() {
return cod;
}
public String getDenumire() {
return denumire;
}
public void setDenumire(String denumire) {
this.denumire = denumire;
}
Am arătat deja mai sus că în cazul multor sau poate chiar a majorităţii
limbajelor de programare care folosesc drept concept fundamental clasa,
aceasta pare mai relevantă preponderent prin latura ei legată de
implementare, adică de construire, mai mult decât latura declarativă, de
utilizare sau externă. De asemenea, am sugerat deja că cel mai relevant
din punctul de vedere al laturii declarative este, în cazul limbajului Java,
noţiunea de interfaţă.
2
În cazul operaţiilor, pe „locul” tipului returnat se poate găsi şi cuvântul-cheie void,
atunci când execuţia metodei de implementare nu returnează explicit o valoare prin
instrucţiunea return.
18 Capitolul 2
Altfel, clasele vor fi utilizate (sau invocate) prin numele lor întreg,
adică inclusiv numele complet al bibliotecii (pachetului) acesteia.
import app.model.validare.Validatable;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public OperatiuneContabila() {
}
în interiorul pachetelor,
dacă sunt declarate public, atunci, în momentul în care se
declară accesul la un pachet, înseamnă că numai clasele
publice pot fi invocate sau referenţiate;
dacă nu sunt însoţite de specificatorul de vizibilitate public
(altele nu sunt acceptate) atunci vor fi considerate cu
vizibilitate la nivel (sau în interiorul) package;
Metodele constructor
// constructor cu parametri
public OperatiuneContabila(Integer idOperatiune,
Date dataContabilizare) {
this.idOperatiune = idOperatiune;
28 Capitolul 2
this.dataContabilizare = dataContabilizare;
}
... ... ...
}
Compunerea claselor
Figura 2.9 Derivări ale operațiunilor rezultate din specializarea naturii contabile
o clasă (abstractă sau nu) care extinde o altă clasă devine sub-tip al
acesteia;
o clasă care implementează o interfaţă devine sub-tip al acesteia;
o interfaţă care extinde o altă interfaţă devine sub-tip al acesteia.
După cum am arătat deja, interfeţele, fiind prin natura lor formate
numai din specificaţii declarative, pot fi asimilate foarte simplu tipurilor,
adică pot fi folosite doar pentru declararea variabilelor, nu şi pentru
construirea instanţelor ale căror referinţe vor fi reţinute în aceste
variabile.
Clasele abstracte constituie un caz mai aparte: nu pot fi utilizate
nemijlocit în crearea instanţelor, fiindcă reprezintă de obicei specificaţii
de implementare incomplete, parţiale, dar nici nu pot fi considerate
absolut declarative, deoarece ar putea prezenta elemente de implementare
care ar putea fi reutilizate în clasele lor concrete. Prin urmare, clasele
abstracte:
Proiectarea claselor 35
// Superclasa InregistrareContabilă –
// abstractă deci neinstanţiabilă direct
public abstract class InregistrareContabila {
... ... ...
}
sau formarea de noi tipuri de caractere ale căror variabile de instanţă sau
componente (interne şi externe) se bazează în foarte multe cazuri pe şiruri
de caractere ca cea mai flexibilă cale de reprezentare.
void testCharArrays() {
char[] char_seq = {'P', 'R', 'O'};
for (char c : char_seq) {
System.out.print(c + ",");
}
System.out.println();
String char_str = "PRO";
for (int i = 0; i < char_str.length(); i++) {
System.out.print(char_str.charAt(i) + "+");
}
System.out.println();
char[] char_str_array = char_str.toCharArray();
for (int i = 0; i < char_seq.length; i++) {
System.out.println("char_seq[" + i + "] == "
+ "char_str_array["
+ char_str.indexOf(char_seq[i]) + "] : "
+ char_str.charAt(i) + " : "
+ (char_seq[i] == char_str_array[i]));
}
}
void testCharAsciiCode() {
char c = 'a';
int asciiCode = (int) c;
System.out.println("char 'a' - ascii cod = " + asciiCode);
}
void testCharacterInstance() {
Character c_a = new Character('a');
Character c_A = 'a'; // auto-boxing
System.out.println("character a - ascii cod = "
+ Character.getNumericValue('a'));
System.out.println("character a - majuscula = "
+ Character.toUpperCase('a'));
System.out.println("character a este numeric: "
+ Character.isDigit('a'));
System.out.println("character a este literal: "
+ Character.isLetter('a'));
System.out.println("character a este spatiu: "
+ Character.isSpaceChar('a'));
System.out.println("character a este minuscula:"
+ Character.isLowerCase('a'));
}
void testStringInstance(){
String str_1 = "pro";
String str_2 = "pro";
System.out.println("str_1 == str_2 :" + (str_1 == str_2));
// true
String str_3 = new String("pro");
System.out.println("str_1 == str_3 :" + (str_1 == str_3));
// false
String str_4 = String.valueOf(true);
System.out.println("true = " + str_4.equals("true"));
// true
}
void testStringImmutable() {
char[] char_seq = {'i', 'm', 'm', 'u', 't',
'a', 'b', 'l', 'e'};
String str_1 = new String(char_seq);
void testSubStrings_2() {
String str_1 = "SELECT o FROM Client c WHERE c.cod = 111";
String s_str_1 = str_1.substring(0, 8);
System.out.println("s_str_1: " + s_str_1);
String s_str_2 = str_1.substring(
str_1.indexOf(s_str_1) + s_str_1.length() + 1,
(str_1.indexOf(s_str_1) + s_str_1.length()+1)+ 13);
System.out.println("s_str_2: " + s_str_2);
System.out.println("str_1.startsWith(s_str_1) :: "
+ str_1.startsWith(s_str_1));
System.out.println("str_1.contains(s_str_2) :: "
+ str_1.contains(s_str_1));
System.out.println("str_1.endsWith(s_str_3) :: "
+ str_1.endsWith(s_str_3));
String x1 = "abcd";
String x2 = "bdce";
System.out.println("x1.compareTo(x2) = "
+ x1.compareTo(x2));
// return -1, adica x1 se gaseste inainte de x2
}
strBuff += str;
}
System.out.println("Time multiply_OperatorConcatenare: "
+ (new Date().getTime() – startTime.getTime()));
return strBuff;
}
return strBuff.toString();
}
Variabilele de tip int vor fi utilizate pentru a stoca valori întregi. Prin
urmare int provine, de fapt, de la integer. Însă, la fel ca şi int, valori
întregi pot stoca și tipurile primitive byte, short și long. Ele se diferenţiază
prin dimensiunea memoriei alocate și, ca urmare, dimensiunea
intervalului matematic acoperit. Dacă este nevoie de stocarea unor valori
reale, atunci limbajul Java pune la dispoziție tipurile float și double. Tipul
boolean este cel mai simplu şi acoperă doar două valori: true și false, pe
când un char va reprezenta un cod (ca urmare tot un număr întreg), însă
un cod dintr-o tabelă în care fiecare element reprezintă un caracter
aparţinând unui set oficial agreat și folosit într-o anumită zonă geografică
(după cum se știe, și limba română are un set de caractere specific).
Boolean b =
new Boolean(false);
char Character Character c = Character c = ‘a’;
new Character(‘a’);
byte Byte Byte b = Byte b = 7;
new Byte(7);
short Short Short s = Short s = 7;
new Short(7);
int Integer Integer n = Integer n = 7;
new Integer(7);
long Long Long n = Long n = 7L;
new Long(7);
float Float Float n = Float n = 7.0f;
new Float(7.07);
double Double Double n = Double n = 7.0;
new Double(7.07);
Proiectarea claselor 45
import java.text.ParseException;
try{
test.throwObjectMethod();
}catch(Throwable t){
System.out.println("t - " + t.getMessage());
}
java.lang.Object
|____ java.lang.Throwable
|____ java.lang.Exception
void testFinally(){
52 Capitolul 2
try{
if (1==1){
throw new Throwable("Trebuie să-l prinzi!");
}
if (1==1){
throw new RuntimeException("Runtime Exception");
}
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Tratament pentru "
+ e.getMessage());
}catch(Throwable e){
System.out.println("Tratament pentru "
+ e.getMessage());
}finally{
System.out.println(
"Anyway ... with or without exception ...");
}
}
int compareTo(Date),
boolean before(Date),
boolean after(Date),
boolean equals(Date).
// 3 Conversii
// 3.1 java.util.Date -> java.util.Calendar
Proiectarea claselor 59
+ f4.format(c6.getTime()));
c6.roll(Calendar.HOUR_OF_DAY, 3);
System.out.println("Timp final = timp start + 3ore : "
+ f4.format(c6.getTime()));
// 4.4 Scadere interval 25 secunde
// fara trecere peste ordin (nr minutelor nu se schimba)
Calendar c7 = Calendar.getInstance();
System.out.println("4.4 Timp start: "
+ f4.format(c7.getTime()));
c7.roll(Calendar.SECOND, -25);
System.out.println("Timp final = timp start - 25secunde : "
+ f4.format(c7.getTime()));
}
}