Programowanie

Java enum

Java enum

Enum – „Jesień”, „Jesien”, a może „JESIEN”? 🍁🌰🐿️

W tym materiale przedstawię Ci typ wyliczeniowy, czyli tzw. enum. Jeżeli chcesz zapewnić bezpieczeństwo typu w czasie kompilacji oraz uniknąć przekazywania niechcianych wartości do parametrów, to zachęcam Cię do zapoznania się z tym materiałem. Pokażę Ci jak korzystać z enumów i w jaki sposób czerpać z nich korzyści, aby uniknąć powyższych rozterek.

Enum – Wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest enum?
  • Jak działa konstruktor w klasie enum?
  • Jakie metody udostępnia klasa enum?
  • Jak działa metoda values()?
  • Czym jest EnumSet oraz EnumMap?

Enum

Enum, czyli tzw. typ wyliczeniowy to specjalny typ danych. Typ ten reprezentuje zbiór predefiniowanych stałych.

Deklaracja enum definiuje klasę (zwaną typem enum). Ciało klasy enum może zawierać metody i inne pola – tak jak klasa.

Enumy są używane, gdy znamy wszystkie możliwe wartości w czasie kompilacji, takie jak np. pory roku, kraje, rozmiary ubrań itp.

enum java enum

Enum java – Deklaracja

Deklaracja enum może być wykonana:

  • poza klasą
  • wewnątrz klasy

Pamiętaj! ⚠️ Nie możesz zadeklarować enum wewnątrz metody.

public class Order {


    private OrderStatus status;
    
    public enum OrderStatus {
        IN_DELIVERY,
        DELIVERED,
        CANCELLED
    }


    public boolean isCancelled() {
        if (getStatus() == OrderStatus.CANCELLED) {
            return true;
        }
        return false;
    }
    public OrderStatus getStatus() {
        return status;
    }
 }

Enum java – Zmienne

Zmienne enum są (niejawnie):

  • Finalne (ang. final)
  • Publiczne (ang. public)
  • Statyczne (ang. static)

Enum java – Korzyści 👍

Stałe zdefiniowane w ten sposób:

  • Czynią kod bardziej czytelnym
  • Zapewniają bezpieczeństwo typu w czasie kompilacji – jeśli spróbujesz przekazać wartości inne niż wartości dostępne w klasie enum, kompilator wygeneruje błąd
  • Dokumentują z góry listę akceptowanych wartości
  • Pozwalają uniknąć nieoczekiwanych zachowań spowodowanych przekazaniem nieprawidłowych wartości
  • Wykorzystywane są w konstrukcji switch
public void printDayType(Day day) {

    switch (day) {
        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY:
            System.out.println(day + " is working day");
            break;
        case FRIDAY: 
            System.out.println(day + " is working day, but almost weekend");
            break;
        case SATURDAY, SUNDAY:
            System.out.println(day + " - yeaah it is WEEKEND :)");
            break;
    }
 }

Powrócę tutaj do początkowej rozterki: „Jesień”, „Jesien”, a może „JESIEN”? 🍁🌰🐿️

Wbrew pozorom jedną wartość można przedstawić na wiele różnych sposobów, dlatego warto przyjąć jakieś odgórne zasady – konwencję.

Odgórnie ustalona konwencja w Javie przyjmuje, że nazwa powinna być pisana wielkimi literami, a wyrazy oddzielone od siebie podkreśleniem (ang. screaming snake case).

Enum java – Metody

Kompilator automatycznie dodaje pewne „specjalne” metody, które Twoja klasa enum dziedziczy automatycznie po klasie java.lang.Enum. Do tych „specjalnych” metod można zaliczyć m.in.:

  • values()
  • ordinal()
  • compareTo()
  • name() będący odpowiednikiem toString()
  • valueOf()

Java enum values

Przykładem takiej „specjalnej” metody klasy enum jest statyczna metoda values.

Metoda ta zwraca tablicę zawierającą wszystkie wartości enum w kolejności, w jakiej zostały zadeklarowane.

values() jest powszechnie używane w pętli for-each do iteracji po wartościach typu enum.

 for (Day day : Day.values()) { 
    System.out.println(day); 
 }

Java Enum ordinal()

Metoda ordinal() zwraca pozycje stałej enum. Nie zaleca się jednak korzystania z liczb porządkowych enumów, ponieważ każde dodanie nowej stałej w innym miejscu niż na końcu zmieni wartość ordinal.

Java enum Constructor

W Javie klasa enum może zawierać konstruktor jak zwykła klasa. Taki konstruktor musi być:

  • prywatny (ang. private) – dostępny wewnątrz klasy
  • pakietowy (ang. package-private) – dostępny w ramach pakietu

Konstruktora nie można jednak wywołać ręcznie – zostanie on wywołany automatycznie podczas tworzenia stałych enum.

public enum OrderStatus {
     IN_DELIVERY("Courier is driving to client"),
     DELIVERED("Client confirmed delivery of package"),
     CANCELLED("Client cancelled delivery");


     private final String orderDetails;


     private OrderStatus(String orderDetails) {
         this.orderDetails = orderDetails;
     }


     public String getOrderDetails() {
         return orderDetails;
     }
 }


 class Main{
     public static void main(String[] args) {
         System.out.println(OrderStatus.DELIVERED.getOrderDetails());
     }
 }

Enum java – Dziedziczenie

W Javie klasa enum jest domyślnie końcowa. W związku z tym nie można po niej dziedziczyć.

Również nie jest możliwe rozszerzenie klas enum, ponieważ wszystkie klasy enum w Javie dziedziczą (niejawnie) po klasie java.lang.Enum. Wielokrotne dziedziczenie nie jest dozwolone w Javie.

Możliwe jest natomiast implementowanie przez klasę enum interfejsów.

 public enum OrderStatus implements DeliveryService{

    IN_DELIVERY,
    DELIVERED,
    CANCELLED;
    
    @Override
    public void displayOrderStatus() {
        System.out.println("Current status of your order is: " + this);
    }
 }


 class Main{
    public static void main(String[] args) {
        OrderStatus.DELIVERED.displayOrderStatus();
    }
 }

Java enum – porównywanie wartości – „==” vs. equals vs. Object.equals()

Ponieważ typy enum zapewniają, że tylko jedna instancja stałej istnieje w JVM, można bezpiecznie użyć operatora „==” do porównania dwóch zmiennych.

Należy pamiętać, że korzystając z metody equals w momencie wywołania jej na null, dostaniemy wyjątek NullPointerException (obj1.equals(obj2) – gdzie obj1 jest równy NULL)

Dobrą alternatywą dla equals() jest metoda o tej samej nazwie z biblioteki Objects (Objects.equals()), która umożliwia porównywanie również wartości nullowych bez ryzyka pojawienia się wyjątku NullPointerException.

@Test
public void objectsEqualsShouldNotThrowNullPointerException(){
    assertFalse(Objects.equals(null, "Kasia"));
}

➡ ZOBACZ 👉: hashCode i equals – co grozi Ci za złamanie kontraktu między metodami equals i hashCode?

EnumSet

EnumSet jest wyspecjalizowaną implementacją Set, która jest przeznaczona do użycia z typami Enum.

EnumSet bardziej wydajnie przechowuje wartości enum w porównaniu do HashSet.

Możliwe jest stworzenie nowego EnumSeta m.in. poprzez:

  • of()
  • all()
  • range()
 EnumSet<OrderStatus> allStatuses = EnumSet.allOf(OrderStatus.class);
 
 EnumSet<OrderStatus> deliveryStatuses = EnumSet.of(OrderStatus.IN_DELIVERY, OrderStatus.DELIVERED);
 
 EnumSet<OrderStatus> finalStatuses = EnumSet.range(OrderStatus.DELIVERED, OrderStatus.CANCELLED);

EnumMap

EnumMap jest wyspecjalizowaną implementacją Mapy przeznaczoną do użycia ze stałymi enum jako kluczami.

EnumMap bardziej wydajnie przechowuje wartości enum w porównaniu do HashMap.

 EnumMap<OrderStatus, String> statusesWithDetails = new EnumMap<>(OrderStatus.class);
 statusesWithDetails.put(OrderStatus.CANCELLED, "Client cancelled delivery");

Enum – Typ wyliczeniowy – Podsumowanie

W ramach tego materiału dowiedzieliśmy się, czym jest enum. Bliżej zapoznaliśmy się ze strukturą klasy enum i jej możliwościami. Jeżeli chcesz kontynuować swoją przygodę z Javą i poznać inne struktury, które oferuję ten język programowania – to zapraszam do zapoznania się z różnymi tematami z serii o Javie.

ZOBACZ 👉: Kurs Java | Darmowy Kurs Programowania w Javie

Kierunek Java

W serii o Javie zapoznajesz się z podstawowymi tematami o Javie. Jeżeli chcesz bardziej kompleksowo zagłębić się w temat Javy, poczytać, posłuchać o Javie, to zachęcam Cię do zapoznania się z moim kursem „Kierunek Java”:

➡ ZOBACZ 👉: Kierunek Java

No comments
Share:
Programowanie obiektowe – Object Oriented Programming (OOP)

Programowanie obiektowe – Object Oriented Programming (OOP)

Programowanie Obiektowe – jesteś zwierzęciem, ssakiem, czy jednak 'bytem’…?

Jako człowiek🧍, jestem zwierzęciem 🐿️, ssakiem, istotą ożywioną? – Czy może jeszcze czymś innym?
Może jestem programistą? Albo rodzicem 🤰, bądź dzieckiem 👶?

Tak naprawdę każdym z nich po trochu i wszystkimi jednocześnie – w zależności od potrzeby chwili – teraz np. jestem copywriterem ✍️

W ramach tego materiału dowiesz się, czym jest programowanie obiektowe (ang. Object Oriented Programming) i jak z nim pracować.

Programowanie obiektowe – wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest programowanie obiektowe?
  • Jak tworzyć aplikację wykorzystując programowanie obiektowe?
  • Jakie są paradygmaty programowania obiektowego?
  • Jak programuje się obiektowo w Javie?

Paradygmaty programowania

Paradygmaty programowania – to różne sposoby, w których dany program lub język programowania może być zorganizowany. Każdy paradygmat składa się z pewnych struktur, cech i opinii na temat tego, jak należy rozwiązywać typowe problemy programistyczne.

Programowanie obiektowe (ang. Object Oriented Programming)

Programowanie obiektowe (ang. OOP — Object Oriented Programming) to popularny paradygmat programowania, w którym modelujemy istniejącą rzeczywistość za pomocą obiektów, zamiast stosować tylko funkcje i logikę.

Projektowanie obiektowe polega na umiejętności przedstawienia sytuacji z życia codziennego pod postacią obiektów i relacji między nimi.

Obiekty to elementy łączące stan – czyli przechowywane dane, nazywane często polami oraz zachowanie – czyli procedury/metody.

Jest to odmienne podejście od tradycyjnego programowania proceduralnego, gdzie dane i metody nie są ze sobą tak ściśle związane. Celem OOP jest ułatwienie modelowania aplikacji oraz późniejszego pisania, konserwacji i potencjalnego ponownego użycia kodu.

Ogromną zaletą programowania obiektowego jest jego zgodność ze światem rzeczywistym. Takie podejście możemy rozmieć, jako próbę przedstawienia świata rzeczywistego i relacji w nim zachodzących pod postacią obiektów i klas.

Obiektowy program komputerowy to zbiór takich obiektów, komunikujących się pomiędzy sobą w celu wykonywania określonych zadań.

Programowanie obiektowe – Od czego zacząć?

Nasze pierwsze aplikacje bardzo często pisane są w podejściu proceduralnym – czyli takim, w którym mamy jedną główną metodę, w której piszemy całą naszą logikę. Czasem wydzielamy jakieś dodatkowe metody (procedury), jednak większość kodu trzymana jest w jednym pliku. Tak właśnie powstają „potworki” 👹 po 1000 linii kodu, czasem po 3000 i więcej. Rekordzista, którego widziałem miał prawie 20 000 linijek kodu 😬 w jednym pliku – IDE wieszało się przy każdej próbie uruchomienia pliku, dlatego trzeba było edytować go w notatniku…

Zdecydowanie nie tędy droga. 🙂 – Pierwsze kroki z OOP

Wyobraź sobie, że Twoim zadaniem jest zaprojektowanie aplikacji obiektowej np. dla:

  • Biblioteki
  • Sklepu monopolowego
  • Szkoły językowej
  • Dowolnej innej sytuacji, z którą chcesz się zmierzyć.

Umiejętność pisania dobrego kodu, (czyli również projektowanie obiektowe) często jest na pograniczu sztuki i twardych umiejętności technicznych. Co to tak naprawdę znaczy dla Ciebie i jak ugryźć ten temat?

  • Zacznij od przygotowania pełnego opisu Twojego problemu. Zastanów się 💡 „jak to ma działać?” i zbierz wszystkie możliwe informacje w formie prostego tekstu
  • W kolejnych krokach zidentyfikuj rzeczowniki, czasowniki i przymiotniki:
    • Rzeczowniki to kandydaci na klasy i obiekty
    • Czasowniki to metody naszych klas
    • Przymiotniki to kandydaci na pola (cechy)

programowanie obiektowe object oriented programming oop java

 

Każdy obiekt ma swój stan (czyli dane, nazywane najczęściej polami) oraz zachowanie (czyli procedury, w tym przypadku – metody) – przykładowo obiekt może reprezentować konkretną osobę np.
Tomka 👦🏻 – Tomek ma swoje imię, nazwisko, wiek oraz zachowanie np. mówi, śpi, programuje itp.

Programowanie obiektowe – Pamiętaj!

Pamiętaj!

Nie wszystkie wymagania są już na samym początku jawne – niektóre relacje i pola trzeba będzie zwyczajnie dodać. Mimo wszystko takie podejście jest dobrym punktem wyjścia do dalszych prac.

Zabierając się za tworzenie aplikacji, wykorzystując programowanie obiektowe, warto odpowiedzieć sobie również na samym początku na wiele pytań, która nasuwają się podczas pracy z obiektami, np.:

  • Co w danym wypadku lepiej się sprawdzi: dziedziczenie, czy kompozycja?
  • Czy zastosować interfejs, czy też klasy abstrakcyjne?
  • Jak stworzyć jak najlepszą hermetyzację danych?
  • Itp.

Warto zrobić przynajmniej raz takie ćwiczenie, bo dzięki temu każdy kolejny raz będzie już dużo prostszy.

Obiekty i klasy

Klasa, z punktu widzenia programowania to typ zmiennej. W ujęciu projektowym ogólna definicja pewnej grupy obiektów, które różnią się tożsamością. Klasa definiuje metody, czyli funkcjonalności, które są dostarczane przez obiekty.
Definiuje też atrybuty, które są indywidualne dla konkretnych obiektów.

Każdy obiekt jest instancją konkretnej klasy np. Tomek jest instancją klasy człowiek. Procedury w OOP nazywane są metodami, a zmienne obszarami, członkami, atrybutami, właściwościami.

Obiekty i klasy – Zmienne i metody

  • Zmienne klasy — należą do całości klasy, jest tylko jedna kopia każdej
  • Instancje zmiennych lub atrybutów — dane, które należą do indywidualnych obiektów, każdy obiekt ma własną
  • Zmienne składowe — odwołują się zarówno do klas, jak i zmiennych instancji
  • Metody w klasach — należą do całości klas i mają dostęp tylko do zmiennych klas oraz wprowadzanych w wywoływaniu procedur
  • Metody instancji – należą do indywidualnych obiektów, mają dostęp do zmiennych instancji dla specyficznych obiektów, dla których są wywoływane, wprowadzanych oraz zmiennych klas

Programowanie obiektowe – Po co to nam?

Programowanie obiektowe z założenia ma ułatwić nam:

  • Pisanie kodu
  • Utrzymanie kodu
  • Wielokrotne użycie kodu lub jego fragmentów
  • Możliwość równoległej pracy wielu deweloperów

W teorii brzmi pięknie 🌻 – w praktyce różnie to bywa…
Pamiętajmy, że OOP to tylko narzędzie 🛠️ i to od nas zależy, jak z niego skorzystamy. Tworząc aplikację wykorzystując OOP, szybko zauważysz zalety tego podejścia, szczególnie gdy zechcesz ją rozbudować.

Paradygmaty programowania obiektowego (ang. oop principles) – Czyli zasady, z których należy korzystać

Główne paradygmaty programowania obiektowego:

  • Abstrakcja
  • Enkapsulacja
  • Dziedziczenie
  • Polimorfizm

Abstrakcja | klasa abstrakcyjna (ang. Abstract class)

Klasa abstrakcyjna różni się od zwykłej klasy tym, że nie można utworzyć jej instancji. Może jednak dostarczyć metod zdefiniowanych i abstrakcyjnych, które muszą zostać zdefiniowane w klasach potomnych.

abstract class programowanie obiektowe oop java object oriented programming
Mammals jest abstrakcyjny, ponieważ, mimo że ssakiem jest pies i człowiek to nie można utworzyć tylko ssaka, który nie będzie żadnym z podgatunków. Dzięki takiemu zamodelowaniu wiemy jednak, że jesteśmy ssakami.

Interfejs (ang. Interface)

Interfejs to specyficzna forma abstrakcji. Od klasy abstrakcyjnej różni się głównie tym, że:

  • nie może definiować atrybutów, tylko metody i stałe,
  • wszystkie metody muszą być abstrakcyjne i publiczne (od Javy 8 wprowadzone zostały również metody default’owe).

Dzięki interfejsom można zapewnić pożądane funkcjonalności – są pewnego rodzaju kontraktem między klasami, które implementując go, wykorzystują jego funkcję.

Interfejs gwarantuje, że jakaś metoda istnieje, a nie jak się konkretnie zachowuje.
interface object oriented programming programowanie obiektowe oop java
Ssaki, jak wszystkie inne zwierzęta, poruszają się i jedzą. Niemniej jednak inaczej przemieszczają się ryby, inaczej ptaki, a inaczej ludzie. To samo tyczy się jedzenia.

W ramach tego materiału zajmiemy się przede wszystkim poznaniem tematu programowania obiektowego – natomiast szersze rozwinięcie tematu abstrakcji znajdziesz poniżej.

➡ ZOBACZ 👉: 

Hermetyzacja

Hermetyzacja polega na ukryciu informacji (szczegółów implementacji), które nie powinny być widoczne poza klasą lub modułem.

Hermetyzacja może być używana do ukrywania zarówno członków danych, jak i metod związanych z instancją klasy lub obiektu.

Aby to osiągnąć, należy:

  • Zadeklarować zmienne lub metody klasy jako prywatne
  • Zapewnić publiczne metody get i set, aby uzyskać dostęp i aktualizować wartość prywatnej zmiennej

Enkapsulacja (ang. Encapsulation)

Enkapsulacja odnosi się do wiązania danych, wspólnie z metodami operującymi na tych danych, w jedną całość.

Przykładem enkapsulacji jest klasa, ponieważ składa się z danych i metod, które zostały połączone w jedną całość.

enkapsulacja oop java programowanie obiektowe

enkapsulacja hermetyzacja programowanie obiektowe oop java object oriented programming
W ramach tego materiału zajmiemy się przede wszystkim poznaniem tematu programowania obiektowego – natomiast szersze rozwinięcie tematu hermetyzacji znajdziesz poniżej.

➡ ZOBACZ 👉: W opracowaniu 🙂 Wkrótce pojawi się tutaj link do ciekawych treści!

Dziedziczenie (ang. Inheritance)

Dziedziczenie jest mechanizmem, w którym można stworzyć nową klasę, bazując na już istniejącej (nadrzędnej) klasie tzw. superklasie. Klasa macierzysta dzieli zestaw publicznych lub chronionych (ang. protected) atrybutów i metod z klasą dziecka.

W celu dziedziczenia po klasie nadrzędnej należy użyć słowa kluczowego „extends”, aby zidentyfikować klasę, którą rozszerza twoja podklasa. Jeśli superklasa nie zostanie zadeklarowana, klasa domyślnie rozszerzy klasę Object.

Klasa Object jest podstawą wszystkich hierarchii dziedziczenia. Jest to jedyna klasa w Javie, która nie rozszerza innej klasy.

oop java inheritance programowanie obiektowe

W ramach tego materiału zajmiemy się przede wszystkim poznaniem tematu programowania obiektowego – natomiast szersze rozwinięcie tematu dziedziczenia znajdziesz poniżej.

➡ ZOBACZ 👉: W opracowaniu 🙂 Wkrótce pojawi się tutaj link do ciekawych treści!

Polimorfizm – wielopostaciowość (ang. Polymorphism)

Polimorfizm to zdolność obiektu do przybierania różnych form. Występuje np., w sytuacji, gdy korzysta się z wielu klas, powiązanych ze sobą poprzez dziedziczenie. Sprowadza się to do tego, że polimorfizm pozwala na wykonanie wielu implementacji poprzez zdefiniowanie jednego interfejsu.

Wielopostaciowość pozwala wykonać tę samą czynność na wiele różnych sposobów.

polymorphism objected oriented programming programowanie obiektowe
polimorfizm programowanie obiektowe object oriented programming oop java
W ramach tego materiału zajmiemy się przede wszystkim poznaniem tematu programowania obiektowego – natomiast szersze rozwinięcie tematu polimorfizmu znajdziesz poniżej.

➡ ZOBACZ 👉: W opracowaniu 🙂 Wkrótce pojawi się tutaj link do ciekawych treści!

Java vs. Programowanie Obiektowe

Java jest językiem zorientowanym obiektowo, który co prawda ma typy proste jak chociażby int, czy long do przechowywania liczb – ale ma również wszystko to, co jest niezbędne, by napisać pełnoprawne obiektowe aplikacje.
Podstawa to oczywiście klasy i obiekty.

programowanie obiektowe oop java object oriented programming
O klasach możemy myśleć, jak o szablonach (wzorach), na podstawie których powstają konkretne obiekty – np. takim szablonem może być klasa Client, a konkretnym obiektem powstałym na podstawie tej klasy „Tomek”.

Kod przygotowany według zasad projektowania obiektowego stara się odzwierciedlać rzeczywistość, co z założenia jest bardziej naturalnym sposobem przetwarzania informacji przez człowieka. Widać to na przykładzie, gdzie mamy obiekt umowy („agreement”) i wywołujemy na nim metodę podpisania 📝 („agreement.sign()”) przekazując jednocześnie niezbędne informacje.

Programowanie obiektowe – Podsumowanie

W ramach tego materiału poznaliśmy jeden z paradygmatów programowania, jakim jest projektowanie obiektowe. Dowiedzieliśmy się też o zbiorze mechanizmów, jakie wykorzystuję się, w programowaniu obiektowym m.in. poznaliśmy temat abstrakcji, hermetyzacji, dziedziczenia oraz polimorfizmu.

Jeżeli chcesz kontynuować swoją przygodę z Javą i programowaniem obiektowym – to zapraszam do mojego kursu „Kierunek Java”:

➡ ZOBACZ 👉: Kierunek Java

No comments
Share:
Java-18

Java 18: premiera, nowości, zmiany – praktyczne podsumowanie

Java 18 ujrzała światło dzienne i według producenta ma nieść za sobą tysiące ulepszeń w zakresie wydajności, stabilności i bezpieczeństwa, które jeszcze bardziej zwiększą produktywność programistów.

„Wydanie Java 18 jest dowodem na to, że firma Oracle nieustannie dąży do zapewnienia przedsiębiorstwom i programistom szybszego dostępu do ulepszeń dzięki sześciomiesięcznemu cyklowi wydawania nowych wersji” — stwierdził Georges Saab, wiceprezes ds. rozwoju Java Platform w firmie Oracle.

Najnowszy pakiet Java Development Kit (JDK) zapewnia aktualizacje i ulepszenia dzięki dziewięciu rozszerzeniom (JDK Enhancement Proposals, JEP), a w tym wpisie pokrótce je omówimy 🙂

Kilka słów o wydawaniu nowych wersji

22 marca 2022 została wydana nowa wersja Java typu 'General-Availability Release’ – czyli bez przedłużonego okresu wsparcia.

Obecnie obowiązujące wydanie z przedłużonym wsparciem – Long Term Support (w skrócie LTS) to wersja Java 17 wydana we wrześniu 2021 z zadeklarowanym wsparciem przez minimum 8 lat.

Zgodnie z założeniami nowe wersje niebędące LTS mają być publikowane co pół roku, natomiast LTS co około 3 lata.

Obecnie tak to wygląda:

  • Java SE 15 wrzesień 2020
  • Java SE 16 marzec 2021
  • Java SE 17 (LTS) wrzesień 2021

Planowane:

  • Java SE 19 wrzesień 2022
  • Java SE 20 marzec 2023
  • Java SE 21 (LTS) wrzesień 2023

Przyjrzymy się teraz zmianom, które przynosi Java 18 🙂

Lista zmian

JEP 400: UTF-8 by Default – domyślnie włączone wsparcie dla kodowania UTF-8

Różne formaty kodowania niewątpliwie przysparzają deweloperom wielu problemów.
W kodzie bardzo często polega się na domyślnym kodowaniu, które pobierane jest z systemu operacyjnego – wtedy wystarczy, że zakodujemy dane u nas lokalnie z innym domyślnym kodowaniem i wyślemy je na serwer z innym domyślnym kodowaniem i problem gotowy.
Większość metod z JDK pozwala nam nadpisać wykorzystywane kodowanie – tutaj chodzi jednak o zmniejszenie prawdopodobieństwa błędu wynikającego z wykorzystania domyślnych wartości.

Dzięki tej zmianie deweloperzy mogą stosować UTF-8 jako domyślny zestaw znaków dla standardowych API Javy.

System.out.println(Charset.defaultCharset());

➡ ZOBACZ 👉: ASCII – ASCII table, tablica kodów AsCii

JEP 408: Simple Web Server – prosty serwer webowy

Jest to narzędzie wiersza poleceń i API do uruchamiania uproszczonego serwera WWW, ale bez plików dynamicznych. Oferuje gotowy do użycia statyczny serwer plików HTTP z łatwą konfiguracją i minimalną funkcjonalnością.

Idea jest stosunkowo prosta – skoro już korzystamy ze środowiska Javowego i chcemy coś na szybko pokazać – to, zamiast instalować w tym celu niezależny serwer, możemy skorzystać z tego dostarczonego z JDK.

var server = SimpleFileServer.createFileServer(
		new InetSocketAddress(8008),
        Path.of("/home/tw/workspace/java18"),
		SimpleFileServer.OutputLevel.VERBOSE
);

server.start();
tw@tw:~/workspace/java18$ nano index.html
tw@tw:~/workspace/java18$ ~/.jdks/openjdk-18/bin/jwebserver

JEP 413: Code Snippets in Java API Documentation – fragmenty kodu w dokumentacji

To narzędzie wprowadza znacznik @snippet do standardowej dokumentacji JavaDoc, aby uprościć umieszczanie w dokumentacji API przykładowego kodu źródłowego.
Dotąd, aby dodać niewielki fragment kodu, konieczne było używanie tagów <pre>.

~/.jdks/openjdk-18/bin/javadoc -d doc 
-cp src/main/java 
--snippet-path ~/workspace/StormJava/java18/snippet-files 
pl.stormit.java18

 

➡ ZOBACZ 👉: Komentarze i samodokumentujący się kod | Kurs Java

JEP 416: Reimplement Core Reflection with Method Handles
– reimplementacja Core Reflection API z użyciem MethodHandle

Ta aktualizacja zmniejszy koszty utrzymania i rozwoju zarówno interfejsów API java.lang.reflect jak i java.lang.invoke poprzez wprowadzenie obsługi metod jako podstawowego mechanizm odzwierciedlania.

JEP 417: Vector API (Third Incubator) – trzeci inkubator

Trzeci inkubator dostarcza API dla programistów, co  pozwala lepiej wykorzystywać architekturę procesorów CPU – Vector API zapewniając skalowalne rozszerzenia wektorowe.

Do uruchomienia kodu niezbędne jest dodanie modułu: jdk.incubator.vector

--add-modules jdk.incubator.vector
public class VectorsExample {

	static final VectorSpecies&lt;Double&gt; SPECIES = DoubleVector.SPECIES_PREFERRED;

	static double[] A = IntStream.range(1, 512).mapToDouble(i -&gt; i).toArray();
	static double[] B = IntStream.range(1, 512).mapToDouble(i -&gt; i).toArray();

	public static void main(String[] args) throws Exception {
		Options opt = new OptionsBuilder()
				.include(VectorsExample.class.getSimpleName())
				.forks(1)
				.warmupIterations(1)
				.measurementIterations(1)
				.build();

		new Runner(opt).run();
	}

	@Benchmark
	public void scalar(Blackhole blackhole) {
		double[] C = new double[512];
		scalarComputation(A, B, C);

		blackhole.consume(C);
	}

	@Benchmark
	public void vector(Blackhole blackhole) {
		double[] C = new double[512];
		vectorComputation(A, B, C);

		blackhole.consume(C);
	}

	void scalarComputation(double[] a, double[] b, double[] c) {
		for (int i = 0; i &lt; a.length; i++) {
			c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
		}
	}

	void vectorComputation(double[] a, double[] b, double[] c) {
		int i = 0;
		int upperBound = SPECIES.loopBound(a.length);
		for (; i &lt; upperBound; i += SPECIES.length()) {
			// FloatVector va, vb, vc;
			var va = DoubleVector.fromArray(SPECIES, a, i);
			var vb = DoubleVector.fromArray(SPECIES, b, i);
			var vc = va.mul(va)
					.add(vb.mul(vb))
					.neg();
			vc.intoArray(c, i);
		}
		for (; i &lt; a.length; i++) {
			c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
		}
	}
}

➡ ZOBACZ 👉: Benchmark sposobem na wydajniejsze aplikacje – JMH

JEP 418: Internet-Address Resolution SPI

JEP 418 – definiuje interfejs dostawcy usług (SPI) do rozpoznawania nazw hostów i adresów, aby java.net.InetAddress mógł korzystać z innych rozwiązań niż wbudowany w platformę mechanizm rozpoznawania nazw.
Interfejs API java.net.InetAddress zamienia nazwy hostów na adresy protokołu internetowego (IP) i odwrotnie.

Obecnie interfejs API korzysta z natywnego mechanizmu rozpoznawania systemu operacyjnego, który zazwyczaj jest skonfigurowany do korzystania z połączenia lokalnego pliku hosts i systemu nazw domen (DNS).

JEP 419: Foreign Function & Memory API (Second Incubator) – drugi inkubator

Wprowadza interfejs API, dzięki któremu programy Java mogą współdziałać z kodem i danymi poza środowiskiem wykonawczym Java. Dzięki wydajnemu wywoływaniu funkcji obcych (tj. kodu poza maszyną JVM) i bezpiecznemu dostępowi do pamięci obcej (tj. pamięci niezarządzanej przez maszynę JVM), interfejs API umożliwia programom Java wywoływanie natywnych bibliotek i przetwarzanie natywnych danych.

JEP 420: Pattern Matching for switch (Second Preview)

To rozwiązanie ulepsza język programowania Java dzięki dopasowywaniu wzorców dla wyrażeń i instrukcji przełącznika, a także rozszerzeń języka wzorców. Rozszerzenie dopasowania do wzorca w celu przełączenia umożliwia testowanie wyrażenia pod kątem wielu wzorców, z których każdy ma określone działanie, dzięki czemu złożone zapytania zorientowane na dane mogą być wyrażane zwięźle i bezpiecznie. Jest to funkcja języka w wersji zapoznawczej w JDK 18.

 

switch (input) {
	case null -> System.out.println("NULL");
	case "a", "b" -> System.out.println("a-b");
	default -> System.out.println("Default");
}
switch (input) {
	case Integer i && i>10 -> System.out.println("Integer > 10");
	case Integer i -> System.out.println("Integer "+i);
	case Double d -> System.out.println("Double "+d);
	default -> System.out.println("Default");
}

➡ ZOBACZ 👉Java Switch Case | Kurs Java

JEP 421: Deprecate Finalization for Removal

Finalizacja pozostaje na razie domyślnie włączona, ale można ją wyłączyć, aby ułatwić wczesne testowanie. W przyszłej wersji zostanie domyślnie wyłączona, a w późniejszej wersji zostanie usunięta. Opiekunowie bibliotek i aplikacji, które opierają się na finalizacji, powinni rozważyć migrację do innych technik zarządzania zasobami, takich jak instrukcja try-with-resources i narzędzia czyszczące.

Dodatkowe materiały

No comments
Share:
Czy to koniec eldorado w IT i automaty WRESZCIE zastąpią programistów?

Czy to koniec eldorado w IT i automaty WRESZCIE zastąpią programistów?

Cześć!

Dziś temat na pograniczu filozofii oraz wróżenia z fusów… 🙃

Czasem trudno nam przewidzieć, co wydarzy się w weekend, czy nawet tego samego dnia wieczorem – a co dopiero za rok, czy za 10 lat… Mimo wszystko jako ludzie lubimy podejmować przynajmniej próby takiego przewidywania przyszłości.

Jakiś czas temu na blogu spytałem kolegów po fachu, jak oni widzą przyszłość naszej branży:

👉 Jak będzie wyglądał świat IT za 50 lat? – Przewidywanie przyszłości 👈

Mimo iż od publikacji tego wpisu minęło trochę czasu – zaskakujące jest to, jak wiele z tych komentarzy dalej jest aktualnych.

Tym razem spróbuję pochylić się również nad przyszłością IT – ale w bardziej ograniczonym zakresie i skupię się na tym, jak może zmienić się nasz rynek pracy w najbliższych latach.

Rynek pracownika w IT

W publicznej opinii przyjęło się mówić, że w IT panuje tak zwany rynek pracownika – co dosłownie możemy tłumaczyć jako sytuację, w której to pracownik, a nie pracodawca ustala zasady.

Co w pewnym stopniu rzeczywiście jest prawdą. Będąc pracownikiem w IT, zyskujemy nie tylko na wyższej pensji w porównaniu do innych branż – ale również na licznych dodatkowych benefitach – jak, chociażby karta Multisport, pokój gier w biurze, czy różnego rodzaju dofinansowania.

To wszystko spowodowane jest tym, że pracodawcy, chcąc pracować z najlepszymi specjalistami, muszą zwyczajnie „przebić ofertę” konkurencji – a jak okazuje się w praktyce – tych specjalistów ciągle jest za mało.

Polska pod tym względem jest dość specyficzna

Dodatkowo – Polska pod tym względem jest dość specyficzna. Mamy bardzo dobrych fachowców, którzy z powodzeniem rywalizują na światowych rynkach. Dodatkowo, dzięki pracy zdalnej możemy zarabiać w stawkach z bogatszych bardziej rozwiniętych krajów, jednocześnie ciesząc się niższymi kosztami życia lokalnie, w swojej ojczyźnie.

To wszystko powoduje, że przeciętny programista w Polsce zarabia relatywnie więcej niż inni specjaliści jak chociażby dentysta, czy inżynier budowy – przykładowo w USA, czy Wielkiej Brytanii te dysproporcje są już mniejsze.

Skaza na idealnym obrazie

Dość wyraźną skazę na tym idealnym obrazie wyryła pandemia. Ta sytuacja nie tylko zatrzęsła naszym prywatnym życiem, ale również rynkiem pracy w IT. Część firma była zmuszona zamknąć swoją działalność, część ograniczyła inwestycje – co oczywiście odbiło się również na samym rynku pracy.

O ile osoby z większym doświadczeniem prawie tego nie odczuły, bo dalej brakuje nam ludzi z doświadczeniem – to już nowe osoby dopiero wchodzące do branży miały zdecydowanie trudniej.

Wiele firm wstrzymując inwestycje, wstrzymało również zatrudnianie nowych osób – szczególnie z mniejszym doświadczeniem, bo takie osoby na początku są głównie inwestycją. Co dodatkowo w połączeniu z coraz większym zainteresowaniem nauką programowania i zmianą branży – przyczyniło się do sytuacji, że mieliśmy (i mamy dalej) więcej osób szukających pierwszej pracy niż rynek może przyjąć.

Paradoksalnie całkowicie inaczej wygląda kwestia osób choćby z niewielkim doświadczeniem, bo np. osoby z 5-letnim i większym doświadczeniem praktycznie z miejsca są wchłaniane przez rynek.

Czy to się zmieni? Czy programiści powinni bać się, że stracą pracę?😱

Podsumowując – jest nieźle. Nawet mimo ostatnich zawirowań. Jednak czy zawsze tak będzie? Czy może jednak programiści – nawet z większym doświadczeniem – powinni bać się o swoje zatrudnienie?

Kto się nie rozwija, ten się cofa…

Kto się nie rozwija, ten się cofa… – bo świat cały czas się zmienia, cały czas się rozwija i idzie do przodu.

Oczywiście – możemy powiedzieć, że to tylko wyświechtany frazes – ale… 🧐

Już dawno minęły czasy, że raz wyuczony zawód możemy wykonywać do spokojnej starości. Przyjrzyj się temu co zawodowo robili Twoi dziadkowie – ile z tych zawodów już nie ma?

Na każdej zmianie – ktoś traci i ktoś zyskuje

W podstawówce na lekcjach historii uczyliśmy się o rewolucji związanej z rolnictwem, czy rewolucji przemysłowej. Niewątpliwie były to ogromne przełomy w dziejach ludzkości – mają one jednak wiele wspólnego z bardziej aktualnymi czasami – jak chociażby zmiany, które obserwowaliśmy po wynalezieniu komputerów, czy popularyzacji Internetu.

Po wprowadzeniu nowych rozwiązań, nowych usprawnień – ludzie, którzy robili coś starym sposobem, tracili swój obecny zawód. Dziś nikogo nie dziwi fakt, że do zaorania i obsiania pola rolnik wykorzystuje wielkie maszyny rolnicze (które swoją drogą coraz częściej działają nawet bez fizycznego kierowcy!) – jednak kiedyś ludzie, którzy wykorzystywali w tym celu konia lub własną siłę mięśni musieli się dostosować (lub tracili źródło utrzymania, bo nie radzili sobie z konkurencją…).

Świat nabiera coraz większego tempa!

Obecne tempo zmian nabiera jeszcze większego i większego tempa.

Dosłownie na naszych oczach wprowadzane są nowe rozwiązania, które wpływają na nasze życie i pojawiają się nowe zawody.

Przyjrzyj się trendom, które zyskują na popularności:

  • samochody autonomiczne,
  • kasy samoobsługowe,
  • chat boty (również konsultanci głosowi),
  • i wiele innych.

Czy wszystkie te rozwiązania są idealne? – no NIE. Jeszcze nie…😉

Czy i nas programistów to czeka?

Nas jako ludzi pracujących w jednej z szybciej rozwijających się dziedzin czeka to szczególnie!

Już teraz:

  • Sztuczna inteligencja potrafi:
    • wygenerować teksty reklamowe,
    • rozpoznać przedmioty znajdujące się na zdjęciu,
    • namalować obraz, czy skomponować muzykę…
  • Mamy coraz popularniejsze rozwiązania no-code i low-code, które sprawiają, że wystarczą ograniczone umiejętności programistyczne, żeby dostarczyć gotowe rozwiązania informatyczne – które działają i z powodzeniem dostarczają wartość dla biznesu.

Cały czas, jesteśmy jednak optymistami!

Na moim profilu na LinkedIn zadałem Wam proste pytanie – Czy Twoim zdaniem doczekamy się czasów, że automatyzacja zastąpi programistów.

80% ankietowanych uważało, że tak nie nastąpi.

ankieta czy automatyzacja zastąpi programistów

Nie ma jednak wątpliwości, że my również będziemy musieli dostosować się do tych zmian.

Co możemy z tym zrobić?

Jak to mówił klasyk – „kijem Wisły nie zawrócisz…”

Nie ma co z tym walczyć, musimy się dostosować i radzić sobie w zmiennym otoczeniu. Warto trzymać rękę na pulsie i cały czas się rozwijać, by nie przespać swojej szansy.

Chcesz być na bieżąco – dołącz do naszego newslettera 😉

A może inaczej – każdy będzie programistą!

W przyszłości – nie zdziwiłaby mnie wcale sytuacja w której, zdecydowana większość społeczeństwa będzie znała przynajmniej podstawy programowania.

Jeszcze kilka/kilkanaście lat temu umiejętność programowania była niezłym wyróżnikiem.

A dziś?

Ilość osób pracujących w IT rośnie w zastraszającym wręcz tempie.

W szkołach podstawowych mamy lekcje informatyki i programowania z prawdziwego zdarzenia (oczywiście nie wszędzie – ale i to się zmienia). Moje dzieci już teraz piszą aplikacje na poziomie tych, które robiłem na studiach. Ich umiejętności są zdecydowanie większe niż moje w ich wieku.

Jednak zmieniły się również same technologie – korzystając z obecnych rozwiązań dużo mniejszym wysiłkiem, możemy osiągnąć zamierzony efekt.

Domyślam się, że ten trend będzie kontynuowany – i wiele rzeczy będziemy mogli osiągnąć dużo mniejszym wysiłkiem.

Kto dziś jeszcze programuje ręcznie w Asemblerze? A kiedyś nie było innego wyjścia…🙃

Prostsze programowanie i specjalizacja

W IT i w programowaniu udoskonalamy nasze technologie, przez co ich wykorzystanie staje się coraz prostsze – oraz następuje związana z tym specjalizacja.

Jeszcze nie tak dawno – „informatyk” to był informatyk.

A teraz?

Większość osób rozróżnia już programistę od informatyka 🙂 Mamy programistów backendu, frontendu, urządzeń mobilnych itp.

Te dziedziny tak się skomplikowały, że mimo iż same technologie bardzo się rozwinęły – potrzebujemy specjalizacji, żeby to wszystko zrozumieć i ogarnąć.

Nie my pierwsi przez to przechodzimy

Jednak nie my pierwsi przechodzimy przez ten proces.

Zaraz po wojnie „inżynier” był zawodem samym w sobie.

A dziś? 🙂

Będąc inżynierem od architektury oprogramowania, nie podjąłbym się nawet próby wykonania pracy inżyniera budownictwa, czy marynarki wojennej – zwyczajnie nie mam bladego pojęcia o tych dziedzinach.

Programowanie będzie jak pisanie, czy czytanie

Bardzo możliwe, że w pewnej przyszłości programować będzie umiał każdy – z tym że to programowanie będzie wyglądało inaczej niż dzisiaj. Będzie prostsze przez rozwój różnego rodzaju narzędzi, a przez to będzie bardziej dostępne dla zwykłych ludzi.

Może to wyglądać podobnie do umiejętności pisania, czy czytania. Na podstawowym poziomie każdy z nas to umie – jednak tylko nieliczni decydują się na napisanie książki, czy dostają nagrodę Nobla za swoją twórczość.

Podsumowanie i moja kapsuła czasu

Bardzo kibicuję tym trendom i sam staram się cały czas rozwijać, by również skorzystać na tych zmianach.

Jestem bardzo ciekaw, ile z tych przewidywań rzeczywiście się sprawdzi – i czy choć odrobinę mam rację 🙂

Tak sobie teraz myślę, że kiedy będę już dziadkiem – i ten blog dalej będzie istniał – to siądę w fotelu i przeczytam ten wpis, weryfikując swoje wcześniejsze założenia.

Pozdrawiam i powodzenia!

Tomek

2 komentarze
Share:
8 zaskakujących mitów na temat programowania i programistów

8 zaskakujących mitów na temat programowania i programistów

Wokół programowania powstało mnóstwo mitów – na pewno niejeden z nich obił Ci się o uszy 🙂

Niektórzy po ich usłyszeniu rezygnują z nauki programowania, więc dzisiaj przyjrzymy się kilku i sprawdzimy, ile mają wspólnego z rzeczywistością.

8 mitów na temat programowania – wprowadzenie

Z tego materiału dowiesz się m.in.:

  • Czy można być za starym, żeby zostać programistą?
  • Czy trzeba skończyć studia, żeby myśleć o programowaniu?
  • Ile naprawdę zarabia Junior Developer?

8 mitów na temat programowania i programistów

Mit #1. Żeby zostać programistą, muszę znać świetnie matematykę

Skąd to przekonanie? Może stąd, że w procesie edukacji dzielimy ludzi na humanistów oraz umysły ścisłe – a to zazwyczaj Ci drudzy są naturalnie wskazywani na adeptów sztuki programowania.

Prawda jest taka, że programista zazwyczaj nie musi wykonywać zaawansowanych zadań matematycznych, ale z pewnością przyda się mu umiejętność logicznego i analitycznego myślenia.

Jeśli z matematyką nie jest Ci po drodze, ale uważasz się za osobę umiejącą rozwiązywać problemy, to nie porzucaj jeszcze marzeń o programowaniu 🙂 Jeśli na jakimś etapie Twojej drogi pojawi się konieczność przeprowadzenia trudniejszych obliczeń, zawsze możesz użyć w tym celu specjalnych wtyczek czy programów.

mity programowanie_za staryMit #2. Jestem za stary/ stara na bycie programistą 👨‍🦳

Wiek zazwyczaj nie gra tutaj większej roli – ważniejsze jest zaangażowanie i chęć sumiennej nauki. Oraz praktyczne umiejętności 😉

Całe szczęście programowanie nie wymaga żadnych zdolności, które ograniczałby wiek – coraz częściej można spotkać ludzi, którzy zdecydowali się na zmianę branży po 30-stym roku życia (a nawet później!).

Co więcej, osoby, które posiadają już doświadczenie zawodowe, mają pewną przewagę, bo wiedzą już jak zaprezentować się na rozmowie kwalifikacyjnej, oraz jak radzić sobie w określonych sytuacjach.

mity programowanie_studia potrzebne by zostać programistąMit #3. By znaleźć pracę jako programista, muszę skończyć studia informatyczne 👩‍🎓

Tak wyglądała droga Tomka, ale Twoja nie musi 🙂

Pracodawcy oczekują wiedzy i umiejętności, a nie dyplomu – pokaże Ci to prawie każde ogłoszenie o pracę na stanowisko programisty.

Ukończone studia nie gwarantują, że kandydat posiada kompetencje i będzie świetnym pracownikiem. Z pewnością ktoś, kto poświęci czas na naukę, realizację projektów oraz przygotowanie odpowiedniego CV ma większe szanse na zdobycie pracy, niż ktoś, kto ukończył studia informatyczne, ale nie posiada portfolio.

Mit #4. Programowanie to tylko męskie zajęcie

To też oczywiście bzdura 🙂 Może faktycznie nadal obserwujemy przewagę mężczyzn–programistów, jednak kobiety, również coraz częściej wybierają tę ścieżkę kariery. Pracodawców nie obchodzi płeć, tylko umiejętności, więc każdy ma równe szanse.

Warto zauważyć również, że kobiety są dużo bardziej wymagające wobec siebie. Statystycznie większość pań nawet nie wyśle swojej kandydatury, jeżeli nie spełnia blisko 100% wymagań! Z drugiej strony są mężczyźni, których zazwyczaj zadowala 60% dopasowania. Ciekawe, co? 🙂

Branża IT jest bardzo przyjazna dla kobiet – wiele firm oferuje pracę zdalną/ elastyczne godziny pracy, co z perspektywy np. młodych mam jest szczególnie istotne. Branża IT daje chyba najwięcej możliwości połączenia życia prywatnego z karierą.

Mit #5. Po bootcampie łatwo znajdę pracę

Gdyby to tylko było takie proste 🙂

Bootcampy to rozwiązanie, które w ostatnim czasie bardzo zyskało na popularności. Budzi jednak również sporo kontrowersji – głównie za sprawą coraz bardziej skomercjalizowanego rynku, który zwyczajnie nadużywa tej formy nauki.

Sama idea jest jednak dość słuszna:

  • „wrzucamy” osobę zainteresowaną w projekt, czyli mamy pełne zanurzenie w nowy temat,
  • uczymy tylko tego co jest niezbędne,
  • i przez to w dość krótkim czasie możemy przygotować delikwenta do pracy.

Niestety idee nie zawsze mają wiele wspólnego z rzeczywistością…

Po bootcampie praktycznie zawsze trzeba zdobyć jeszcze dużo kolejnych umiejętności. Ważne jest również opanowanie umiejętności zaprezentowania się podczas rozmowy rekrutacyjnej, tak aby wyróżnić się wśród innych kandydatów.

👉 Od czego zacząć naukę programowania? Jakich języków się uczyć?

mity programowanie_bogaty junior developerMit #6. Jak tylko zostanę Junior Developerem, będę zarabiać [przynajmniej!] 10-15K 💰

Osobom, które podejmują decyzję o byciu programistą, wydaje się, że odkryli kopalnię złota i od momentu otrzymania pierwszej pracy będą kąpać się w pieniądzach – nie zaprzeczę, byłoby miło 🙂

Jednak prawdziwe życie wygląda nieco inaczej – Junior (w zależności od firmy) może liczyć na początkowe zarobki rzędu 3-4 tysięcy netto.

Oczywiście wraz ze zdobywaniem  doświadczenia zarobki będą rosnąć proporcjonalnie i dosyć szybko. W branży IT łatwo możesz podnieść swoje zarobki do kwoty, która Cię satysfakcjonuje. Wymaga to jednak nieustannej nauki, zaangażowania w projekty i rozwoju.

Mit #7. Język programowania wybiera się raz na zawsze

Wybór pierwszego języka to nie jest podpisanie cyrografu na całe życie. Powiem więcej, dość rzadko programiści zostają wierni swojemu pierwszemu językowi przez całą karierę. Zdecydowana większość osób zna po kilka/kilkanaście języków, a czasem i więcej. Każda kolejna poznana technologia poszerza Twój horyzont i sprawia, że trochę bardziej krytycznie patrzysz na swoje poprzednie wybory.

Dla programisty nauka nowego języka programowania jest naturalnym elementem rozwoju, który czasem jest wymuszony przez sytuację, a czasem jest zwyczajnie świadomą decyzją, by poszerzyć swoje horyzonty i możliwości zawodowe.

Mit #8. Wszyscy programiści to introwertycy

Programiści to też ludzie i można spotkać wśród nich cały wachlarz osobowościowy. Tak jak w każdej innej dziedzinie znajdziesz wesołków, głośnych ekstrawertyków, czy osoby ciche, nielubiące zbyt wiele atencji.

Jednak warto zaznaczyć, że obraz brudnego programisty z zarostem do kolan, siedzącego w bluzie z kapturem na głowie w piwnicy to totalny mit!

Umiejętności miękkie stanowią pochodną naszego charakteru i czasem bardzo ciężko jest je zmienić. Wiedzą o tym również osoby rekrutujące, dlatego zwracają na nie coraz większą uwagę. Braki w znajomości jakiejś technologii można nadrobić stosunkowo szybko. Jednak zmiana charakteru pracownika, tak by pasował do zespołu, to praca na wiele miesięcy, lub czasem jest po prostu niemożliwa.

👉 Umiejętności i kompetencje miękkie – soft skills

8 mitów na temat programowania – podsumowanie

Jaki jest morał tego wpisu? Nie ufaj wszystkiemu, co przeczytasz lub usłyszysz – myśl samodzielnie i weryfikuj informacje 🙂

No comments
Share:
Markdown – GitHub

Markdown – GitHub MarkDOWN, cheat sheet, link, table, image, …

Markdown to lekki język znaczników, który można używać do dodawania elementów formatowania do dokumentów tekstowych w postaci zwykłego tekstu. Jego celem jest łatwość w odczytywaniu i pisaniu. Za pomocą kilku znaczników pomoże nam podrasować wygląd tekstu, tak aby był bardziej przyjazny dla oka.

Markdown – wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest Markdown?
  • Jaka jest składnia w Markdown’ie?
  • Czy można stosować Markdown w GitHubie?

Markdown

Markdown to lekki język znaczników, który można używać do dodawania elementów formatowania do dokumentów tekstowych w postaci zwykłego tekstu. Markdown jest obecnie jednym z najpopularniejszych języków znaczników na świecie. Jest on szeroko stosowany m.in. w komunikatorach, forach internetowych, stronach www, dokumentacji i plikach readme.

Używanie Markdown różni się od takich aplikacji jak np. Microsoft Word, w której klikasz przyciski, aby sformatować słowa i frazy, a zmiany są widoczne natychmiast. Natomiast kiedy tworzysz plik w formacie Markdown, dodajesz składnię Markdown do tekstu, aby wskazać, które słowa i frazy powinny wyglądać inaczej.

Przykład Markdown

Fragment markdown:

# Nagłówek

Zostanie zamieniony na następujący kod HTML:

<h1>Nagłówek</h1>

Kilka ciekawych faktów na temat Markdown’u:

  • Twórcami są John Gruber i Aaron Swartz.
  • Markdown został stworzony w 2004 roku.
  • Oryginalnie został zaimplementowany w Perlu.
  • W 2014 r. udostępniono jednoznaczną specyfikację i zestaw testów dla Markdown.

Markdown | Markdown tutorial

Markdown cechuje prostota, która zwiększa czytelność, jak i ułatwia pisanie tekstu. Wszystko fajnie, ale pewnie zastanawiasz się, po co Ci tak właściwie Markdown? 🙃

Istnieją dość dobrze rozwinięte edytory tekstów, w których za jednym kliknięciem możesz szybko sformatować kod. Jest kilka powodów, dla których duża grupa osób wybiera właśnie Markdown:

  • Popularność – markdown stosuję się naprawdę w wielu miejscach, gdzie tworzy się tekst. Przykładowo jest używany do tworzenia stron internetowych, dokumentów, notatek, książek, prezentacji, wiadomości e-mail oraz dokumentacji technicznej. Są to elementy tak naprawdę, z których korzysta praktycznie każdy, kto obecnie nie żyje w jaskini pośrodku niczego 😏
  • Pliki zawierające tekst w formacie Markdown można otwierać za pomocą praktycznie dowolnej aplikacji. Jeśli uznasz, że nie podoba Ci się aktualnie używana aplikacja Markdown, możesz zaimportować swoje pliki Markdown do innej aplikacji Markdown. Stanowi to wyraźny kontrast z aplikacjami do przetwarzania tekstu, takimi jak Microsoft Word, które blokują zawartość w zastrzeżonym formacie pliku.
  • Markdown jest niezależny od platformy. Tekst w formacie Markdown można tworzyć na dowolnym urządzeniu z dowolnym systemem operacyjnym.
  • Obecnie wiele aplikacji oraz witryn internetowych obsługuję Markdown m.in. takie witryny jak Reddit czy GitHub.

Powyższe argumenty oraz łatwość użytkowania Markdown’u sprawiają, że z przyjemnością z niego korzystam w codziennej pracy.

Markdown editor

Istnieje sporo możliwości, jeżeli chodzi o wybór edytora Markdown’u. Wszystko tak naprawdę zależy od Twoich osobistych preferencji. Przykładowo – możesz użyć Visual Studio Code, Typora lub iA Writer, czy IntelliJ Idea – czyli naszego popularnego IDE.

Markdown cheat sheet | Ściągawka

Kiedy zaczynasz używać Markdown’u dobrze mieć małą ściągawkę, do której można zajrzeć w każdej chwili. Poniżej znajdziesz taką właśnie ściągawkę, która pomoże Ci w przyswojeniu elementów składni Markdown. Sam Markdown również udostępnia nam naprawdę przystępną i zrozumiała dokumentację, do której warto zajrzeć.

Markdown syntax | Składnia

Liczba opcji formatowania w Markdown jest dosyć ograniczona. Warto dlatego jest się nauczyć na pamięć, co niewątpliwie przyspieszy proces tworzenia i formatowania praktycznie dowolnego tekstu.

Markdown heading | Nagłówek

Aby uzyskać nagłówek, należy zastosować #<tekst> co będzie równe H1, ##<tekst> to H2 itd. np. #Hello lub ##Hello.

Markdown – Markdown syntax Markdown nagłówek

Markdown bold | Pogrubienie

W celu pogrubienia tekstu należy zastosować jedną z poniższych opcji:

  • **<tekst>** np. **Hello**,
  •  __<tekst>__ np. __Hello_.

Markdown – Markdown syntax Markdown bold

Markdown italic | Kursywa

Aby uzyskać kursywę tekstu, należy zastosować jedną z poniższych opcji:

  • *<tekst>* np. *Hello*,
  • _<tekst>_ np. _Hello_.

Markdown – Markdown syntax Markdown italic

Markdown bold & italic | Pogrubienie z kursywą

Możliwa jest również sytuacja, w której chcemy zarówno pogrubić tekst, jak i nadać mu kursywę.
W tym przypadku mamy kilka możliwości, aby to zrobić:

  • ***<tekst>*** np. ***Hello***,
  • ___<tekst>___ np. ___Hello___,
  • __*<tekst>*__ np. __*Hello*__,
  • **_<tekst>_** np. **_Hello_**.

Markdown – Markdown syntax Markdown bold italic

Markdown blockquote | Cytat

Jeżeli chcesz w tekście stworzyć tzw. blok cytatu (ang. blocquote), musisz dodać > <tekst> np. > Być albo nie być.
Blok cytatu powoduje dodanie charakterystycznego wcięcia z lewej strony.
Markdown – Markdown syntax Markdown bold

Markdown list | Lista

Lista uporządkowana (ang. ordered list)

Jeżeli chcesz w tekście stworzyć listę uporządkowaną, wystarczy, że postąpisz według poniższego schematu:

  • 1.<tekst> następnie 2.<tekst> itd. np. 1. Pierwszy, 2. Drugi.

Alternatywnie możesz nawet wypisać kolejne punkty jako:

  • 1. <tekst> 1.<tekst> 1.<tekst> np. 1. Pierwszy, 1. Drugi, 1. Trzeci,
  • 1.<tekst> 5.<tekst> 7.<tekst> np. 1. Pierwszy, 5. Drugi, 7. Trzeci.

Efekt końcowy będzie taki sam – lista sformatuje się poprawnie, wyliczając po kolei elementy.

 

Markdown – Markdown syntax Markdown list

 

Istnieje również możliwość stworzenia zagnieżdżonej listy:

Markdown – Markdown syntax Markdown list

Lista nieuporządkowana (ang. unordered list)

Jeżeli chcesz w tekście stworzyć listę nieuporządkowaną, musisz dodać odpowiedni symbol do każdej kolejnej linii, która ma tworzyć listę:

  • <tekst> następnie -<tekst> itd. np. – Pierwszy, – Drugi,
  • +<tekst> następnie +<tekst> itd. np. +Pierwszy, +Drugi,
  • *<tekst> następnie *<tekst> itd. np. *Pierwszy, *Drugi.

Markdown – Markdown syntax Markdown unordered list

Analogicznie jak w przypadku listy uporządkowanej i w tym przypadku możesz stworzyć zagnieżdżoną listę.

Lista z zadaniami (ang. task list)

Ciekawą opcją jest możliwość tworzenia tzw. task lists. Aby utworzyć taką listę, musisz dodać przed elementem listy – [x] lub – []:

  • – [x] <zadanie> (jeżeli task jest wykonany), – [] <zadanie> (jeżeli task nie został jeszcze wykonany) np.
    – [x] Zrobić zakupu,
    – [] Umyć naczynia.

Markdown – Markdown syntax Markdown task list

Markdown code | Kod

Aby dodać do tekstu fragment kodu, wystarczy użyć `<code>` np. `int i = 1;`.

Markdown – Markdown syntax Markdown code

Markdown horizontal | Pozioma linia

Aby utworzyć poziomą linię, należy użyć lub ***.

Markdown – Markdown syntax Markdown horizontal

Markdown link

Chcąc dodać link, należy użyć [<nazwa>](<url>)
np. [StormIt](https://www.stormit.pl).

Markdown image | Zdjęcie

Dodanie zdjęcia jest zbliżone do dodawania linku:
![<alt-tekst>](<zdjęcie>)
np. ![Markdown image](Markdown.jpg)

Markdown new line | Nowa linia

Chcąc dodać tekst w nowej linii, nie wystarczy pojedynczy enter.
Aby rozpocząć tekst w nowej linii, zastosuj podwójny enter lub html’owy znacznik <br>.

Markdown table | Tabela

Stworzenie tabeli w Markdown’ie wymaga trochę więcej pracy.

  • Kolumnę wyznacza się, używając ||.
  • Aby wyznaczyć nowy wiersz, wystarczy w nowej linii stworzyć kolejną kolumnę.
  • Jeżeli chcesz oznaczyć pierwszy wiersz jako nagłówek, musisz oddzielić go od kolejnego wiersza | ———– |.

Tabela dostosowuje swój rozmiar automatycznie do treści.

Markdown – Markdown syntax Markdown table

Możliwe jest również wyjustowanie tekstu. Aby to zrobić zamiast | ———– | zastosuj jedną z opcji:

  • | :—       | – Wyjustowanie do lewej,

  • :—-:   | – Wyśrodkowanie,

  • |       —: | – Wyjustowanie do prawej.

Nie musisz koniecznie ustawiać jednej opcji dla całej tabeli. Możesz również modyfikować pojedyncze kolumny.

Markdown comment | Komentarz

Zdarzają się sytuacje, kiedy chcemy pozostawić komentarz, który ma nie być widoczny, wtedy wykorzystaj jedną z poniższych opcji:

  • [comment]: <> (<komentarz>) np. [comment]: <> (Jestem komentarzem).
  • [//]: <> (<komentarz>) np. [//]: <> (Jestem komentarzem).

Markdown – Markdown syntax Markdown comment

 

Github Markdown

GitHub to bardzo wszechstronne narzędzie, które ciągle się rozwija. Posiada on również w swojej „ofercie” możliwość korzystania z Markdown’u.

GitHub łączy składnię do formatowania tekstu o nazwie GitHub Flavored Markdown z kilkoma unikalnymi funkcjami pisania.

Oprócz formatowania Markdown takiego jak pogrubienie i kursywa oraz tworzenie nagłówków, linków i list, pasek narzędzi zawiera funkcje specyficzne dla GitHub, takie jak @wzmianki, listy zadań oraz linki do problemów (ang. issues) i pull request’ów.

GitHub umożliwia włączenie czcionki o stałej szerokości w każdym polu komentarza w serwisie GitHub. Każdy znak w czcionce o stałej szerokości zajmuje tę samą przestrzeń poziomą, co może ułatwić edycję zaawansowanych struktur Markdown, takich jak tabele i fragmenty kodu.

Aby włączyć opcję stałej szerokości czcionki na swoim koncie GitHub, musisz udać się do zakładki ustawień (ang. settings).

Markdown – Markdown syntax Markdown GitHub

Następnie wejdź do zakładki wyglądu (ang. appearance).

Markdown – Markdown syntax Markdown GitHub

W ostatni, kroku musisz odhaczyć opcję dot. czcionki i już od teraz możesz korzystać z tej funkcjonalności.

Markdown – Markdown syntax Markdown GitHub

W tym materiale tylko wspominam o GitHub’ie w kontekście Markdown’u. Jeżeli nie masz jeszcze konta na GitHub’ie lub chciałbyś bliżej zapoznać się z tym narzędziem, to zapraszam do poniższego wpisu:

➡ ZOBACZ 👉: GitHub tutorial | GitHub desktop, GitHub actions

Markdown – podsumowanie

W ramach tego materiału zapoznaliśmy się z tematem Markdown. Poznaliśmy również składnię Markdown’u, która przyda Ci się w codziennej pracy z tekstem.

Jeżeli spodobał Ci się ten materiał lub jeśli dopiero co zaczynasz swoją przygodę z programowaniem i chcesz dobrze wejść w świat deweloperów, zapraszam Cię do zapoznania się z moim programem dotyczącym Javy:

➡ ZOBACZ 👉: Java od podstaw

No comments
Share:
Ciąg Fibonacciego – Fibonacci, Liczby Fibonacciego

Ciąg Fibonacciego – Fibonacci, Liczby Fibonacciego

Liczby Fibonacciego i ciąg Fibonacciego – to zagadnienie, które fascynuje ludzi od lat – i mimo iż spotykamy się z nim na co dzień, często nawet nie zdajemy sobie z tego sprawy.
Przyjrzyjmy się dziś bliżej tym niezwykłym liczbom i jak przystało na prawdziwych programistów – spróbujmy zmierzyć się z algorytmami wykorzystującymi ciąg Fibonacciego w praktyce.

Ciąg Fibonacciego – wprowadzenie

Z tego materiału dowiesz się:

  • Co to jest ciąg liczb Fibonacciego?
  • Kim był Leonardo Fibonacci?
  • Gdzie w przyrodzie, ciele człowieka oraz matematyce możemy spotkać liczby Fibonacciego?
  • Jak zaimplementować algorytmy iteracyjne i rekurencyjne wykorzystujące liczby Fibonacciego?

Ciąg Fibonacciego

Ciąg Fibonacciego – to ciąg liczb naturalnych w którym:

Ciąg Fibonacciego – wzór rekurencyjny

Ciąg Fibonacciego – wzór rekurencyjny

  • Pierwszy (czasem nazywany również zerowym – stąd rozbieżności w numeracji)
    wyraz jest równy 0;
  • Drugi wyraz jest równy 1;
  • Każdy kolejny element jest sumą dwóch poprzednich.

Ciąg Fibonacciego – wzór

Fibonacci series | Fibonacci sequence

Przykładowo 6. element Ciągu Fibonacciego będzie miał wartość:

Element ciągu 0 1 2 3 4 5 6
Wartość 0 1 1 2 3 5 8

Liczby Fibonacciego (ang. Fibonacci numbers)

IndexWartość
00
11
21
32
43
55
68
713
821
934
1055

Lista 1000 kolejnych liczb ciągu Fibonacciego.

Ciąg Fibonacciego – implementacja

Skoro wiemy już, czym jest Ciąg Fibonacciego, to spróbujmy zaimplementować algorytm, który wyświetli nam kolejne elementy tego ciągu.

W tym celu posłużymy się dwoma niezależnymi podejściami: iteracyjnym oraz rekurencyjnym.

O obu podejściach możesz przeczytać w podlinkowanych materiałach – tutaj skupimy się już na samych liczbach Fibonacciego.

➡ ZOBACZ 👉: Rekurencja ➿ rekursja ➿ rekurencja

➡ ZOBACZ 👉: Iteracja, iteracje – powtarzanie w programowaniu vs Rekurencja ➿

Ciąg Fibonacciego – wzór i implementacja rekurencyjna

Zgodnie ze wzorem rekurencyjnym – wiemy, że:

Ciąg Fibonacciego – wzór rekurencyjny

Ciąg Fibonacciego – wzór rekurencyjny

  • Pierwszy wyraz jest równy 0;
  • Drugi wyraz jest równy 1;
  • Każdy kolejny element jest sumą dwóch poprzednich.

Czyli przekładając to na kod Javy – mamy metodę z zaimplementowanym algorytmem rekurencyjnym:

int fibo(int i) {
	if (i == 0) {
		return 0;
	}
	if (i == 1) {
		return 1;
	}

	return fibo(i - 2) + fibo(i - 1);
}

Ciąg Fibonacciego – podejście rekurencyjne a stos wywołań

Podejście rekurencyjne, mimo iż jest bardzo proste w implementacji – nie jest jednak pozbawione wad.

Zobacz, jak wygląda stos wywołań, gdybyśmy chcieli obliczyć 6. element tego ciągu.

Fibonacci – rekurencja

Fibonacci – rekurencja

 

Przy każdym wywołaniu funkcji deklarowane są kolejne zmienne, które są zwalniane, dopiero jak funkcja zwróci wartość.
W naszym wypadku do obliczenia mamy tylko 6. element ciągu Fibonacciego – wyobraź sobie jednak, jak wyglądałoby to dla 100. lub 1000. elementu…

Nie jest to jednak jedyny sposób, w jaki możemy podejść do tego problemu – alternatywny sposób opiera się o algorytm iteracyjny.

Ciąg Fibonacciego – implementacja iteracyjna

Poniżej fragment kodu w wersji iteracyjnej:

int fibIter(int n){
	int a = 0, b = 1, t = 1;

	for (int i = 0; i < n; i++) {
		t = a;
		a = a + b;
		b = t;
	}

	return a;
}

To rozwiązanie jest szczególnie wygodne jeżeli chcemy wyświetlić kolejne elementy ciągu – a nie tylko jeden z nich.

  • Nasza funkcja przyjmuje argument, który określa który wyraz ciągu Fibonacciego mamy wyliczyć.
  • Algorytm zaczynamy od zadeklarowanie zmiennych:
    • a – początkowo pierwszy wyraz ciągu
    • b – początkowo drugi wyraz ciągu
    • t – zmienna pomocnicza
  • W kolejnych iteracjach naszej pętli:
    • przechowujemy w zmiennej pomocniczej wartość zmiennej a
    • zwiększamy wartość zmiennej a, o wartość b
    • ustawiamy wartość zmiennej b na wartość przechowaną w zmiennej pomocniczej
  • Dzięki temu – zmienna a przechowuje wartość i-tego elementu ciągu, a zmienna b jego poprzednika.

Ciąg Fibonacciego – optymalizacja algorytmów

Obie implementacje – pod względem wydajności świetnie sobie radzą przy stosunkowo niedużych liczbach. Na moim komputerze dla 20. elementu ciągu Fibonacciego gołym okiem nie byłem w stanie zobaczyć różnicy.

Sprawa oczywiście zacznie nam się komplikować, gdy spróbujemy obliczyć dalsze elementy ciągu – jak, chociażby 100. czy 1000.

➡ ZOBACZ 👉: Benchmark sposobem na wydajniejsze aplikacje – JMH

Wielkie liczby (ang. big numbers) – BigInteger

Pierwszym z problemów, na który trafiamy jest ograniczony zakres typu Integer.

Problem możemy lekko odsunąć w czasie, wykorzystując typ Long – jednak dopiero wykorzystaniem wbudowanych w Javie wielkich liczb i klasy BigInteger zapewnimy sobie wsparcie dla naprawdę dużego wyniku.

BigInteger fibo(int i) {
	if (i == 0) {
		return BigInteger.ZERO;
	}

	if (i == 1) {
		return BigInteger.ONE;
	}

	return fibo(i - 1).add(fibo(i - 2));
}

Ta prosta zmiana pozwala nam się cieszyć większymi wynikami bez konieczności martwienia się o zakres zmiennych.

Nie rozwiązuje to jednak problemów związanych z wydajnością i baaaardzo długim czasem oczekiwania na nasz wynik 🙂

Programowanie dynamiczne

Kolejną optymalizacją, którą możemy zastosować, jest programowanie dynamiczne.

Sama idea jest dosyć prosta i polega na identyfikacji problemów w ten sposób, by ich obliczenia wykonywać tylko raz – co w przypadku wersji rekurencyjnej ma BARDZO duże znaczenie.

Map<BigInteger, BigInteger> results = new HashMap<>();

BigInteger fibo(int i) {
	if (i == 0) {
		return BigInteger.ZERO;
	}

	if (i == 1) {
		return BigInteger.ONE;
	}

	if (results.containsKey(BigInteger.valueOf(i))) {
		return results.get(BigInteger.valueOf(i));
	}

	BigInteger result = fibo(i - 1).add(fibo(i - 2));

	results.put(BigInteger.valueOf(i), result);

	return result;
}

Więcej na temat programowania dynamicznego możesz przeczytać w podlinkowanym wpisie:

➡ ZOBACZ 👉: Programowanie dynamiczne – rekurencja ➿

Rekurencja ogonowa

Dobre wyniki optymalizacji daje również wprowadzenie rekurencji ogonowej.
Ta na pierwszy rzut oka niewielka zmiana w wersji algorytmu rekurencyjnego daje możliwość kompilatorowi wprowadzić znaczące optymalizacje i uniknąć kosztownego budowania ogromnego stosu wywołań.

public long fibo(int n) {
	return fibo(n, 1, 0);
}

private long fibo(int n, long a, long b) {
	if (n == 0) {
		return b;
	}

	return fibo(n - 1, a + b, a);
}

Temat rekurencji ogonowej rozwijam w poniższym wpisie:

➡ ZOBACZ 👉: Rekurencja ogonowa

Fibonacci – Leonardo Fibonacci

Leonardo Fibonacci

Leonardo Fibonacci

Leonardo Fibonacci – żył w latach 1175 – 1250.
Był to włoski matematyk pochodzący z Pizy.

To dzięki niemu między innymi posługujemy się cyframi arabskimi – jednak jego najbardziej znanym dziełem jest wzór określający kolejne wyrazy ciągu Fibonacciego.

Złota proporcja

Złota proporcjapodział harmoniczny, złoty podział, boska proporcja – czy też złoty środek 😃

Niezależnie od nazw, których jak widzimy, jest sporo – chodzi o zależność, która jak się okazało, występuje w bardzo wielu miejscach – w przyrodzie, w matematyce i również w ciągu Fibonacciego.

Jeżeli podzielimy odcinek na 2 części (lub weźmiemy odpowiadające jego długości 2 kolejne elementy ciągu Fibonacciego) – to stosunek długości dłuższego z nich do krótszego jest taki sam jak całego odcinka do części dłuższej.

 

Fibonacci – złoty podział odcinka

Przykładowo:

  • Mamy elementy ciągu: 1, 1, 2, 3, 5 – rozważmy 3 i 5
  • (5+3) / 5 = (powinno być równe) = 5/3
  • 8/5 = 5/3
  • 1,6 = 1,67
  • Mniej więcej się zgadza 😉

Okazuje się, że im większe wyrazy ciągu podzielimy, tym dokładniejsze przybliżenie uzyskamy.

Powstałą w ten sposób liczbę nazywamy właśnie – „złotą liczbą” i oznaczamy grecką literą φ (czyt. „fi”).

Właśnie ten stosunek udało się odnaleźć w bardzo wielu miejscach – zarówno w przyrodzie, jak i w matematyce.

Ciąg kwadratów, których długości boków są kolejnymi liczbami Fibonacciego

Ciąg kwadratów, których długości boków są kolejnymi liczbami Fibonacciego

Ciąg Fibonacciego w przyrodzie

Ciąg Fibonacciego – drzewo

Dla „idealnego drzewa” – gdyby ponumerować gałęzie zgodnie z wysokością, na której wyrosły – to okazuje się, że liczba gałęzi na tym poziomie jest liczbą Fibonacciego.

Spirala Fibonacciego (ang. Fibonacci spiral)

Przykładem spirali Fibonacciego w przyrodzie są muszle.

Ciąg Fibonacciego – muszle

Ciąg Fibonacciego – muszla

Ciało człowieka

A ciało człowieka?

Okazuje się, że liczb Fibonacciego można również doszukać się w naszym ciele – np. stosunek wzrostu człowieka do odległości od stóp do pępka też pasuje do złotej proporcji.

Ciąg liczb Fibonacciego na giełdzie

Liczby Fibonacciego znajdują również swoje zastosowanie między innymi w analizie technicznej na rynkach finansowych. Tzw. zniesienia Fibonacciego to linie odpowiadające poszczególnym wartościom procentowym, wynikającym z operacji na liczbach ciągu Fibonacciego.

Ciąg Fibonacciego – giełda

Ciąg Fibonacciego – podsumowanie

W ramach tego materiału przećwiczyliśmy wyliczanie liczb ciągu Fibonacciego, wykorzystując algorytm rekurencyjny oraz iterację. Dowiedzieliśmy się również więcej o tzw. złotym środku, a także gdzie w otaczającym nas świecie możemy „dopatrzeć się” liczb Fibonacciego.

Jeżeli spodobał Ci się ten materiał lub jeśli dopiero co zaczynasz swoją przygodę z programowaniem i chcesz dobrze wejść w świat deweloperów, zapraszam Cię do zapoznania się z moim programem dotyczącym Javy:

➡ ZOBACZ 👉: Java od podstaw

2 komentarze
Share:
Rekurencja, rekursja, rekurencja

Rekurencja ➿ rekursja ➿ rekurencja

Rekurencja (inaczej rekursja; ang. recursion), czyli odwoływanie się np. funkcji lub definicji do samej siebie.

Żeby zrozumieć rekurencję, trzeba najpierw zrozumieć – rekurencję…

Rekurencja – wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest rekurencja?
  • Jak wykonać rekurencję?
  • Czym jest rekurencyjny ciąg wywołań?
  • Jakie istnieją sposoby na optymalizację algorytmu rekurencyjnego?

Co to jest rekurencja (rekursja)?

W praktyce o funkcji rekurencyjnej mówimy, jeżeli logika funkcji polega między innymi na wywołaniu samej siebie.

Za pierwszym razem brzmi to trochę jak nieskończona pętla wywołań lub przekładając to na bardziej uniwersalny język jak pies, który goni swój ogon i próbuje go złapać, jednak nigdy mu się to nie udaje… 🐕

Czasami rzeczywiście może się zdarzyć, że taka metoda zapętli się i nie zwróci prawidłowego wyniku – co nie jest tak naprawdę pożądanym rezultatem. Dlatego tak ważne jest, by dobrze zaprojektować warunki końcowe, po których spełnieniu metoda ma przestać wywoływać samą siebie.

Każda definicja rekurencyjna potrzebuje przynajmniej jednego przypadku bazowego (nierekurencyjnego), który zakończy jej działanie.

Zobaczmy 🔎 to na prostym przykładzie.
Naszym zadaniem jest wyświetlenie rekurencyjnie liczb z przedziału od 0 do 10.

Iteracyjne podejście

W standardowym iteracyjnym podejściu moglibyśmy w tym celu wykorzystać pętlę for oraz dekrementację.

for (int i = 10; i >= 0; i--) {
	System.out.println(i);
}
  • Pętla zaczyna się od wartości licznika 10.
  • W ciele metody wyświetlamy licznik.
  • Następnie zmniejszamy go o 1.
  • Jeżeli licznik dalej jest większy bądź równy 0 – to wykonujemy kolejną iterację pętli.

Więcej o podejściu iteracyjnym i iteracji możesz przeczytać tutaj:

➡ ZOBACZ 👉: Iteracja, iteracje | powtarzanie w programowaniu vs Rekurencja ➿

Rekurencyjne podejście

Recursion – Rekurencja Algorytm

Zapiszmy teraz ten algorytm w wersji rekurencyjnej.

Zacznijmy od określenia warunków wyświetlenia podanej liczby:

  1. Jeżeli liczba jest większa od zera – to wywołaj metodę wyświetlania z liczbą pomniejszoną o jeden.
  2. W przeciwnym wypadku – nie rób nic (warunek bazowy).
display(10);

void display(int i) {
	System.out.println(i);

	if (i > 0) {
		display(i - 1);
	}
}

Rekurencyjny ciąg wywołań

Wynik działania programu w obu przypadkach będzie dokładnie taki sam – w kolejnych liniach zostaną wyświetlone liczby od 10 do 0.

Natomiast sam stos wywołań dla podejścia rekurencyjnego wyglądałby całkowicie inaczej:

display(10);
    10
    display(9);
        9
        display(8);
            8
            display(7);
                7
                display(6);
                    6
                    display(5);
                        5
                        display(4);
                            4
                            display(3);
                                3
                                display(2);
                                    2
                                    display(1);
                                        1
                                        display(0);
                                            0

Kolejne wywołania takiej funkcji nazywamy rekurencyjnym ciągiem wywołań.
Bardzo ważne jest poprawne określenie warunku bazowego (nierekurencyjnego), który zakończy wywołania rekurencyjne – w tym przypadku jest to if (i > 0), które musi być niespełnione, aby zakończyć rekurencję. Jeżeli źle zdefiniujemy ten warunek, to nasza aplikacja się zwyczajnie zapętli.

Derekursywacja

Gdybyśmy chcieli przeprowadzić operację odwrotną – czyli zamienić algorytm rekurencyjny na iteracyjny – mówimy wtedy o derekurencji.
Derekursywacja – czyli przekształcenie algorytmu rekursyjnego w odpowiadający mu funkcjonalnie algorytm iteracyjny.

Cechy algorytmów rekurencyjnych

  • Zakończenie algorytmu nie jest jasno określone – mamy, tylko warunek bazowy, który mówi nam, kiedy mamy skończyć rekurencję.
  • Ogólny, bardziej rozbudowany problem zostaje rozbity na mniejsze, elementarne problemy, które łatwiej jest rozwiązać.
  • Problem jest upraszczany tak długo, aż dojdziemy do warunku bazowego, który jest na tyle trywialny, że nie da się już go rozdrobnić i zwyczajnie podajemy jego wynik.
  • Główny problem przedstawiamy jako „mniejszą wersję” tego samego problemu.

Kiedy korzystać z rekurencji

Skoro wiele problemów możemy rozwiązać iteracyjne oraz rekurencyjne, to w takim przypadku kiedy zdecydować się na który sposób?

Zazwyczaj wybieramy rekurencję, kiedy:

  • Rozwiązywany problem da się zredukować do łatwiejszego podproblemu, który można dalej upraszczać – główny problem da się przedstawić jako „mniejszą wersję tego samego problemu”.
  • Możemy znaleźć bazowy przypadek, którego dalej już nie redukujemy.
!!Pamiętaj!! Rekurencja może być wolniejsza od wersji iteracyjnej ze względu na konieczność dodatkowych wywołań metody – może być, co nie znaczy, że zawsze tak jest – jednak warto zwrócić uwagę na wydajność obu rozwiązań.

Silnia a rekurencja (ang. factorial)

Przyjrzyjmy się teraz kilku praktycznym przykładom wykorzystania rekurencji.

Silnia (ang. factorial) z liczby N to iloczyn wszystkich liczb naturalnych dodatnich nie większych niż liczba N, czyli silnia z 7 to:

7! = 7*6*5*4*3*2*1

można to jednak zapisać również jako:

7! = 7 * 6!

Natomiast ten przypadek – bardzo łatwo jest już zapisać w formie funkcji rekurencyjnej:


int factorial(int n) {
	if (n <= 0) {
		return 1;
	}

	return n * factorial(n - 1);
} 

Wartości silni dla kolejnych 10 liczb:
0 => 1
1 => 1
2 => 2
3 => 6
4 => 24
5 => 120
6 => 720
7 => 5040
8 => 40320
9 => 362880
10 => 3628800

Ciąg Fibonacciego a rekurencja

Ciąg Fibonacciego – to kolejny problem, który bardzo często rozwiązywany jest w sposób rekurencyjny.

Co to jest Ciąg Fibonacciego?

Ciąg Fibonacciego – to ciąg liczb naturalnych w którym:

Ciąg Fibonacciego – wzór rekurencyjny

Ciąg Fibonacciego – wzór rekurencyjny

  • Pierwszy wyraz jest równy 0;
  • Drugi wyraz jest równy 1;
  • Każdy kolejny element jest sumą dwóch poprzednich.

Przykładowo 6. element Ciągu Fibonacciego będzie miał wartość:

Element ciągu 0 1 2 3 4 5 6
Wartość 0 1 1 2 3 5 8
Fibonacci – rekurencja

Fibonacci – rekurencja

Więcej o ciągu Fibonacciego możesz przeczytać tutaj:

➡ ZOBACZ 👉: 🐚 Ciąg Fibonacciego – Fibonacci, Liczby Fibonacciego

Rekurencyjne wyświetlenie wszystkich katalogów

Rekurencja katalogi

Kolejnym bardzo wyraźnym przykładem zastosowania rekurencji jest rekurencyjne wyświetlanie i przeszukiwanie katalogów w systemie plików.

Mamy główny katalog – np. w systemie Unix „/” – a w nim podkatalogi: /tmp, /run itp. Każdy z tych katalogów znowu ma swoje podkatalogi itp.

Dlatego chcąc np. znaleźć plik, przeszukując cały dysk, możemy:

  • Wejść do głównego katalogu.
  • Sprawdzić, czy nie ma tam naszego pliku.
  • Wyświetlić listę wszystkich podkatalogów i dla nich odpalić ten sam algorytm 😉

Rekursja – nie zawsze taka idealna

Rekurencja – jak każde inne narzędzie ma swoje plusy i minusy – a co za tym idzie, nie zawsze jest idealnym rozwiązaniem problemu.

Przyjrzyjmy się teraz kilku potencjalnym problemom, które musimy zaadresować w kontekście podejścia rekurencyjnego.

  • Skomplikowane problemy rekurencyjne mogą znacząco wpłynąć na wykorzystanie zasobów – ponieważ wymaga zapamiętywania między innymi adresów powrotu z wywołań rekurencyjnych.
  • Kompletnie niezależne rozwiązywanie podproblemów – tak że czasem jeden problem jest rozwiązywany wielokrotnie. Prześledź np. stos wywołań dla ciągu Fibonacciego dla 4 elementu – fib(2) zostaje obliczone dwukrotnie. Rozwiążemy za chwilę ten problem, korzystając z programowania dynamicznego.
  • Przy źle zdefiniowanym warunku kończącym istnieje ryzyko zapętlenia wywołań. Może to doprowadzić do przepełnienia stosu i pojawienia się bardzo niechcianego wyjątku StackOverflowError.

java.lang.StackOverflowError

Jeżeli korzystając z rekurencji, źle zdefiniujemy warunek bazowy i doprowadzimy do zapętlenia się algorytmu – można powiedzieć, że mleko 🥛 się już rozlało i nasz algorytm nie działa poprawnie.

Jednak taka sytuacja niesie za sobą jeszcze inne negatywne konsekwencje – prędzej czy później skończą nam się zasoby wykorzystywane na stosie wywołań (ang. stack) i JVM (ang. java virtual machine) poinformuje nas o tym niechlubnym błędem: java.lang.StackOverflowError.

Recursion – Rekurencja StackOverflowError

Więcej na ten temat pisałem w kontekście stosu i LIFO – dlatego zachęcam do zapoznania się z poniższym tekstem:

➡ ZOBACZ 👉: Stos (Stack) – 7+ tajników implementacji LIFO 

➡ ZOBACZ 👉: Stos – StackOverflowError

Programowanie dynamiczne

Programowanie dynamiczne jest jednym ze sposobów na optymalizację złożonych algorytmów rekurencyjnych.

Sama idea jest dosyć prosta i polega na identyfikacji podproblemów w ten sposób, by ich obliczenia wykonywać tylko raz.

Zobaczmy to na przykładzie:

int fibRecDP(int i) {
	if (i == 0) {
		return 0;
	}
	if (i == 1) {
		return 1;
	}

	if(results.containsKey(i)){
		return results.get(i);
	}

	int result = fibRecDP(i - 2) + fibRecDP(i - 1);
	results.put(i, result);

	return result;
}

W tym przykładzie rozbudowaliśmy pierwotną metodę rekurencyjną o dwie rzeczy:

  1. Sprawdzamy, czy wcześniej nie obliczaliśmy już takiego problemu – i jeżeli tak to zwracamy wynik.
  2. Przechowujemy obliczenia w pomocniczej mapie.

Dzięki tej prostej optymalizacji zużyjemy odrobinę więcej pamięci na przechowanie wyników, jednak znacząco skrócimy czas obliczeń dla większej ilości elementów ciągu Fibonacciego.

Rekurencja ogonowa (ang. tail recursion)

Rekurencja ogonowa jest kolejnym sposobem na optymalizację algorytmów rekurencyjnych.
Tym razem musimy jednak przeorganizować nasz algorytm w taki sposób, by pomóc kompilatorowi odpowiednio go zoptymalizować.

Rekurencja ogonowa wymusza na nas spełnienie dwóch warunków:

  1. Może być tylko jedno wywołanie rekurencyjne w kodzie naszej metody i to wywołanie jest na samym końcu metody.
  2. Wyniku kolejnego wywołania nie można modyfikować – więc potrzebujemy dodatkowej zmiennej na przechowanie aktualnego wyniku, który na sam koniec zostanie zwrócony.

Prawda, że proste? 😏

To spróbujmy przemodelować nasz algorytm ciągu Fibonacciego:

long fibRecTail(int n, long a, long b) {
	if (n == 0) {
		return b;
	}

	return fibRecTail(n - 1, a + b, a);
}

Na pierwszy rzut oka obie wersje wyglądają bardzo podobnie – jednak te drobne różnice pozwalają na bardzo znaczącą optymalizację.

Jeżeli wykorzystywany przez nas kompilator obsługuje optymalizację rekurencji ogonowej (większość współczesnych kompilatorów to robi) – zauważy wtedy, że nie ma potrzeby zapisywania ramki na stosie, ponieważ i tak później ma zwrócić wynik kolejnego wywołania. Dzięki tej drobnej różnicy stos wywołań nie rośnie już przy każdym kolejnym zagnieżdżeniu, a ma stały rozmiar.

Rekurencja ➿ – podsumowanie

W ramach tego materiału przećwiczyliśmy tworzenie algorytmów rekurencyjnych. Zapoznaliśmy się również z 2 sposobami na optymalizację algorytmu rekurencyjnego – rekurencją ogonową i programowaniem dynamicznym.

Jeżeli spodobał Ci się ten materiał lub jeśli dopiero co zaczynasz swoją przygodę z programowaniem i chcesz dobrze wejść, w świat deweloperów zapraszam Cię do zapoznania się z moim programem dotyczącym Javy:

➡ ZOBACZ 👉: Java od podstaw

No comments
Share:
Iteracja, iteracje – powtarzanie w programowaniu vs Rekurencja ➿

Iteracja, iteracje – powtarzanie w programowaniu vs Rekurencja ➿, Iteracja, iteracje

Iteracja, czyli powtarzanie tego samego fragmentu logiki określoną ilość razy lub aż do momentu, kiedy zostaną spełnione założone warunki. Z iteracją spotykamy się najczęściej przy pracy z pętlami, tablicami i innymi kolekcjami danych.

Iteracja – wprowadzenie

Z tego materiału dowiesz się:

  • Czym jest iteracja?
  • Czym jest algorytm iteracyjny?
  • Do czego może służyć algorytm iteracyjny?
  • Jak wygląda i jak działa przykładowa pętla w kodzie?

Iteracja

Iteracja oznacza powtarzanie tej samej operacji (zestawu instrukcji) z góry określoną liczbę razy lub aż do spełnienia określonego warunku. Kiedy sekwencja instrukcji jest wykonywana w sposób powtarzalny, nazywa się to pętlą.

Wyobraź sobie, że posiadasz listę uczniów w klasie. Twoim zadaniem jest wyczytanie po kolei w ten sam sposób imienia i nazwiska każdego z uczniów. Wiesz, że w klasie jest 20 uczniów. Czytasz imię i nazwisko po czym, przechodzisz do kolejnej osoby i w identyczny sposób wyczytujesz kolejnego ucznia, aż dojdziesz do ostatniego – 20-stego i wtedy zakończysz wyczytywanie. I w ten oto sposób przeiterowałeś się przez listę uczniów 😉

Przykładowo poprzez iterację można również:

  • Wymienić wszystkie dni tygodnia;
  • Pobrać elementy z kolejki, dopóki coś w niej jest;
  • Wyświetlić liczby całkowite od 0 do 10 itp. itd.

W tym wypadku – iteracja to jedno powtórzenie takiej pętli.

Algorytm iteracyjny

Iteracja – Wieże Hanoi Algorytmy

Algorytm iteracyjny – to po prostu algorytm, który opiera swoje działanie o iteracje, czyli powtarzanie danej operacji określoną ilość razy lub aż do spełnienia określonego warunku.

Niektóre problemy algorytmiczne można rozwiązać zarówno w sposób iteracyjny, jak i korzystając z algorytmów rekurencyjnych np. problem wież Hanoi, czy Ciąg Fibonacciego.

Często podczas procesu rekrutacji na stanowiska deweloperskie można spotkać się z zadaniem zaimplementowania takich algorytmów.

Pętla iteracyjna

Pętle są jednym z podstawowych narzędzi wykorzystywanych przez programistę. Dzięki nim można np. wywołać określoną funkcjonalność podaną ilość razy, zamiast za każdym razem wywoływać ją ręcznie.

Tablice i kolekcje to struktury, bez których ja przynajmniej nie wyobrażam sobie programowania. Nieodłącznym towarzyszem kolekcji jest na pewno pętla iteracyjna. Niezależnie od wielkości naszej kolekcji, działania na jej elementach dzięki pętli iteracyjnej możesz wykonać tylko raz, ograniczając znacząco linie zbędnego kodu. Nie ma w tym przypadku znaczenia czy będziesz usuwał, czy dodawał elementy do kolekcji – nie wpłynie to na strukturę kodu wewnątrz pętli.

Ucząc się programować, powinniśmy od samego początku używać dobrych praktyk, które pomogą tworzyć przejrzysty i zrozumiały kod dla nas, ale i dla innych programistów. Jedną z zasad jest DRY (ang. don’t repeat yourself), czyli po prostu – nie powtarzaj się. Pętla jest bardzo dobrym przykładem na stosowanie tej zasady👌🏻

Przykładowo, zamiast wypisywać kolejno dni tygodnia w 7 instrukcjach, możemy wykorzystać do tego pętlę.
W przypadku tygodnia mamy tylko 7 instrukcji, ale gdybyśmy chcieli wyświetlić dni z miesiąca, roku albo nawet kilku lat?
W takich sytuacjach pętlę okazują się niezrównane.

Jeżeli chcesz lepiej poznać zagadnienie pętli, zajrzyj do poniższego postu:

➡ ZOBACZ 👉: Pętla (for, while, do while, foreach)| Kurs Java ➿ 

Pętla iteracyjna – przykład

Prześledźmy działanie prostego algorytmy iteracyjnego z wykorzystaniem pętli na przykładzie.

Naszym zadaniem będzie zsumowanie kolejnych 3 liczb naturalnych, zaczynając od 1.

int result = 0;
for(int i = 1; i <= 3; i++){
  result += i;
}

Działanie iteracji

Działanie naszej przykładowej pętli polega na powtarzaniu kolejnych iteracji – przy czym dla każdej iteracji modyfikowana jest zmienna sterująca oraz sprawdzane są warunki, czy w ogóle możemy wykonać kolejną iterację.

Prześledźmy to krok po kroku:

  • Przypisz wartość początkową zmiennej sterującej – zmienna i=1;
  • Sprawdź, czy wartość zmiennej i mieści się w dopuszczalnym zakresie – czyli i <=3;
  • Ponieważ warunek pętli jest spełniony, przechodzimy do wykonywania iteracji – czyli w tym wypadku wykonujemy ciało pętli – result += i (gdzie i=1);
  • Na zakończenie iteracji zwiększamy wartość licznika o 1 – i++ (czyli go inkrementujemy);
  • Rozpoczynamy kolejną iterację od sprawdzenia warunku pętli – ponieważ jest on spełniony (2<=3), możemy wykonać ciało pętli i na koniec iteracji ponownie zwiększyć licznik pętli, który teraz jest równy 3;
  • Kolejna iteracja to – spełniony warunek pętli (3<=3) i na końcu zwiększony licznik pętli do wartości 4;
  • Tym razem jednak kolejna iteracja się już nie wykona – ponieważ wartość licznika przekroczyła założony zakres (4<=3);
    Dlatego też kończymy wykonanie całej pętli.

Rekurencja

Rekurencja (inaczej rekursja; ang. recursion) – jest podejściem alternatywnym do iteracyjnego i w uproszczeniu polega na odwoływaniu się np. funkcji lub definicji do samej siebie.

Więcej o rekurencji możesz przeczytać tutaj:

➡ ZOBACZ 👉: Rekurencja ➿ rekursja ➿ rekurencja

Iteracja – podsumowanie

W ramach tego materiału przećwiczyliśmy pracę z iteracjami. Zapoznaliśmy się również z tematem algorytmu iteracyjnego oraz pętli iteracyjnej.

Jeżeli spodobał Ci się ten materiał lub jeśli dopiero co zaczynasz swoją przygodę z programowaniem i chcesz dobrze wejść w świat deweloperów, zapraszam Cię do zapoznania się z moim programem dotyczącym Javy:

➡ ZOBACZ 👉: Java od podstaw

No comments
Share:
Git tutorial

Git tutorial | stash, rebase, commit, merge, checkout, push i clone

Tutorial Git wprowadzi Cię krok po kroku w tajniki systemów kontroli wersji. Kurs oparty jest o przykłady z objaśnieniami konkretnych zagadnień. Z powodzeniem można go potraktować jako monolit i przeczytać od deski do deski, ale sprawdzi się również jako ściąga z wybranych funkcjonalności Git.

Git – wprowadzenie

Z tego materiału dowiesz się:

  • Jakie istnieją rodzaje systemów kontroli wersji?
  • Czym jest Git?
  • Jakie są stany plików w Gicie?
  • Jak skonfigurować Git’a?
  • Jakie możliwości daje Ci Git?
  • Czym jest Git-flow?

Systemy kontroli wersji kodu źródłowego

Systemy kontroli wersji odpowiedzialne są za śledzenie wszystkich zmian dokonywanych w plikach. Za ich pomocą można podejrzeć wcześniej dokonane zmiany oraz, w razie potrzeby, powrócić do starszej wersji pliku. Dzięki nim można również sprawdzić kto i kiedy dokonał tych zmian.

Kiedy jesteś w większym projekcie, dość dynamicznie pojawia się dużo zmian. Zdarzają się sytuacje, w których wprowadzona linia kodu jest błędna lub też chcesz dopytać twórcę, dlaczego coś jest tak, a nie inaczej. Średnim pomysłem byłoby chodzenie od biurka do biurka z pytaniem: Czy to Ty wprowadziłeś tę zmianę? O wiele łatwiejsze jest sprawdzenie w systemie kontroli wersji, kto i kiedy dokonał zmiany. Dzięki niemu szybko będziesz mógł określić twórcę danego kodu.

Zanim opowiem Ci o najpopularniejszym obecnie systemie kontroli wersji, jakim jest Git, chcę przedstawić Ci jeszcze inne rozwiązania. Aby lepiej zrozumieć Git’a warto poznać jego prostsze alternatywy 👨🏼‍🏫

Lokalne systemy kontroli wersji

Najprostszą formą lokalnego systemu kontroli wersji jest metoda polegająca na przekopiowaniu wszystkich plików do osobnego katalogu. Nowo powstały katalog można też, oznaczyć datą powstania lub numerem wersji.

Takie podejście, z racji swojej prostoty, jest dosyć kuszące i jednocześnie wystarczające dla wielu podstawowych sytuacji. Jednak jego główną wadą jest brak odporności na błędy użytkownika. Przykładowo – wystarczy pomylić katalog, na którym się pracuje i można w ten sposób stracić całą swoją pracę.

W celu zabezpieczenia się przed tym dosyć szybko powstały proste systemy kontroli wersji oparte o bazę danych. W lokalnej bazie przechowywane były kolejne zmiany, które były dokonywane na wersjonowanych plikach. Dzięki temu można było już porównywać ze sobą zmiany z kolejnych wersji oraz, w razie czego, przywrócić stabilną wersję.

Jednym z najbardziej popularnych systemów tego typu jest RCS (Revision Control System).

Lokalny system kontroli wersji
Niestety lokalne systemy kontroli wersji mają jedną znaczącą wadę. Ogranicza je możliwość wykonywania zmian tylko do jednego komputera 💻 👎🏼

Scentralizowane systemy kontroli wersji

Scentralizowane systemy kontroli wersji CVCS (Centralized Version Control System) powstały w celu umożliwienia pracy na plikach, z więcej niż jednej maszyny 💻 💻

W tym celu zaprojektowano model, w którym jest jeden centralny serwer przechowujący wszystkie wersjonowane pliki oraz maszyny klienckie. Klienci łączą się do niego, w celu pobrania najnowszych zmian. Ten model jest już na tyle funkcjonalny, że jest wykorzystywany powszechnie do dziś.

Wielką zaletą tego rozwiązania jest możliwość zarządzania uprawnieniami z jednego miejsca. Dzięki temu można również łatwo zweryfikować zmiany wprowadzone przez każdego uczestnika procesu.

Natomiast główny problem systemów scentralizowanych związany jest z ich potencjalną awaryjnością. Wystarczy, że główny serwer będzie przez chwilę niedostępny, a żaden klient nie może pobrać najnowszych zmian oraz zapisać swojej pracy. W przypadku uszkodzenia dysku z przechowywanymi plikami tracona jest również cała historia projektu.

Najbardziej popularne systemy tej klasy to CVS i Subversion.

Scentralizowany system kontroli wersji

Rozproszone systemy kontroli wersji

Zdecydowanie bardziej rozwiniętą wersją, jeżeli mówimy o systemach kontroli wersji są rozproszone systemy kontroli wersji DVCS (Distributed Version Control System). Główna różnica polega na tym, że klienci nie pobierają tylko najnowszych zmian, a całe dostępne repozytorium. Dlatego w przypadku awarii głównego serwera wystarczy przekopiować pliki od jednego z klientów na nowy serwer i nie zostanie stracona historia projektu. W razie potrzeby klient też sam może służyć jako serwer główny dla innych klientów. Wygląda to trochę tak, że klienci dogadują się, który z nich będzie pełnił funkcję serwera i łączą się do niego.

Rozproszone systemy kontroli wersji

Ciekawostką jest również to, że wiele z systemów tej klasy potrafi współpracować jednocześnie z kilkoma różnymi serwerami zewnętrznymi. Daje to ogromne możliwości, jeżeli chodzi o stosowanie różnych modeli współpracy.

Najpopularniejsze systemy tego typu to: Git oraz Mercurial.

Git | Git tutorial

Obecnie Git jest najpopularniejszym rozproszonym systemem kontroli wersji.

Poznajmy Git’a trochę bliżej:

  • System został stworzony przez Linusa Torvalds jako narzędzie wspomagające rozwój jądra Linux.
  • Git oficjalnie został wydany w 2005 roku.
  • Obecnie jest rozwijany na zasadach wolnej licencji GNU GPL v2.

Praca na zmianach

Podstawową cechą odróżniającą Gita od innych podobnych narzędzi jest sposób przechowywania zmian w lokalnej bazie danych. Większość systemów (np. CVSSubversion) przechowuje informacje o nowej wersji jako różnicową listę zmian, jakie zaszły na plikach.
Git natomiast tworzy swego rodzaju migawkę (ang. snapshot) stanu repozytorium w danym momencie. W zapisanym w ten sposób obrazie repozytorium przechowane są informacje o wszystkich plikach, jakie są przechowywane w repozytorium. W celach optymalizacyjnych, jeżeli w danej wersji plik nie był zmieniany, przechowywana jest tylko referencja do jego najnowszej wersji.

Praca lokalnie

Większość operacji wykonywanych przy pomocy Gita nie wymaga połączenia do Internetu. Jest to ogromny krok w przód w porównaniu do innych systemów. Nie tylko przeglądanie historii wersji trwa wielokrotnie szybciej, ale np. do rozpoczęcia pracy z nowym plikiem nie trzeba już łączyć się do zdalnego serwera.
Dzięki temu, że prawie wszystko dzieje się offline, praca z systemem kontroli wersji już dużo szybsza. Dopiero na koniec pracy potrzebne jest połączenie internetowe, w celu wysłania swojej pracy na serwer lub pobrania najnowszych zmian.

Stany plików w Git

Żeby móc pracować z Gitem trzeba zrozumieć, w jakich stanach mogą znajdować się zarządzane przez system pliki. Git wprowadza trzy główne stany dla zmian: zmodyfikowany, śledzony oraz zatwierdzony.

  • Zmodyfikowany — plik był edytowany, ale zmiana o tym nie została jeszcze nigdzie zapisana.
  • Śledzony — zmodyfikowany plik został oznaczony do zatwierdzenia przy najbliższej operacji commit.
  • Zatwierdzony — dokonana zmiana została zapisana i utrwalona w lokalnej bazie danych.

Przesłanie zmian do zdalnego repozytorium jest już operacją opcjonalną.
Stany plików Git
Każdy stan wiąże się bezpośrednio z miejscem, w którym konkretna zmiana się znajduję:

  • Katalog roboczy — jest to odtworzony obraz wersji projektu. To właśnie zawartość tego katalogu jest modyfikowana przez użytkownika.
  • Przechowalnia (stage) — to miejsce pośrednie, między katalogiem roboczym a lokalną bazą danych. Dzięki niej można utrwalić tylko wybrane zmiany.
  • Katalog Git — to trzon lokalnego repozytorium. W nim Git przechowuje metadane o plikach oraz obiektową bazę danych. Ten katalog jest kopiowany podczas klonowania repozytorium.

Git install – instalacja Git

Jeżeli nie masz jeszcze Gita zainstalowanego lokalnie – szybko możesz to nadrobić.
W poniższym materiale znajdziesz instrukcję, jak można to zrobić.

➡ ZOBACZ 👉: Install Git – Instalacja Git, Windows, Ubuntu – Bash, GUI

Konfiguracja Git

Git posiada trzy poziomową konfigurację. Każdy kolejny poziom jest bardziej szczegółowy i można z niego nadpisać bardziej ogólną konfigurację. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest konfiguracja git’a sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git config – konfiguracja git, config, global, local

Aliasy

Dla często wykorzystywanych komend powstał mechanizm aliasów. Dzięki nim można przypisać wywołanie komendy, wraz z wszystkimi jej argumentami, do prostszego aliasu i korzystać z niego, jak z wbudowanej komendy.

Poniżej zaprezentowany jest przykład bardzo długiej komendy wyświetlającej historię zmian oraz przypisanie jej do aliasu.

git alias – git tutorial
Dużo łatwiej jest zapamiętać polecenie git hist niż jego pierwotną wersję.

Wywołanie zewnętrznej aplikacji z aliasu

Chcąc lepiej zintegrować gita z zewnętrznymi systemami, można posłużyć się wywołaniem zewnętrznej komendy z poziomu aliasu. W tym przypadku należy takie wywołanie poprzedzić wykrzyknikiem.

git alias – git tutorial

Klient GUI dla Gita

Niewątpliwie obsługa systemów z linii komend daje największe możliwości. Jednak nie zawsze mamy ochotę uczyć się na pamięć tych wszystkich komend. Dlatego w kursie będą pokazane alternatywne drogi: z poziomu konsoli i klienta GUI.

Git dorobił się już naprawdę pokaźnej listy aplikacji klienckich. Do tej pory najlepiej pracowało mi się na Windowsie na GitExtensions i TortoiseGit oraz na Linuksie na SmartGit i GitKraken.

Git tutorial | Git create repository– tworzenie nowego repozytorium

Jeżeli nie posiadasz jeszcze repozytorium, możesz je utworzyć na dwa sposoby: sklonować istniejące już repozytorium lub stworzyć całkowicie nowe.

Git init | Git init repo – utworzenie nowego repozytorium

W celu zainicjowania nowego repozytorium, będąc w docelowym katalogu, należy wykonać poniższą komendę.

git init

git init – git

Powyższy przykład pokazuje, że komenda git init wygeneruje strukturę nowego repozytorium w katalogu .git. Jednak jeżeli w katalogu znajdowały się już jakieś pliki, żeby rozpocząć śledzenie ich zmian, trzeba je jeszcze dodać do przechowalni (ang. stage) i je zapisać (ang. git commit).

Git clone – klonowanie repozytorium

W ramach tego materiału zajmujemy się przede wszystkim ogólnym zagadnieniem, jakim jest git – natomiast jeżeli chcesz poznać bliżej zagadnienie git clone, sprawdź w poniższy wpis.

➡ ZOBACZ 👉: Git branch | git branch create, rename, delete, clone, checkout, merge

Rejestrowanie zmian w repozytorium

Rejestrowanie zmian w repozytorium podzielone jest na kilka etapów bezpośrednio związanych z cyklem życia zmian.

Git log – historia zmian

Przyjrzymy się teraz bliżej możliwościom przeglądania historii projektu. Do przeglądania historii służy polecenie git log.

Domyślnie git log bez podania żadnych argumentów wyświetla zmiany od najnowszego do najstarszego.

Poniżej widać, zapisaną (ang. git commit) przeze mnie zmianę. Oprócz treści commit’a dostaliśmy także informację o hash’u commit’a. Możemy również podejrzeć, kiedy dany commit był utworzony i kto był jego autorem.

git log – git r

Polecenie log jest bardzo rozbudowane i zawiera wiele opcji konfiguracyjnych, ich pełną listę można znaleźć, korzystając z pomocy git help log.

Git commit | Git amend | Git add | Git status | Git diff

Git posiada kilka ciekawych opcji dotyczących rejestrowania zmian w repozytorium m.in. git commit lub git add. Jeżeli chcesz je bliżej poznać, sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git commit | git commit, amend, add, status, diff

Git tag – tagowanie źródeł

Tagowanie (etykietowanie źródeł) to mechanizm pozwalający na oznaczenie ważniejszych miejsc w historii zmian projektu. Najczęściej jest wykorzystywany do oznaczania wersji aplikacji (np. wersja 11.0, itp.). Jeżeli chcesz je bliżej poznać zagadnienie, jakim jest tagowanie w gicie, sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git tag – tagowanie w git, add, push, checkout

Gitignore – ignorowanie plików

W większości projektów mamy do czynienia z plikami, których nie chcemy wersjonować. Są to np. pliki generowane automatycznie lub przechowujące skeszowane dane. Dodanie ich do repozytorium powoduje tylko zaciemnienie obrazu wprowadzanych zmian.

Można, co prawda, pomijać tego typu pliki przy zatwierdzaniu zmian, jednak nie jest to zbyt pragmatyczne podejście. Dużo lepszym wyjściem jest oznaczenie takiej klasy plików jako ignorowane. Od tego momentu nie będą nawet widoczne jako pliki zmodyfikowane.

Mechanizm ignorowana plików oparty jest o plik tekstowy .gitignore. Poniżej przykładowa zawartość:

*.tmp
tmp

Kolejne klasy ignorowanych plików wpisujemy w osobnych linijkach. Pierwsza linijka odpowiedzialna jest za ignorowanie wszystkich plików o rozszerzeniu .tmp, natomiast druga za cały katalog tmp oraz jego zawartość.

Warto już na starcie zdefiniować, które pliki mają być ignorowane. Pozwoli to w przyszłości na uniknięcie zabawy z niepotrzebnymi plikami.

Ponieważ plik .gitignore jest zwykłym plikiem tekstowym przechowywanym w głównym katalogu repozytorium, on również może podlegać wersjonowaniu. Po jego dodaniu lub modyfikacji warto zakomitować naniesione zmiany, lub jego też oznaczyć do ignorowania 😉

Git revert – cofanie zmian

Git umożliwia kilka opcji, jeżeli chodzi o cofanie zmian. Jedną z nich jest git revert. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git revert sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git revert – git revert commit, revert last commit

Git reset – cofanie zmian

Bardziej rozbudowaną alternatywą dla git revert jest polecenie git reset. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git reset, sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git reset – Git reset hard, git reset to origin

Git branch – praca z gałęziami w Gicie

Git branch jest jedną z kluczowych funkcjonalności Gita, dzięki której stał się on tak popularny. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git branch i możliwości, jakie daje, sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git branch | git branch create, rename, delete, checkout, merge

Git checkout| Git switch – przełączanie się między gałęziami

Pracując z gałęziami (ang. git branches) umiejętność przełączania się między branch’ami jest bardzo przydatna. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git checkout oraz git switch, sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git checkout, git checkout remote branch, git switch

Git merge – scalanie gałęzi

Pracując na osobnych branch’ach dochodzimy do punktu, w którym chcemy złączyć nasze zmiany z resztą projektu. Do scalania zmian służy polecenie git merge. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git merge i dowiedzieć się jak radzić sobie z konfliktami (ang. merge conflicts), sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git merge, git merge branch

Git remote – praca ze zdalnym repozytorium

Dzięki wykorzystaniu możliwości zdalnego repozytorium można współpracować z innymi osobami, nie ograniczając się już tylko do pracy na jednym komputerze. Git umożliwia współpracę jednocześnie z kilkoma różnymi zdalnymi repozytoriami. Można z nich pobierać kod, wysyłać swoje zmiany oraz zarządzać gałęziami kodu itp.

Dodanie zdalnego repozytorium

Konfiguracja nowego zdalnego repozytorium polega na wywołaniu komendy w ogólnym formacie: git remote add <nazwa> <url>.

Od tego momentu nowe zdalne repozytorium jest już dowiązane do naszego lokalnego.

git remote add – git 

Git SSH

W dzisiejszych czasach bezpieczeństwo jest na miarę złota. Dlatego łącząc się ze zdalnym repozytorium, powinniśmy łączyć się, korzystając z bezpiecznego połączenia SSH (ang. git ssh).

➡ ZOBACZ 👉: Git SSH

Git push – wysłanie lokalnych zmian do zdalnego repozytorium

Posiadając zdalne repozytorium, wielokrotnie powstaje potrzeba, a czasem i konieczność wypychania (ang. git push) lokalnych zmian do zdalnego repozytorium. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git push sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git push – git integracja ze zdalnym repozytorium, git push, ssh, remote

Git fetch| Git pull – pobranie zmian ze zdalnego repozytorium

Wykonując różne operacje na gałęziach (ang. branch), konieczne jest odświeżanie stanu gałęzi (ang. git fetch) oraz scalanie brakujących danych ze swoim lokalnym repozytorium (ang. git pull). Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git fetch oraz git pull sprawdź poniższy wpis.

➡ ZOBACZ 👉: Git fetch vs. pull | Git fetch, git pull

Git Flow – czyli sposoby pracy z gałęziami

Wprowadzenie lekkiego i szybkiego modelu zarządzania gałęziami w Gicie przyczyniło się do powstania wielu różnych schematów pracy opartych o rozgałęzianie kodu. Jeżeli chcesz bliżej poznać zagadnienie, jakim jest git flowsprawdź poniższy wpis.

➡ ZOBACZ 👉: Git flow – wydajny system zarządzania gałęziami w Git

Git – System kontroli wersji – podsumowanie

W ramach tego materiału dowiedzieliśmy się, czym jest system kontroli wersji. Bliżej zapoznaliśmy się z Git’em i jego możliwościami. Jeżeli chcesz kontynuować swoją przygodę z gitem – to zapraszam do dodatkowych materiałów o GitHub’ie:

➡ ZOBACZ 👉: GitHub tutorial | GitHub desktop, GitHub actions

6 komentarzy
Share: