LEKCJA 7 – funkcje w języku C#

W tej lekcji dowiesz się:

  • Czym jest funkcja
  • Jak uzyskać lepszą czytelność kodu dzięki dzieleniu kodu na małe funkcje
  • Jak zbudować funkcję
  • Co to jest refaktoryzacja (ang. refactoring)

Opis problemu – nieczytelny kod

Dotychczas kod programu zapisywaliśmy wewnątrz metody Main(). Jest to jak najbardziej prawidłowy sposób pracy ponieważ każda aplikacja zawiera metodę Main. Jednak gdy tworzymy większe programy to szybko okazuje się, że kod może się rozrastać i w efekcie stać się mało czytelny.

Kod, który można czytać jak książkę

Jak temu zaradzić? Prostą metodę poznałem dzięki książce pt. Clean Code Roberta C. Martina. Robert Martin, znany również jako Uncle Bob napisał w tej książce, że warto wydzielać dobrze nazwane funkcje aby zwiększyć czytelność kodu. Dzięki temu programista, który czyta kod nie musi zaglądać do wnętrza funkcji aby zrozumieć co robi program. Wystarczy przeczytać nazwy funkcji aby zrozumieć koncepcję. Dzięki dzieleniu kodu na mniejsze fragmenty kod będzie się czytało przyjemnie jak dobrą książkę.

Przykład: kod bez użycia funkcji

Zobaczmy jak wygląda program zapisany w jednej funkcji:

using System;

namespace Kalkulator
{
    class Program
    {
        static void Main(string[] args)
           {
            Console.WriteLine("Podaj kwotę pożyczki");
            var loanAmountApplication = int.Parse(Console.ReadLine());

            var loanAmounts = new int[] { 1200, 2500, 1350, 990, 1600 };

            int sum = 0;
            foreach(var amount in loanAmounts)
            {
                sum = sum + amount;
            }

            int averageAmount = sum/loanAmounts.Length;

            double interest;
            if(loanAmountApplication > averageAmount)
            {
                interest = 0.1;
            }
            else
            {
                interest = 0.2;
            }

            var totalToPay = loanAmountApplication * (1+ interest);

            Console.WriteLine("Calkowita kwota do spłaty: " + totalToPay);
            Console.WriteLine("Wysokość raty: " + totalToPay/12);
        }
    }
}

Celowo nie napisałem jakie jest zadanie powyższego kodu. Spróbuj zgadnąć. Okazuje się, że odpowiedź na to pytanie wymaga przeczytania całego programu. Musisz przeczytać wszystkie linijki kodu, żeby wiedzieć co on robi. O ile nie stanowi to problemu w tak małym programie jak nasz(38 linii kodu), ale w rzeczywistości programy liczą tysiące linii kodu. I wtedy pojawia się problem braku czytelności.

Aby zwiększyć czytelność możemy podzielić kod na logiczne sekcje, które nazywamy funkcjami, albo metodami.

Przykład: kod z użyciem funkcji

Zatem zobaczmy zaraz jak poprawiła się czytelność kiedy podzieliliśmy kod na funkcje.

using System;

namespace ConsoleApp11
{
    class Program
    {
        static void Main(string[] args)
        {
            var loanAmountApplication = GetAmount();
            var averageAmount = CalculateAverageAmount();
            var totalToPay = CalculateTotalToPay(loanAmountApplication, averageAmount);
            DisplayProposedLoanDetails(totalToPay);
        }

        private static void DisplayProposedLoanDetails(double totalToPay)
        {
            Console.WriteLine("Calkowita kwota do spłaty: " + totalToPay);
            Console.WriteLine("Wysokość raty: " + totalToPay / 12);
        }

        private static double CalculateTotalToPay(int loanAmountApplication, int averageAmount)
        {
            double interest;
            if (loanAmountApplication > averageAmount)
            {
                interest = 0.1;
            }
            else
            {
                interest = 0.2;
            }

            var totalToPay = loanAmountApplication * (1 + interest);
            return totalToPay;
        }

        private static int CalculateAverageAmount()
        {
            var loanAmounts = new int[] {1200, 2500, 1350, 990, 1600};

            int sum = 0;
            foreach (var amount in loanAmounts)
            {
                sum = sum + amount;
            }

            int averageAmount = sum / loanAmounts.Length;
            return averageAmount;
        }

        private static int GetAmount()
        {
            Console.WriteLine("Podaj kwotę pożyczki");
            var loanAmountApplication = int.Parse(Console.ReadLine());
            return loanAmountApplication;
        }
    }
}

To ten sam kod, ale z wydzielonymi funkcjami. Powyższa refaktoryzacja (czyli przepisanie kodu zwiększające jego czytelność ale nie zmieniające totalnie jego działania) zajęła mi około 90 sekund, ponieważ użyłem dodatku do Visual Studio o nazwie Resharper, co pozwoliło mi szybko wydzielić funkcje.

Spróbujmy jeszcze raz przeczytać nasz program. Dzięki prawidłowemu wydzieleniu funkcji możesz teraz zaoszczędzić sporo czasu bo nie musisz już czytać całego programu, ponieważ główny algorytm został zapisany w następujących linijkach:

        static void Main(string[] args)
        {
            var loanAmountApplication = GetAmount();
            var averageAmount = CalculateAverageAmount();
            var totalToPay = CalculateTotalToPay(loanAmountApplication, averageAmount);
            DisplayProposedLoanDetails(totalToPay);
        }

Z tego zapisu jasno wynika co się po kolei dzieje, a algorytm można przeczytać jak książkę bez koniecznośći czytania poszczególnych metod. Oto co się po kolei dzieje w programie:

  1. Pobieramy kwotę wniosku kredytowego od klienta
  2. Obliczamy średnią kwotę dotychczasowych wniosków
  3. Obliczamy całkowitą kwotę do spłaty
  4. Wyświetlamy użytkownikowi proponowane warunki kredytu.

Jeszcze raz zadajmy sobie pytanie: co robi nasz program? Odpowiedź jest już prosta: Oblicza warunki kredytu na podstawie kwoty podanej przez klienta. Program bardzo się uprościł i jest łatwiejszy do zrozumienia, a wystarczyło tylko wprowadzić 4 nowe funkcje i odpowiednio je nazwać.

Zadanie domowe

A teraz tradycyjnie czas na zadania dla Ciebie. Zachęcam do przesyłania rozwiązania na adres: darek@kursdotnet.pl.

Zadanie 1: Funkcja bez zwracania wartości

  1. Napisz program wyświetlający „Hello World”
  2. Wydziel z programu funkcję SayHello(string name), która jako parametr będzie przyjmować zmienną typu string.
  3. Wywołaj funkcję podając parametr. Wywołanie powinno wyglądać dokładnie tak: SayHello(„Darek”). W efekcie oczekujemy, że wyświetli się napis „Hello Darek”

Zadanie 2: Funkcja, która zwraca wartość

  1. Napisz funkcję Sum(int a, int b), która oblicza sumę dwóch liczb całkowitych
  2. Wywołaj funkcję w następujący sposób: var result = Sum(10, 20);
  3. Wyświetl wynik w konsoli za pomocą kodu: Console.WriteLine(result);
  4. Sprawdź czy program wyświetli prawidłową wartość czyli 30.