Czym jest złożoność cyklomatyczna?

Opublikowano: 08.02.2024 - tagi: Kod Jakość Metryka Refaktoring

Czytelny kod

Kod częściej się czyta, niż pisze. Więc logiczne jest to, że warto skupić się na pisaniu kodu, który jest łatwo zrozumiały dla programisty.

Na czytelność kodu składa się kilka czynników, jak na przykład: odpowiednie nazewnictwo, liczba linijek kodu itp.

Innym ważnym czynnikiem jest złożoność kodu. Im bardziej kod jest złożony, tym trudniej się go czyta.

Jedną z metryk, która pozwala na zmierzenie złożoności kodu, jest: złożoność cyklomatyczna.

Złożoność cyklomatyczna

Informuje ona o liczbie ścieżek prowadzących w kodzie. Mierzy się ja w funkcji/metodzie.

Dzięki tej informacji wiesz nie tylko, jak bardzo złożona jest funkcja/metoda, ale także o minimalnej ilości testów potrzebnych do napisania, aby w pełni pokryć kod testami.

Minimalna wartość złożoności cyklomatycznej wynosi: 1.

Ile to za dużo?

Do jakiej wartości złożoności cyklomatycznej powinniśmy dążyć? Nie ma na to jasnej odpowiedzi, ale im mniejsza wartość, tym lepiej.

Autor książki: Zrównoważony kod. Dobre praktyki i heurystyki dla inżynierów oprogramowaniaMark Seemann proponuje, żeby maksymalna wartość wynosiła 7. Dlaczego akurat 7? Argumentuje to tym, że mózg człowieka dysponuje pamięcią krótko terminową. Jest ona ograniczona. Kiedy programista natrafia w metodzie na wiele instrukcji warunkowych lub/i pętli stara się "ogarnąć", jak działa ten kod. Im bardziej jest on złożony, tym trudniej go zrozumieć. Dlatego warto ograniczyć złożoność cyklomatyczną do stałej wartości i się tego konsekwentnie trzymać.

Jeśli złożoność cyklomatyczna wynosi więcej niż ustalona wartość powinieneś zrefaktorować kod, w wyniku czego ta wartość się zmniejszy.

Jak to policzyć?

Złożoność cyklomatyczną liczy się w prosty sposób: zaczynasz od wartości 1, licznik zwiększasz za każdym, gdy napotkasz w kodzie: instrukcje warunkowe i pętle.

Sam proces liczenia złożoności cyklomatycznej warto zautomatyzować, instalując na przykład jakąś bibliotekę do statycznej analizy kodu.

Przykłady

Przykład 1

function doSomething() {
	console.log('Hello World!');
}

Złożoność cyklomatyczna wynosi: 1.

Przykład 2

function checkSomething(a) {
	if (a > 5) {
		return true;
	}
	return false;
}

Złożoność cyklomatyczna wynosi: 2.

Przykład 3

function iterate(max) {
	let counter = 1;
	while (counter <= max) {
		console.log(`counter: ${counter}`);
		counter++;
	}
}

Złożoność cyklomatyczna wynosi: 2.

Przykład 4

function getColorInHex(color) {
	switch(color) {
		case 'red':
		return '#ff0000';
		
		case 'green':
		return '#00ff00';
		
		case 'blue':
		return '#0000ff';
		
		default:
		return 'not found';
	}
}

Złożoność cyklomatyczna wynosi: 5.

W przypadku instrukcji switch liczymy wszystkie case i default co daje nam: 1 + 4 = 5.

Przykład 5

function sayHello(name) {
	let userName = name ?? 'stranger';
	console.log(`Hello ${userName}!`);
}

Złożoność cyklomatyczna wynosi: 2.


Podsumowanie: Styczeń 2024

Opublikowano: 31.01.2024 - tagi: Blog Podsumowanie Styczeń 2024

W styczniu opublikowałem 9 wpisów:


SwiftUI:

  1. Do czego służy @Binding?
  2. Do czego służy @StateObject?
  3. Do czego służy @ObservedObject?
  4. Do czego służy @Environment?
  5. Jak sprawdzić stan aplikacji?
  6. Do czego służy @EnvironmentObject?
  7. VStack, HStack, ZStack

iOS:

  1. iOS: Wszystkie stany aplikacji

MySQL:

  1. Jak zaimportować bazę danych z pliku?

Przeczytałem 1 książkę:

  1. Zrównoważony kod. Dobre praktyki i heurystyki dla inżynierów oprogramowania — Mark Seemann

Przesłuchałem 4 audiobooki:

  1. Drach — Szczepan Twardoch
  2. Demon Copperhead — Barbara Kingsolver
  3. Chwile wieczności — Kjersti Anfinnsen
  4. Wiatr — Jozef Karika

MySQL: Jak zaimportować bazę danych z pliku?

Opublikowano: 27.01.2024 - tagi: MySQL Baza danych Import Komenda Plik

Import bazy danych

Można to zrobić za pomocą jednej komendy:

mysql -u username -p database_name < file.sql

gdzie username to nazwa użytkownika za pomocą, który ma uprawnienia do importu.

Załóżmy, że chcemy przy pomocy użytkownika user zaimportować dane do bazy db plik o nazwie file.sql:

mysql -u user -p db < file.sql

SwiftUI: VStack, HStack, ZStack

Opublikowano: 20.01.2024 - tagi: SwiftUI Swift Widok Layout

Stosy w SwiftUI

Stosy (ang.: stacks) służą do budowania layoutu aplikacji. Możesz je łączyć ze sobą, żeby budować bardziej rozbudowane układy.

Masz do dyspozycji następujące stosy: VStack, HStack, ZStack.

VStack

Stos pionowy (V jako ang.: vertical), który wyświetla elementy z góry na dół.

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        VStack {
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.red)
								
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.green)
								
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.blue)
        }
    }
}

HStack

Stos poziomy H jako ang.: horizontal), który wyświetla elementy od lewej do prawej strony.

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        HStack {
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.red)
								
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.green)
								
			Circle()
			    .frame(width: 100, height: 100)
			    .foregroundColor(.blue)
        }
    }
}

ZStack

Stos głębi, który wyświetla elementy, w kolejności od początku do końca. Na przykład mając trzy elementy, które są takich samych rozmiarów, ten ostatni zostanie wyświetlony jako widoczny, a resztę przykryje.

import SwiftUI

struct ContentView: View {
   
    var body: some View {
        ZStack {
            Circle()
                .frame(width: 300, height: 300)
                .foregroundColor(.green)
           
            Circle()
                .frame(width: 200, height: 200)
                .foregroundColor(.blue)
           
            Circle()
                .frame(width: 100, height: 100)
                .foregroundColor(.red)
           
            Text("Hi!")
                .font(.largeTitle)
        }
    }
}

Właściwości stosu

Stos daje także możliwość na sposób wyświetlania elementów poprzez wyrównanie (ang.: alignment) oraz na ustawienie odstępów między elementami (ang.: spacing).

Wyrównanie

Jeśli chodzi o wyrównanie, użyj właściwości: alignment.

Przy czym, każdy stos przyjmuje własne wartości:

Dla VStack:

  1. .leading — wyrówanie do lewej
  2. .center — wyrównanie do środka (domyślna wartość)
  3. .trailing — wyrównanie do prawej
VStack(alignment: .leading)

Dla HStack:

  1. .top — umieszcza elementy na górze stosu
  2. .bottom — umieszcza elementy na dole stosu
  3. .center — umieszcza elementy na środku
HStack(alignment: .leading)

Dla ZStack:

  1. Przyjmuje takie same wartości jak dla VStack i HStack, np: leading lub top
  2. Lub kombinacje: topLeading, topTrailing, bottomLeading, bottomTrailing

Odstęp

Żeby ustawić odpowiedni odstęp między elementami stosu, użyj właściwości: spacing.

VStack(spacing: 50)

SwiftUI: Do czego służy @EnvironmentObject?

Opublikowano: 18.01.2024 - tagi: SwiftUI Swift Widok Wrapper Dane

Czym jest @EnvironmentObject?

Ten wrapper służy do wymiany danych między wieloma widokami. Jest to szczególnie przydatne kiedy jeden widok składa się z wielu innych i na przykład do niektórych z nich musimy przekazać jakieś dane.

Przekazywanie danych z jednego widoku do drugiego, aż w końcu trafimy na ten, w którym te dane są potrzebne, staje się męczące. Ten problem rozwiązuje @EnvironmentObject.

Masz także gwarancję, że jeśli dane się zmienią, widok zostanie automatycznie o tym poinformowany.

Aby przejąć dane oznaczone przez @EnvironmentObject, będziesz musiał w widoku, który znajduje się wyżej w hierarchii wywołać metodę environmentObject i przekazać do niej obiekt, którym chcesz się dzielić z innymi widokami.

Najlepiej wszystko zobrazuje poniższy przykład.

Przykład

Mamy jeden model o nazwie Counter.

Trzy widoki w takiej hierarchii: ContentView -> InformationView -> CounterLabelView.

Widok CounterLabelView potrzebuje mieć dostęp do obiektu klasy Counter. Dlatego w tym widoku zostanie użyty @EnvironmentObject, aby przechwycić przesłane dane.

W widoku InformationView niepotrzebny jest obiekt klasy Counter. Zawiera on widok CounterLabelView.

W widoku głównym ContentView wywołujemy metodę environmentObject, żeby przekazać instancję klasy Counter do widoków potomnych.

Model:

import SwiftUI

class Counter: ObservableObject {
    @Published var value: Int = 0
    
    func increment() {
        value += 1
    }
    
    func decrement() {
        value -= 1
    }
    
    func reset() {
        value = 0
    }
}

Pierwszy widok:

import SwiftUI

struct CounterLabelView: View {
    @EnvironmentObject var counter: Counter
    
    var body: some View {
        Text("\(counter.value)")
            .font(.largeTitle)
    }
}

Drugi widok:

import SwiftUI

struct InformationView: View {
    var body: some View {
        VStack {
            CounterLabelView()
            Text("Click '+' to increment counter").padding()
            Text("Click '-' to decrement counter").padding()
        }
    }
}

Główny widok:

import SwiftUI

struct ContentView: View {
    @StateObject var counter = Counter()
    
    var body: some View {
        VStack {
            InformationView()
            HStack {
                Button("+") {
                    counter.increment()
                }.padding()
                
                Button("-") {
                    counter.decrement()
                }.padding()
            }
        }
        .environmentObject(counter)
    }
}