Modulaarisuus¶
Moduulaarisuus on ohjelman suunnittelu- ja toteutusvaiheen mekanismi, jolla suuri ohjelma saadaan jaettua pienempiin ja helpommin hallittaviin osiin. Jos ohjelma on modulaarinen, se on jaettu selkeisiin osakokonaisuuksiin, joiden yhteistyön tuloksena saadaan koko ohjelma.
Moduuli on ohjelman osakokonaisuus, joka koostuu yhteenkuuluvista ohjelmarakenteista. Useimmissa ohjelmointikielissä jokainen moduuli toteutetaan erillisenä lähdekooditiedostona tai lähdekooditiedostoparina.
Moduulit ovat jossain määrin analogisia luokkien kanssa: Jokaisella moduulilla on julkinen ja yksityinen rajapinta. Luokat ja moduulit eivät kuitenkaan missään tapauksessa tarkoita samaa asiaa. Ainoat yhtäläisyydet liittyvät siihen, että kumpiakin käytetään julkisen rajapinnan avulla. Lisäksi, jos luokka muodostaa selkeän osakokonaisuuden ohjelmasta, se on usein järkevää toteuttaa moduulina, kuten myöhemmissä esimerkeissä käy ilmi.
Miniesimerkki: geometrialaskut¶
Tutkitaan esimerkkiä, josta moduulien toteuttamisen mekaaniset perusyksityiskohdat käyvät ilmi. Tämän esimerkin ohjelma on yksinkertainen prototyyppi geometrialaskin-ohjelmalle. Se koostuu kahdesta moduulista: pääohjelmasta ja geometriset laskutoimitukset sisältävästä moduulista.
Pääohjelmamoduuli tiedostossa calculator.cpp
näyttää seuraavalta:
// Module: calculator / file: calculator.cpp
// Provides the main function for a geometry calculator.
#include "geometry.hh"
#include <iostream>
using namespace std;
int main() {
double dimension = 0.0;
cout << "Input the length of the side of a square: ";
cin >> dimension;
cout << "Perimeter: " << square_perimeter(dimension) << endl
<< "Area: " << square_area(dimension) << endl;
cout << "Input the radius of a circle: ";
cin >> dimension;
cout << "Perimeter: " << circle_perimeter(dimension) << endl
<< "Area: " << circle_area(dimension) << endl;
}
C++:ssa jokainen moduuli, joka tarjoaa julkisessa rajapinnassaan palveluita muille moduuleille, tarvitsee esittelytiedoston (otsikkotiedosto, headerfile), jossa kuvataan kaikki ne palvelut, jotka kuuluvat moduulin julkiseen rajapintaan.
Esimerkin ohjelmassa moduuli geometry
tarjoaa pääohjelmalle
valmiit funktiot ympärysmittojen ja pinta-alojen laskemiseksi
tarvittaville geometrisille kuvioille. Moduulin esittelytiedosto
koostuu siis pääohjelman tarvitsemien funktioiden esittelyistä:
// Module: geometry / file: geometry.hh
// Header file of module geometry: provides the declarations for functions
// needed in calculations concerning geometric shapes
#ifndef GEOMETRY_HH
#define GEOMETRY_HH
double square_perimeter(double side);
double square_area(double side);
double circle_perimeter(double radius);
double circle_area(double radius);
#endif // GEOMETRY_HH
Viimeistään tässä vaiheessa on syytä panna merkille, kuinka
pääohjelmamoduulissa (calculator.cpp
) on rivi
#include "geometry.hh"
Tämä on esiprosessorin direktiivi, jolla C++-kääntäjälle kerrotaan
moduulin käyttävän palveluita toisen moduulin julkisesta rajapinnasta.
Kun edellä mainittu #include
-rivi on lisätty
calculator.cpp
-tiedoston alkuun,
voidaan main
-funktiossa kutsua geometry.hh
:ssa esiteltyjä
funktioita, vaikka niiden määrittely onkin jossakin muualla.
Esiprosessori on kääntäjän osa, joka valmistelee tiedostot ennen
varsinaista käännöstä. Esiprosessori tekee oikeastaan vain
tekstikorvausta, eli rivin #include "geometry.hh"
tilalle
esiprosessori korvaa tiedoston geometry.hh
sisällön.
Koska #include
ainoastaan liittää tiedostot osaksi käännettävää
tiedostoa, on mahdollista, että jokin tiedosto tulee mukaan useampaan
kertaan ja päällekkäiset määrittelyt estävät kääntämisen.
Ongelma ratkaistaan kirjoittamalla
otsikkotiedostoon direktiivit #ifndef
, #define
ja #endif
yllä olevan koodin mukaisesti.
Ne kertovat esiprosessorille, että tiedoston sisältö liitetään ainoastaan
kerran käännettävään tiedostoon riippumatta siitä, kuinka monta kertaa
sitä yritetään ottaa mukaan.
Esimerkin mukaisesti #ifndef
- ja #define
-rivien perässä on
kyseessä olevan esittelytiedoston nimi isoilla kirjaimilla ja piste
alaviivaksi muutettuna.
Tämä on yleinen ohjelmointityylillinen menettely, jota kannattaa noudattaa.
Pääohjelmamoduulille ei yleensä ole tarpeen kirjoittaa omaa esittelytiedostoa, koska se ei tarjoa palveluita muille moduuleille.
Toteutustiedosto geometry.cpp
sisältää moduulin julkisessa
rajapinnassa esiteltyjen funktioiden määrittelyt sekä niiden
tarvitsemat yksityisen rajapinnan apupalvelut:
// Module: geometry / file: geometry.cpp
// Implementation file of module geometry: provides the implementations
// for functions needed in calculations concerning geometric shapes
const double PI = 3.141593;
double square_perimeter(double side) {
return 4 * side;
}
double square_area(double side) {
return side * side;
}
double circle_perimeter(double radius) {
return 2 * PI * radius;
}
double circle_area(double radius) {
return PI * radius * radius;
}
Geometrialaskimen käännösvaiheet¶
Qt Creator huolehtii siitä, että ohjelmat käännetään oikealla tavalla. Tutkitaan nyt tarkemmin, mitä vaiheita ohjelman kääntämisessä on. Käännetään edellä esitelty geometrialaskin komentorivillä, jolloin käännöksen kaikki vaiheet tulevat paremmin esiin.
Kopioi hakemisto examples/11/geometry
kääntämistä varten
student
-hakemiston puolelle. Huomaat, että kyseisessä hakemistossa
ei ole Qt Creatorin .pro
-tiedostoa.
Kaikkein yksinkertaisimmalla tavalla saat ohjelman käännettyä siirtymällä hakemistoon, jossa tiedostot ovat ja kirjoittamalla komennon:
g++ calculator.cpp geometry.cpp
Huomaa, että käännöskomentoon ei kirjoiteta ollenkaan tiedostoa
geometry.hh
. Miksi ei?
Kyseinen tiedosto tulee mukaan käännökseen siten, että esiprosessori
sisällyttää sen sisällön tiedostoon calculator.cpp
.
Jos otat komennon suorittamisen jälkeen tiedostolistauksen, näet, että
hakemistosta löytyy tiedosto nimeltä a.out
. Tämä on suoritettava
ohjelma (binääri). Saat ohjelman suoritettua komentorivillä
kirjoittamalla komennon:
./a.out
Tämä yksinkertaisin käännöstapa ei kuitenkaan paljasta käännöksen
vaiheista paljon sen enempää kuin Qt Creatorissa kääntäminenkään.
Poista binääritiedosto (komento, jota tarvitset on rm
), ja
aloitetaan kääntäminen alusta vaiheittain.
Ensimmäiseksi käännetään vain tiedosto geometry.cpp
. Tämä
tapahtuu komennolla:
g++ -c geometry.cpp
Tässä komentoriviparametri -c
(compile) kertoo kääntäjälle, että
tehdään vain käännös, ei linkitetä. Kun otat tämän jälkeen
tiedostolistauksen, huomaat, että hakemistoon on ilmestynyt tiedosto
geometry.o
. Tämä tietosto sisältää konekoodia, mutta sitä ei voi
yksinään suorittaa, koska se ei sisällä kokonaista ohjelmaa.
Kuten tiedät, tiedosto
geometry.cpp
ei sisällä esimerkiksi main
-funktiota ollenkaan.
Seuraavaksi käännetään vain tiedosto calculator.cpp
samalla tavoin
komennolla:
g++ -c calculator.cpp
Tämän jälkeen hakemistosta löytyy kaksi .o
-päätteistä
objektitiedostoa.
Käännöksen viimeinen vaihe on objektitiedostojen yhdistäminen suoritettavaksi tiedostoksi. Tämän saa tehtyä komennolla:
g++ calculator.o geometry.o
Jos binäärin haluaa nimetä joksikin muuksi kuin a.out
, voi
käännöskomennolle antaa parametrin -o nimi
, esimerkiksi:
g++ -o calculator calculator.o geometry.o
Isoja ohjelmia käännettäessä voi olla selkeämpää erottaa käännös- ja linkitysvaiheet toisistaan. Näin ohjelmoija tietää koko ajan käännöstä suorittaessaan, kummasta vaiheesta virheilmoitukset tulevat.
Qt Creator käyttää ohjelmaa nimeltä make
käännöksen
automatisoimisessa. Sitä voi käyttää myös komentoriviltä. Jos
päädyt joskus tekemään projektia, joka käännetään ja suoritetaan
komentoriviltä, kannattaa make
-ohjelman toimintaan perehtyä huolella.
Monimutkaisempi esimerkki: bussiaikataulut¶
Tutkitaan hakemistosta examples/11/bus_timetables
löytyvää
ohjelmaa, joka osaa kellonajan ja bussinumeron kysyttyään tulostaa
kolmen seuraavan kyseisen numeroisen bussin lähtöajat. (Tässäkään
hakemistossa ei ole valmiina Qt Creatorin .pro
-tiedostoa, eli saat
harjoitella ohjelman kääntämistä komentoriviltä.)
Ohjelma koostuu kolmesta moduulista:
timetable.cpp
Pääohjelmamoduuli sisältää aikataulutietorakenteen alustuksen, hyvin yksinkertaisen käyttöliittymän ja käyttäjän syöttämien kellonajan ja bussivuoron perusteella suoritettavan bussivuorojen haun tietorakenteesta.
Se käyttää hyväkseen sekä moduulin
time
että moduulinutilities
julkisen rajapinnan tarjoamia palveluita.
time.hh
+ time.cpp
Moduuli määrittelee luokan
Time
, joka mahdollistaa kellonaikojen käsittelyn: alustuksen, asettamisen, näppäimistöltä lukemisen, tulostamisen ja pienempi–yhtäsuuri -vertailun suorittamisen kahdelleTime
-tyyppiselle oliolle.Se käyttää hyväkseen moduulin
utilities
julkisen rajapinnan palveluita.
utilities.hh
+ utilities.cpp
Moduuli tarjoaa funktiot merkkijonon muuttamiseksi kokonaisluvuksi ja kokonaisluvun lukemiseksi näppäimistöltä. Moduulin tarjoamat palvelut ovat jossain määrin sekalaisia funktioita, joille ei ollut muuta sopivampaa moduulia.
Se ei hyödynnä muiden moduulien tarjoamia palveluita.
Ohjelma saattaa vaikuttaa aluksi monimutkaiselta, mutta se ei muutamaa
kohtaa lukuunottamatta sisällä mitään uutta. Uutta asiaa sisältävät
kohdat on merkitty kommentilla //***
ja ne käydään läpi tässä:
Tiedoston timetable.cpp
rivi 52
Kannattaa muistaa, että helpoin tulkinta viitteelle on lisänimen antaminen olemassa olevalle muuttujalla. Tästä näkökulmasta ajateltuna määrittely
const vector<Time>& timevec = iter->second;tarkoittaa vain sitä, että jatkossa nimeä
timevec
voidaan käyttää tarkoittamaan samaa kuiniter->second
. Määrittelyn avulla koodia on saatu selkeytettyä ja kirjoittamisen vaivaa vähennettyä.Sana
const
on mukana siksi, ettäiter
osoittaaconst
-säiliööntimetable
, jota ei voi käsitellä kuin vakiona (const
).
Tiedoston utilities.cpp
rivi 7
Rivillä on varattu sana
namespace
, jonka avulla moduulille saadaan muodostettua yksityinen rajapinta. Sen palveluihin päästään käsiksi vain saman moduulin (lähdekooditiedoston) sisältä.Kaikkia esittelyjä ja määrittelyjä, jotka ovat varattua sanaa
namespace
seuraavien aaltosulkeiden sisällä, voidaan käyttää vainutilities.cpp
-tiedostossa.Nimetön nimiavaruus on moduulimekanismin vastike luokkien
private
-osalle.
Tiedoston time.cpp
rivi 11
Tämä on ensimmäinen esimerkki luokan rakentajasta, jossa olion alustus ei tapahdu alustuslistassa, vaan se tehdään rakentajafunktion rungossa olevien käskyjen avulla. Tässä ei sinällään ole mitään kummallista, sillä rakentaja on funktio, jonka rungossa voi tarvittaessa olla käskyjä.
Kuitenkin kurssin tässä vaiheessa saavutetulla osaamistasolla
Time
-luokan rakentajan toteutukseen liittyy ongelma: Mitä tulisi tehdä, jos parametritime
on virheellinen? Funktioset_value
palauttaa kyllä siinä tilanteessa arvonfalse
, mutta rakentaja ei voi tehdä arvolla mitään, koska sillä itsellään ei ole paluuarvoa.Oikea ratkaisu olisi poikkeuksen aiheuttaminen, kuten Pythonissa samankaltaisissa tilanteissa tehtiin. Sitä vain ei osata C++:lla vielä tehdä (vaan asia opitaan vasta seuraavalla ohjelmointikurssilla).
Muuta täysin uutta asiaa esimerkissä ei pitäisi olla. Olkoonkin, että aiemmin opittuja mekanismeja on saatettu käyttää luovilla tavoilla, joten esimerkkiin kannattaa tutustua ajatuksen kanssa.
Moduulin julkinen rajapinta (.hh-tiedosto)¶
Moduulin julkinen rajapinta (siis moduulin toisille moduuleille
tarjoamat palvelut) kirjataan moduulin esittelytiedostoon, jonka nimi
perinteisesti päättyy C++:ssa .hh
-kirjainyhdistelmään.
Julkinen rajapinta voi sisältää:
- funktioiden esittelyjä
const
-vakioiden määrittelyjä- uusien tietotyyppien (myös luokkien) määrittelyjä
- mitä tahansa yhdistelmiä edellisistä.
Julkinen rajapinta ei saa sisältää:
- muuttujien määrittelyjä
- funktioiden tai luokan metodien määrittelyjä.
Jokaiseen lähdekooditiedostoon, joka tarvitsee jotain palvelua toisen moduulin julkisesta rajapinnasta, lisätään rivi
#include "palvelun_tarjoavan_moduulin_nimi.hh"
Vaikka esimerkkikoodissa ei näin ollut tarvetta tehdä, edellä mainittu
#include
-rivi saattaa olla tarpeen laittaa myös jonkun moduulin
.hh
-tiedostoon. Millaisessa tilanteessa näin voisi käydä?
Samoin on yleistä, että moduulin .cpp
-tiedosto joutuu tekemään
include-operaation omalle .hh
-tiedostolleen (vrt. esimerkin tiedosto
time.cpp
). Miksi näin on pitänyt tehdä?
Direktiiviä #include
ei saa käyttää siihen, että sen avulla
otettaisiin käyttöön .cpp
-tiedostoja. Vaikka tämä joissain
tilainteissa saattaakin toimia, se kuvaa ohjelmoijan syvää
ymmärtämättömyyttä moduulimekanismin toiminnasta.
Ohjelmointityylillisesti pidetään hyvänä käytäntönä sitä, että otettaessa käyttöön sekä ohjelmoijan omia moduuleja että systeemin standardikirjastoja, luetellaan omat moduulit ensin:
#include "omamoduuli-1.hh"
...
#include "omamoduuli-n.hh"
#include <standardikirjasto-1>
...
#include <standardikirjasto-m>
Tällä tavalla kääntäjä saadaan tarkistamaan, että itse kirjoitetut moduulit
sisältävät kaikki tarvittavat include
-tiedostot.
Toisin sanoen voidaan varmistua siitä, omat moduulit muodostavat
itsenäisesti kääntyvän käännösyksikön.
Monimutkaisten ohjelmien kanssa eteen tulee usein vastaan tilanne,
jossa yksi ja sama .hh
-tiedosto saattaa tulla otetuksi käyttöön
useita kertoja, kun eri lähdekooditiedostot suorittavat sille
#include
-käskyn omien tarpeidensa täyttämiseksi.
Ongelman ratkaisu esitettiin tämän materiaaliosion alkupäässä,
kun käsiteltiin miniesimerkkiä geometrialaskuista.
Moduulin yksityinen rajapinta (.cpp-tiedosto)¶
Toteutus- eli .cpp
-tiedostossa määritellään kaikki ne funktiot ja
metodit, jotka esittelytiedostossa on kuvattu moduulin julkiseksi
rajapinnaksi. Toteutustiedosto voi toki sisältää mitä tahansa koodia,
jota julkisen rajapinnan funktioiden toteuttamisen avuksi tarvitaan.
Jos .cpp
-tiedostossa halutaan toteuttaa moduulin omia
apufunktioita, joita ei voida edes kikkailemalla kutsua muista
moduuleista, ne on esiteltävä ja määriteltävä nimettömän nimiavaruuden
sisällä:
namespace { // Esittelyosa
void moduulin_yksityinen_funktio();
...
}
...
// julkisen rajapinnan funktiomäärittelyt tässä
...
namespace { // Määrittelyosa
void moduulin_yksityinen_funktio() {
...
}
...
}
Esittelyosa voi tuttuun tapaan puuttua, jos funktioiden määrittelyt järjestetään niin, että kääntäjä on nähnyt funktioiden määrittelyt ennen kuin niitä yritetään kutsua.
Käytännössä siis määrittelyosa kirjoitetaan tiedoston alkuun, kuten
esimerkkiohjelman utilities.cpp
-tiedostossa oli tehty
riviltä 7 alkaen.
Edellä esitetty nimetön nimiavaruus -mekanismi pätee vain normaaleihin
funktioihin.
Jos taas .cpp
-tiedostossa määritellään luokan metodeita,
namespace
-mekanismilla ei ole merkitystä, koska luokka huolehtii
omista rajapinta/näkyvyysaluejärjestelyistään public
- ja
private
-mekanismin avulla.
Jos moduulin julkisessa rajapinnassa (.hh
-tiedosto) on määritelty
const
-vakioita tai tietotyyppejä, joita tarvitaan myös moduulin
.cpp
-tiedostossa, joudutaan tekemään #include
moduulin omalle
.hh
-tiedostolle.
Ohjelmoija voi halutessaan määritellä myös nimettyjä nimiavaruuksia:
namespace mun_avaruus {
void mun_funktio() {
...
}
}
Tällaisia funktioita voidaan kutsua kahdella tavalla joko
mun_avaruus::mun_funktio();
tai lisäämällä kooditiedostoon using namespace
-käskyn
using namespace mun_avaruus;
...
mun_funktio();
Nimetyn nimiavaruuden toteuttamiseen kurssilla tuskin tulee tarvetta, mutta kyseessä on mukava yleissivistävä selitys sille, miksi toisinaan ohjelmakooditiedostoihin on kirjoitettu
using namespace std;
Nimettyjen nimiavaruuksien avulla voidaan välttää nimikonflikteja.
Esimerkiksi tunniste nimi
voisi opintorekisteriohjelmassa olla
käytössä sekä opiskelijan tiedoissa että opintojakson tiedoissa. Kun
molemmat näistä olisi toteutettu omissa nimiavaruuksissaan,
tunnisteisiin päästäisiin käsiksi kirjoittamalla Opiskelija::nimi
tai
Opintojakso::nimi
.
Moduulien suunnittelusta¶
Oikeassa ohjelmointiprojektissa ohjelman jako moduuleihin tehdään ohjelman suunnitteluvaiheessa. Periaatteessa moduulit löytää miettimällä, mistä loogisista osakokonaisuuksista toteutettava ohjelma koostuu.
Esimerkkinä käytetty bussiaikatauluohjelma on lähes lapsellisen yksinkertainen ja pieni, jotta moduulijaolla varsinaisesti saavutettiin mitään hyötyä, mutta silti asiaa miettimällä (jo ennen koodauksen aloittamista) päällimäisiksi ajatuksiksi kohosi:
- Ohjelman pitää pystyä käsittelemään ja vertailemaan kellonaikoja.
- Koska eri muodossa esitettyjä numeroita pitää pystyä lukemaan näppäimistöltä ja suorittaa muutoksia merkkijonojen ja numeroiden välillä, tuntui aika selvältä, että tarvitaan jonkinlainen joukko funktioita käyttäjän syöttämien numeroiden käsittelyyn.
- Myös hyvin yksinkertainen käyttöliittymä tarvittaisiin.
- Lisäksi tarvitaan algoritmi, joka osaa hakea sopivat bussivuorot ohjelman käyttämästä tietorakenteesta.
Lopulta, lähinnä esitysteknisistä syistä, käyttöliittymä ja hakualgoritmi päätettiin toteuttaa pääohjelmamoduulina ja kaksi muuta kokonaisuutta omina moduuleinaan, jolloin lopputuloksena ohjelman moduulijaoksi saatiin:
- pääohjelmamoduuli (
timetable
) - ajankäsittelymoduuli (
time
) - apufunktiomoduuli numeroiden käsittelyyn (
utilities
).
Moduulien etsinnässä siis on päämääränä jakaa ohjelma osiin, joista kukin:
- toteuttaa selkeästi rajatun osan kokonaisuudesta ja
- on riittävän yksinkertainen (mitä jos ei ole?).
Jos ongelma on isohko, kannattaa moduuleita yrittää etsiä pilkkomalla ongelmaa ja saatuja osaongelmia toistuvasti pienempiin osiin, kunnes lopulta päädytään niin pieniin osaongelmiin, että ne ovat helposti hallittavissa. Tällaista lähestymistapaa kutsutaan top-down-menetelmäksi.
Seuraavassa listassa luetellaan yleisiä suuntaviivoja kokonaisuuksista, jotka tyypillisesti kannattaa toteuttaa moduuleina sen kokoluokan ohjelmissa, joihin kurssilla törmätään:
- luokka
- pääohjelma
- käyttöliittymä/monimutkaisen syötteen käsittely
- tiedoston lukeminen ja jäsentely
- yleiskäyttöiset algoritmit (haku, lajittelu).
Oma mielikuvitus auttaa myös paljon.
Huomaa, että bussiaikatauluohjelman moduulit olisi voitu toteuttaa myös luokkina, joilla on omat julkiset ja yksityiset rajapinnat. Luokat ovat tyypillisin esimerkki moduuleista C++-ohjelmoinnissa.
Moduulien julkisten rajapintojen suunnittelu on haastavampaa kuin moduulijaon suunnittelu.
Varsinkin kokemuksen karttuessa ainakin suurpiirteisen moduulijaon sen kokoisissa ohjelmissa, joihin peruskursseilla törmätään, näkee lähes suoraan.
Julkisen rajapinnan suunnittelussa täytyy ottaa jo huomattavasti syvällisemmin kantaa siihen, mitä palveluita ja miten moduulin on tarkoitus tarjota muille. Jotta tämän voi tehdä edes jollain tasolla menestyksellisesti, ohjelman rakennetta ja toteutusperiaatteita on tarpeen miettiä melkoisen tarkasti.
Jos rajapinta on suunniteltu ideaalisen onnistuneesti, niin lopputuloksena kukin moduuli tarjoaa palveluita muille moduuleille julkisessa rajapinnassaan, jota ei periaatteessa ole tarpeen muokata projektin edetessä. Näin onnistuneeseen lopputulokseen päästään harvoin. Käytännössä projektin edetessä tulee aina vastaan tilanteita, joissa ymmärretään, että rajapinnassa on päädytty tekemään jotain huonosti tai siitä on unohtunut jotain aivan kokonaan. Näissä tilanteissa rajapintoja joudutaan muuttamaan, mikä saattaa olla kallista, jos puutteellisen rajapinnan pohjalta on ehditty jo kirjoittamaan paljon koodia. Muutokset rajapintaan heijastuvat muutoksina kaikkialle, missä sitä käytetään.
Moduulaarisuudella saavutettavat hyödyt¶
Modulaarisuuden hyödyt ovat pitkälti samoja kuin luokkien hyödyt, koska ne juontavat juurensa pääosin rajapintojen käytöstä:
- Moduulin toteutusta (siis
.cpp
-tiedostoa eli yksityistä rajapintaa) voidaan muuttaa julkisen rajapinnan säilyessä ennallaan. - Ohjelmiston loogisesti yhteenkuuluvat osat voidaan koota samaan pakettiin, mikä selkeyttää ohjelmaa ja helpottaa sen testaamista ja ylläpitoa.
- Moduuleita voidaan kehittää projektissa rinnakkain sen jälkeen, kun julkinen rajapinta on sovittu.
- Modulaarisuus on hyvä työkalu suurien ohjelmointiprojektien hallintaan.
- Usein moduuleita voidaan uudelleenkäyttää joko kokonaan tai ainakin osittain.
- Useimmat modulaarisuutta tukevat ohjelmointikielet mahdollistavat moduulien kääntämisen erikseen. Tämä nopeuttaa kehitystyötä ja kuluttaa vähemmän resursseja, sillä muutosten jälkeen vain muuttuneet moduulit tarvitsee kääntää uudelleen.