Iga süsteem on ehitatud domeenipõhisele keelele, mis on disainitud, et programmeeriad kirjeldaksid seda süsteemi. Funktsioonid on selle keele tegusõnad ja klassid on nimisõnad.
80ndatel öeldi, et funktsioon ei tohiks olla suurem kui ekraanile mahub. Funktsioon ei tohiks olla 100 rida pikk. Harva on juhuseid, kus funktsioon peaks olema 20 rida pikk.
FUNKTSIOONID PEAKSID TEGEMA AINULT ÜHTE ASJA. NAD PEAKSID SEDA TEGEMA HÄSTI. NAD PEAKSID TEGEMA AINULT SEDA.
Me võime kirjeldada funtskooni lühikese ET sõnaga algava lausega.
- TO RenderPageWithSetupAndTeardowns, we check to see whether the page is a test page and if so, we include the setups and teardowns. In either case we render the page in HTML.
Kui funktsioon teeb ainult neid samme, mis on üks tase funktsiooni nimest allpool, siis funktsioon teeb ainult ühte asja.
Kas funktsioon teeb ühte või kolme asja? Pane tähele, et kolm funtsiooni sammu on kõik üks abstraktsiooniaste allpool funktsiooni nimest.
Me tahame funtsiooni lugeda nagu ülevalt-alla jutustust. Me tahame, et igale funktsioonile järgneksid funktsioonid, mis on sellest aste allpool – nii me saame lugeda programmi, üks abstraktsiooni tase korraga, kui me loeme funktsioonide listi laskuvalt.
Tehes koodi lugemine astmelisteks ET paragrahvideks on efektiivne meetod hoidmaks abstraktsioonde tase konstante. Nimetagem seda “trepist laskumise reegliks” (The Stepdown Rule).
- TO include the setups and teardowns, we include setups, then we include the test page content, and then we include the teardowns.
- TO include the setups, we include the suite setup if this is a suite, then we include the regular setup.
- To include the suite setup, we search the parent hierarchy for the “SuiteSetUp” page and add an include statement with the path of that page.
- TO search the parent…
Ärgem kartkem nimetada funktsioone pikkade nimedega. Pikk seletav nimi on parem kui lühike ja salapärane. Pikk seletav nimi on parem kui pikk seletav kommentaar.
Seletava nime valimine aitab peas paremini mõista mooduli disaini ja seda parandada. Pole üldse haruldane, et parema funktsiooninime otsingul jõutakse koodi soovitud ümberehitamiseni.
Lipu argumendid on inetud. Kahendmuutuja funktsioonile kaasa andmine on tõeliselt kohutav praktika. See koheselt tõstab meetodi keerukust ning valjult kisendab, et funktsioon teeb rohkem kui ühte asja. See teeb ühte asja siis, kui muutuja on tõene ja teist siis, kui muutuja on väär!
Enne objekt-orienteeritud programmeerimist oli mõnikord vajalik väljundparameetrite kaasa andmine argumentidena. Kui OO-programmeeerimise levides kadus see vajadus ära, sest this on mõeldud käituma väljundparameetrina. Teiste sõnadega, on parem kui appendFooter kutsutakse välja järmiselt:
retport.appendFooter();
Üldiselt tuleks väljundparameetreid vältida. Kui funktsioon peab muutma millegi olekut, siis las muudab selle olekut omanikust objekt.
Funktsioonid peaksid kas tegema midagi või vastama millegile, mitte mõlemat.
Eelista Veateateid Veakoodidele
if (deletePage(page) == E_OK) {
if (registry.deleteReference(page.name) == E_OK) {
if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {
logger.log('page deleted');
} else {
logger.log('configKey not deleted');
}
} else {
logger.log('deleteReference from registry failed');
}
} else {
logger.log('delete failed');
return E_ERROR;
}
… muutub selliseks …
try {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
catch (Exception e) {
logger.log(e.getMessage());
}
public void delete(Page page) {
try {
deletePageAndAllReferences(page);
}
catch (Exception e) {
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throws Exception {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessage());
}
Ülalpool olev kustutamise (delete) funktsioon tegeleb ainult veateadete töötlemisega. Seda on lihtne mõista ja ignoreerida. Funktsioon deletePageAndAllReferences tegelib ainult lehe täielikult kustutamisega. Veateadete haldamist võib ignoreerida. See tagab eraldatuse, mis võimaldab koodi lihstamalt mõista ja muuta.
Funktsioonid peaksid tegelema ühe asjaga. Veateadete haldamine on teine asi. Sellepärast ei tohiks funktsioon, mis haldab veateateid, tegelda millegi muuga. Sellet järeldub, et kui funtsiooni nimes on try sõna, siis see peaks olema funktsiooni kõige esimene rida ning funktsiooni lõpus ei tohiks midagi järgneda peale catch/finally plokki.
Koodikordused võib just olla kogu kurja alus tarkvara kirjutamises. Selle kontrollimiseks ja välja juurimiseks on loodud palju põhimõtteid ja praktikaid.
Siiski kui hoida funktsioonid väiksed, siis mõni üksik mitkekordne return, break või continue lause ei tekita kahju ja võib olla tähendusrikkam kui ühe sisendi ja ühe väljundi reegel.
Kuidas kirjutada funtksioone nagu ette nähtud?
Tarkvara kirjutamine on nagu mistahes muu kirjutamine. Kui sa kirjutad teaduspaberit, siis sa paned oma mõtted kõigepealt paberile, seejärel mudid neid senikaua, kuni need on hästi loetavad. Esimene mustand võib olla kohmakas ja halvasti organiseeritud, nii et sa kohendad ja kirjutad ümber sendikaua, kuni see väljendab hästi seda, mida tahtsid öelda.
Kui sa kirjutad funktsioone, siis need tulevad välja pikad ja kohmakad. Neis on palju taandeid ja üksteise see olevaid ahelaid. Neil on pikad argumentide jadad. Nimed on kunstlikud ning neis on koodikordusi. Kuid on ka ühikutestid, mis katavad kõiki neid kohmakaid ridasid.
Niisiis, kui ma mudin ja puhastan seda koodi, jagan väiksemateks funktsioonideks, muudan nimesid, juurin välja kordusi. Ma muudan meetodeid väiksemateks ning järjestan neid ringi. Mõnikord ma muudan terveid klasse samal ajal hoides teste rohelistena. Lõpuks ma saavutan funktsioonid, mis järgivad neid reegleid, mis siis peatükis on välja toodud.
Ma ei kirjuta neid ilusti esimese korraga. Ma ei usu, et keegi suudaks seda teha.