Complexitatea cognitiva si complexitatea cyclomatica

Stefanescu Mihai 9 months ago Clean Code

Atat timp cat scriem cod in mod profesional, metricile sunt foarte importante pentru a determina daca scriem cod de calitate ce este usor de testat, inteles si intretinut pe termen lung. Cum fiecare dezvoltator are stilul lui de a scrie devine important sa setam un standard dupa care sa masuram daca acel cod este sau nu de calitate. Doua dintre cele mai relevante metrici pe care ar trebui sa le avem in calcul sunt complexitatea cognitiva si cyclomatica.

Complexitatea Cyclomatica si fratele ei mai mic, Complexitatea Cognitiva

Conceptul de complexitate cognitiva a fost adus de SonarQube. Ai vrut sa introduca un mod de masurare a complexitati codului mai contextual. Stiu, exista mai multe surse din care puteti citi despre complexitatea cognitiva, inclusiv acest whitepaper de la sonar. In schimb, in acest articol am sa incerc sa povestesc cat mai simplu ca sa fie cat se poate de usor de inteles.

Complexitatea Cyclomatica

Aceasta metrica masoara cat de greu este de testat codul. Mai exact, calculeaza numarul de test case-uri distincte de care ai nevoie pentru a avea acoperire de 100% in testele unitare.

Complexitatea Cognitiva

Masoara cat de greu este de citit si inteles codul.
Hai sa incerc sa explic cu o bucata de cod:

switch ($input) {
	case 1:
		// rulam o anumita logica de business
		break;
	default:
		// rulam alta logica de business
		break;
}

Complexitatea cyclomatica a acestui cod este de 2. Ajungem la aceasta concluzie pentru ca ai 2 testcase-uri, cate unul pentru fiecare case din switch. Desi acest exemplu este simplist, extrapoland putem intelege ca impreuna cu restul codului din functia in care este acest switch si codul din case-uri acest rezultat poate creste in mod liniar.
Am sa las mai jos un tabel cu un guideline al complexitatilor si riscurilor modificarii codului ulterior:

Scor Complexitate Cyclomatica Risk
de la 1 la 10 Simpla Fara riscuri
de la 11 la 20 Complexa Anumite riscuri
de la 21 la 50 Mult prea complexa Risc mediu, necesita atentie
mai mult de 50 Mult prea complexa Prea multe cazuri de testare, risc mare

Desi avem o complexitate cyclomatica de 2, complexitatea cognitiva pentru acest cod este de 1 (sa ne amintim ca complexitatea cognitiva indica cat de usor de inteles este codul), deci sa citesti un switch cu 2 case-uri este relativ usor de citit si inteles.

Hai sa mai luam cateva exemple care sa ne ajute sa intelegem mai bine diferenta dintre cele 2. Am sa iau ca exemplu un program simplu care va decide daca un input dat este sau nu un an bisect sau nu.
Pentru a decide daca un an este bisect sau nu trebuie sa vedem daca este divizibil cu 4, de asemenea, daca este divizibil cu 100, dar nu cu 400, iar apoi, daca este divizibil cu 400.

Testaces-urile noastre sunt urmatoarele:

An bisesc : 1980, 1996, 2000, 2400 etc.

An care nu este bisect : 1981, 1995, 1700, 1900 etc.

Prima varianta a acestui cod:

public function  isLeapYear(int $year)
{
	$response = false;

	if ($year >= 1000 && $year <= 9999) {
		// anul este divizibil cu 400, ex: 1600, 2000, etc
		if ($year % 400 == 0){
			$response = true;
		// anul este divizibil cu 100, dar nu cu 400, ex: 1700, 1900, etc
		} elseif (($year % 100 == 0) && ($year % 400 != 0)) {
			$response = false;
		// in cele din urma, daca primele 2 conditii nu sunt indeplinite
		// verificam daca este divizibil cu 4
		}elseif ($year % 4 == 0) {
			$response = true;
		}
	} else {
		throw new Exception("Anul data ca parametru trebuie sa fie intre 1000 si 9999");
	}

	return $response;
}

Complexitatea cyclomatica pentru aceasta functie este de 7, decide asta uitandu-se la fiecare bucata de cod subliniata cu rosu din imaginea de mai jos

In schimb, pentru complexitatea cognitiva masuratoarea se face altfel:

Pentru fiecare conditie mai creste complexitatea cu 1, daca exista o despartire a conditiei, de exemplu && si || nou complexitatea este crescuta. Daca Avem 5 conditii ce contin &&, atunci scorul va fi de 1, in schimb  daca avem combinatii de && si ||, pentru fiecare dintre ele scorul va fi crescut.

Deci, ce putem face pentru a scade acest scor? Daca ne uitam mai atent la cod, observa, ca avem operatii care se repeta si in urma carora luam o decizie. Deci, daca facem o cunftie noua checkDivisibility() care sa contina aceasta logica, am putea reduce numarul de conditii.

pubic function checkDivisibility(int $year, int $dividedBy, bool $cont) {
	$response = false;
    // ne folosim de $cont pentru a determina daca conditia precedenta a fost indeplinita
    // si mergem mai departe doar daca a fost indeplinita
	if ($cont) {
		$remainder = $year % $dividedBy;
		if ($remainder == 0) {
			$response = true;
		}
	}
	return $response;
}

Acum, in functia care determina daca anul este sau nu bisect putem face urmatoarele modificari:

public function  isLeapYear(int $year)
{
	$response = false;

	if ($year >= 1000 && $year <= 9999) {
		$isDivisibleByFour = $this->checkDivisibility($year, 4, true);
		$isDivisibleByOneHundred = $this->checkDivisibility($year, 100, $isDivisibleByFour);
		$isDivisibleByFourHundred = $this->checkDivisibility($year, 400, $isDivisibleByOneHundred);

		$response = ($isDivisibleByFour && !($isDivisibleByOneHundred)) || $isDivisibleByFourHundred;
	} else {
		throw new Exception("Anul data ca parametru trebuie sa fie intre 1000 si 9999");
	}

	return $response;
}

Acum, putem observa cum am reusit sa reducem complexitatea cyclomatica (liniile rosii) cat si pe cea cognitiva (liniile verzi)

Ba chiar mai mult, putem reduce complexitatea cyclomatica si mai mult mutand verificarea range-ului de ani (1000 - 9999) intr-o functie separata.

public function validateRange(int $year)
{
	if ($year >= 1000 && $year <= 9999) {
		return true;
	}
	
	return false;
}

iar functia care principala va deveni:

public function  isLeapYear(int $year)
{
	if (!$this->validateRange($year)) {
		throw new Exception("Anul data ca parametru trebuie sa fie intre 1000 si 9999");
	}

	$isDivisibleByFour = $this->checkDivisibility($year, 4, true);
	$isDivisibleByOneHundred = $this->checkDivisibility($year, 100, $isDivisibleByFour);
	$isDivisibleByFourHundred = $this->checkDivisibility($year, 400, $isDivisibleByOneHundred);

	return ($isDivisibleByFour && !($isDivisibleByOneHundred)) || $isDivisibleByFourHundred;
}

Concluzie
1. Incearca sa scrii metode cat mai mici care sa faca un singur lucru.
2. Profita de utilitatile pe care ti le pune la dispozitie limbajul in care lucrezi.

Programator de ~8 ani, am lucrat la proiecte din mai multe industrstrii, de la eCommerce la telecomunicatii la automatizari. In acest timp am folosi diferite tehnologii, de la PHP, MySQL, PostgreSql, RabbitMq, Redis, Memcached si altele.


Get in touch
Pentru nelamuriri, dubii, comentarii si chestii de pe suflet ne putem auzi pe Discord, Reddit sau poti deschide o discutie noua pe forum

Diane # 4 weeks ago I visited multiple sites however the audio feature for audio songs current at this web site is really wonderful.
Coral # 4 weeks ago With havin so much written content do you ever run into any problems of plagorism or copyright infringement? My site has a lot of unique content I've either written myself or outsourced but it appears a lot of it is popping it up all over the web without my agreement. Do you know any methods to help stop content from being ripped off? I'd really appreciate it.
Glory # 3 weeks ago Hello there, just became aware of your blog through Google, and found that it's really informative. I'm going to watch out for brussels. I'll appreciate if you continue this in future. A lot of people will be benefited from your writing. Cheers!
montelukast rezeptfrei erhältlich in der Schweiz # 3 weeks ago That is really attention-grabbing, You're an excessively professional blogger. I have joined your feed and stay up for searching for extra of your fantastic post. Also, I have shared your site in my social networks
Phil # 3 weeks ago An interesting discussion is worth comment. I believe that you ought to publish more on this issue, it might not be a taboo subject but generally people do not speak about these issues. To the next! Cheers!!
Omer # 3 weeks ago I think this is among the most vital information for me. And i'm happy studying your article. But wanna statement on some basic things, The website taste is ideal, the articles is actually great : D. Just right process, cheers
clarinex 5 mg ohne Rezept in Deutschland finden # 2 weeks ago If you wish for to take much from this post then you have to apply these methods to your won weblog.
cetirizine 10 mg in der Apotheke erhältlich # 2 weeks ago Post writing is also a excitement, if you be acquainted with after that you can write otherwise it is complicated to write.
???????? ??? ?????? ? ???????? ?????????????? ??????????? # 2 weeks ago Very great post. I simply stumbled upon your blog and wished to say that I have really enjoyed browsing your weblog posts. In any case I will be subscribing for your feed and I hope you write once more very soon!
Preis von Medikamenten in der Apotheke # 2 weeks ago Hi there, I enjoy reading all of your post. I like to write a little comment to support you.
Freiverkäufliches acillin 250 mg in Deutschland # 1 week ago Hi, i read your blog from time to time and i own a similar one and i was just wondering if you get a lot of spam responses? If so how do you stop it, any plugin or anything you can suggest? I get so much lately it's driving me mad so any support is very much appreciated.
venta libre de venlafaxine 37.5 mg en España # 1 week ago When I initially commented I clicked the "Notify me when new comments are added" checkbox and now each time a comment is added I get several emails with the same comment. Is there any way you can remove me from that service? Thanks a lot!
Fordern Sie cefixime mit ärztlichem Rezept an # 1 week ago Hey! I just wanted to ask if you ever have any problems with hackers? My last blog (wordpress) was hacked and I ended up losing a few months of hard work due to no data backup. Do you have any solutions to prevent hackers?
nasonex met of zonder recept # 1 week ago Hello to all, it's in fact a nice for me to pay a visit this web page, it consists of important Information.
duagen ohne Verschreibung erhältlich in Deutschland # 1 week ago I think the admin of this site is really working hard in favor of his site, for the reason that here every material is quality based information.
consulta con un médico para obtener una receta de dicyclomine 100 pills en Quito # 5 days ago For hottest news you have to go to see web and on web I found this web page as a most excellent website for newest updates.
balcoga disponibile senza prescrizione medica # 3 days ago bookmarked!!, I really like your blog!
acheter altace sans risque # 2 days ago I really like it when individuals get together and share views. Great blog, stick with it!
alfadil in der Apotheke von Brüssel # 2 days ago Hello There. I discovered your weblog using msn. This is a really neatly written article. I will be sure to bookmark it and come back to learn extra of your helpful information. Thank you for the post. I'll definitely comeback.
cena bisacodyl w Krakowie # 1 hour ago Thank you for another informative blog. Where else may just I am getting that kind of information written in such a perfect way? I've a undertaking that I'm just now operating on, and I've been at the glance out for such information.
Club-ul este dedicat membrilor si ofera access la mai multe zone ale website-ului.
Login Register

🔖 Bookmarks
✨ Pentru a sustine aceasta comunitate am sa te rog sa te autentifici sau sa te inregistrezi!

🌪️ Discord
Back to top
Folosim cookie-uri pentru a oferi functionalitatile critice ale aplicatiei Invata-Programare. Folosim cookie-uri si pentru a analiza traficul, pentru care e nevoie de consimtamantul dvs. explicit.