3.4. Proiectarea modulară
Descompunerea unei probleme și testarea independentă a subprogramelor
La finalul lecției vei putea...
- să explici principiul proiectării modulare;
- să descompui o problemă în subprobleme cu responsabilități clare;
- să alegi subprogramele potrivite pentru o problemă algoritmică;
- să stabilești datele de intrare și rezultatele fiecărui subprogram;
- să utilizezi funcții definite în lecțiile anterioare pentru a construi o soluție mai mare;
- să testezi independent subprogramele înainte de integrare;
- să diferențiezi testarea unei funcții de testarea programului complet;
- să identifici avantajele modularizării: claritate, reutilizare, depanare și modificare mai ușoară.
Proiectarea modulară
2.1. Ce înseamnă proiectarea modulară?
O problemă complexă poate fi împărțită în probleme mai mici. Fiecare subproblemă este realizată de un subprogram. Această organizare se numește proiectare modulară. Programul complet este obținut prin colaborarea subprogramelor.
Un modul bun are o responsabilitate clară.
2.2. Cum descompunem o problemă?
- Citim enunțul și identificăm rezultatul cerut.
- Separăm operațiile care au un rol distinct.
- Pentru fiecare operație stabilim datele necesare și rezultatul.
- Definim funcțiile corespunzătoare.
- Testăm fiecare funcție separat.
- Combinăm funcțiile în programul principal.
- Testăm programul complet pe date reprezentative.
2.3. Interfața unui subprogram
Pentru a utiliza corect o funcție trebuie să știm ce primește și ce produce. Aceste informații formează interfața funcției: parametrii de intrare și valoarea returnată sau efectul produs.
| Subprogram | Date primite | Rezultat/efect |
|---|---|---|
cmmdc(a,b) | două numere naturale | CMMDC-ul lor |
suma_cifre(n) | un număr natural | suma cifrelor |
este_prim(n) | un număr natural | adevărat/fals |
afiseaza_raport(...) | datele analizate | afișare |
2.4. De ce testăm independent?
Dacă programul complet nu produce rezultatul așteptat, testarea separată a funcțiilor reduce zona în care căutăm eroarea. Dacă fiecare funcție a trecut testele proprii, integrarea devine mai ușor de verificat.
2.5. Testarea și verificarea
Testarea înseamnă executarea programului cu date alese pentru a observa comportamentul. Verificarea înseamnă justificarea faptului că algoritmul și rezultatul sunt corecte.
Responsabilități separate
Organizarea unui eveniment presupune mai multe roluri: invitați, program și logistică. Într-un program mare, nu vrem o singură funcție uriașă. Preferăm funcții mai mici, fiecare responsabilă de o operație.
Dacă o funcție de calcul al mediei este corectă, ea poate fi reutilizată în mai multe rapoarte.
Alege responsabilitatea
Care este o responsabilitate bună pentru o funcție?
Construim o soluție din funcții
def media(a, b, c):
return (a + b + c) / 3
def interval(a, b, c):
return max(a, b, c) - min(a, b, c)
def afiseaza_raport(a, b, c):
print("Media =", media(a, b, c))
print("Interval =", interval(a, b, c))
x = float(input("x = "))
y = float(input("y = "))
z = float(input("z = "))
afiseaza_raport(x, y, z)Funcțiile media() și interval() calculează și returnează valori. Funcția afiseaza_raport() le reutilizează. Programul principal se ocupă de citire și apel.
Ce urmează?
Într-o soluție modulară, după ce ai testat funcțiile separat, ce faci?
Separăm responsabilitățile
subprogram media(a, b, c) returnează
returnează (a+b+c)/3
■
subprogram interval(a, b, c) returnează
returnează maxim(a,b,c) - minim(a,b,c)
■
subprogram raport(a, b, c)
m ← Apel media(a,b,c)
d ← Apel interval(a,b,c)
afișează m, d
■
citește a, b, c
Apel raport(a,b,c)Două funcții calculează rezultate, iar a treia le folosește pentru prezentarea raportului.
Reguli pentru o modularizare bună
- Modularizarea nu înseamnă să transformăm fiecare linie într-o funcție. Un modul trebuie să aibă un rol clar.
- O funcție nu trebuie să depindă inutil de variabile globale.
- Înainte de integrare, testează fiecare funcție cu valori normale și cazuri-limită.
- Un singur test nu este dovadă absolută a corectitudinii.
- Testarea observă comportamentul pe date concrete; verificarea justifică metoda și rezultatul.
- Dacă două funcții fac aproape aceeași prelucrare, analizează dacă una poate fi reutilizată de cealaltă.
- Programul principal ar trebui să fie ușor de urmărit: citește date, apelează funcțiile potrivite și folosește rezultatele.
Analizator modular de numere
Problemă
Se citește un număr natural n. Să se construiască un program modular care determină suma cifrelor, numărul de cifre și dacă numărul este palindrom. Fiecare prelucrare trebuie realizată într-o funcție separată, iar funcțiile care calculează rezultate trebuie să le returneze.
Ideea de rezolvare
Descompunem problema în trei subprobleme: suma cifrelor, numărul de cifre și testul de palindrom. Fiecare funcție primește n și returnează un rezultat.
Algoritm
subprogram suma_cifre(n) returnează
s ← 0
cât timp n > 0 execută
s ← s + n % 10
n ← [n/10]
■
returnează s
■
subprogram numar_cifre(n) returnează
dacă n = 0 atunci
returnează 1
■
c ← 0
cât timp n > 0 execută
c ← c + 1
n ← [n/10]
■
returnează c
■
subprogram palindrom(n) returnează
copie ← n
inv ← 0
cât timp n > 0 execută
inv ← inv*10 + n%10
n ← [n/10]
■
returnează inv = copie
■
citește n
s ← Apel suma_cifre(n)
c ← Apel numar_cifre(n)
p ← Apel palindrom(n)
afișează s, c, pPython
def suma_cifre(n):
s = 0
while n > 0:
s = s + n % 10
n = n // 10
return s
def numar_cifre(n):
if n == 0:
return 1
c = 0
while n > 0:
c = c + 1
n = n // 10
return c
def palindrom(n):
copie = n
inv = 0
while n > 0:
inv = inv * 10 + n % 10
n = n // 10
return inv == copie
n = int(input("n = "))
s = suma_cifre(n)
c = numar_cifre(n)
p = palindrom(n)
print("Suma cifrelor =", s)
print("Număr de cifre =", c)
print("Palindrom =", p)Testare
| Tip | n | Rezultate așteptate |
|---|---|---|
| normal | 1221 | 6, 4, True |
| limită | 0 | 0, 1, True |
| particular | 7 | 7, 1, True |
| nepalindrom | 123 | 6, 3, False |
Verificare
Pentru n = 1221, suma este 6, numărul de cifre este 4, iar inversul este tot 1221. Testul cu 0 este important.
Construim module simple
- Descompune problema „determinarea mediei și a maximului a trei valori” în două funcții.
- Construiește o funcție care returnează minimul a trei numere și testeaz-o separat.
- Construiește o funcție care returnează suma cifrelor și una care returnează produsul cifrelor.
- Scrie un program care utilizează două funcții independente pentru CMMDC și CMMC.
- Pentru un număr natural, creează funcții separate pentru numărul de cifre și cifra maximă.
- Pentru fiecare funcție de la exercițiile anterioare, propune câte trei teste: normal, limită și particular.
Proiectare și testare modulară
- Descompune o problemă de analiză a unui număr în cel puțin patru funcții și justifică responsabilitatea fiecăreia.
- Construiește o funcție care returnează cel mai mare divizor propriu și reutilizeaz-o într-o funcție care verifică dacă numărul este perfect.
- Construiește o funcție pentru conversia din baza 10 în baza 2 și o funcție separată pentru verificarea reprezentării obținute.
- Realizează un program modular care citește trei numere și raportează: minim, maxim, CMMDC, CMMC și media. Evită variabilele globale.
- Analizează un program cu o singură funcție foarte lungă și propune o descompunere în cel puțin trei funcții. Explică avantajele.
- Proiectează o strategie de testare pentru un program modular: ce testezi mai întâi, ce testezi după integrare și ce verifici matematic?
Verifică-ți înțelegerea
Mini-quiz
1. Ce înseamnă proiectare modulară?
2. De ce testăm funcțiile separat?
3. Ce trebuie stabilit pentru un subprogram?
4. Testarea înseamnă...
5. Programul principal ar trebui să...
Analizator modular de numere
Analizator modular de numere
Construiește o aplicație modulară care primește un număr natural și generează un raport cu mai multe proprietăți: numărul de cifre, suma cifrelor, cifra maximă, numărul de divizori, primalitatea, CMMDC cu o a doua valoare și conversia în baza 2. Fiecare operație importantă trebuie realizată printr-o funcție separată.
- stabilește înainte de programare responsabilitatea fiecărei funcții;
- folosește parametri pentru datele de intrare și return pentru rezultate;
- testează independent fiecare funcție;
- integrează apoi funcțiile într-un program principal simplu;
- include cel puțin un caz-limită: n = 0 sau n = 1, după caz;
- documentează într-un tabel cel puțin cinci teste și rezultatele așteptate;
- alege funcțiile astfel încât aceeași prelucrare să nu fie implementată de două ori.