Sierpień 7, 2018

Aplikacja Todo

Aplikacja Todo

Ze względu, że w pierwszym tygodniu poznałeś wszystkie podstawowe zagadnienia związane z programowaniem w Javie. Nadszedł czas na sprawdzenie się – stworzymy wspólnie krótką aplikację do zarządzania zadaniami i projektami. W tej lekcji pokażę jak stworzyć część do zarządzania zadaniami, zaś Twoim zadaniem będzie stworzenie części projektowej. Oczywiście będziesz mógł zobaczyć moje rozwiązanie w kolejnym wpisie.

Założenia

Tak jak wspomniałem aplikacja będzie miała przechowywać zadania oraz projekty. Czyli będzie mogli tworzyć nowe zadania i projekty, usuwać je oraz wyświetlać je wszystkie. Do stworzenia aplikacji wykorzystamy wiedzę zdobytą w pierwszym tygodniu. Zaczynajmy!

Szablon

Klasycznie ruszmy od czystego szablonu klasy wraz z metodą main:

public class Main {
    public static void main(String[] args) {
        
    }
}

Ruszajmy z tworzeniem aplikacji.

Potrzebne pola…

Będziemy potrzebowali oczywiście dwóch tablic – jedną do przechowywania projektów, drugą do przechowywania zadań. Obie zainicjalizujemy sobie wstępnie na 100 zadań i 100 projektów.

static String [] tasks = new String[100];
static String [] projects = new String[100];

Dodatkowo stworzymy sobie tablicę do przechowywania logów – czyli spisu wszystkich operacji, które były wykonane w obrębie aplikacji.

static String [] changeLog = new String[100];

Następnie będziemy potrzebowali jakiś zmiennych gdzie będziemy przechowywać index tablicy, który nie jest jeszcze zarezerwowany przez zadanie/projekt/log. Za każdym dodaniem jednej z tych rzeczy będziemy inkrementować konkretną zmienną – wkrótce wszystko się wyjaśni. Teraz zainicjalizujmy je.

static int tasksCount = 0;
static int projectsCount = 0;
static int changeLogCount = 0;

Będą to pola, które wykorzystamy do naszej aplikacji – przejdźmy dalej, do tworzenia metod do obsługi wszystkich funkcjonalności.

Póki co wszystkie zmienne i metody będą statyczne – później pokażę Ci o co chodzi ze słowem static i jak można się go „pozbyć”.

Nasz kod aktualnie wygląda tak:

public class Main {
    
    static String [] tasks = new String[100];
    static String [] projects = new String[100];
    static String [] changeLog = new String[100];

    static int tasksCount = 0;
    static int projectsCount = 0;
    static int changeLogCount = 0;
    
    public static void main(String[] args) {
        
    }
}

Menu

Pierwszą rzeczą jaką musimy zrobić to stworzyć metodę do wyświetlania menu użytkownikowi. Zwykła, prosta funkcja void.

public static void displayMenu() {
    System.out.println("1 - Create new task");
    System.out.println("2 - Remove task");
    System.out.println("3 - Create new project");
    System.out.println("4 - Remove project");
    System.out.println("5 - Display all tasks");
    System.out.println("6 - Display all projects");
    System.out.println("7 - Display change log");
    System.out.println("0 - Exit app");
}

I to tyle, będziemy jej używać do pokazania użytkownikowi pod jaką liczbą jaka jest ukryta funkcjonalność.

Dodawanie zadania

Zacznijmy od dodawania nowego zadania do naszej tablicy.

Sygnatura metody będzie wyglądać tak:

public static void addTask(String task) {
}

Jest to metoda typu void, która jako argument przyjmuję nazwę zadania. Czas z nim coś zrobić – czas go dodać do tablicy.

Przed dodaniem do tablicy, jednak musimy sprawdzić czy task zmieści się jeszcze w naszej tablicy. Czyli musimy sprawdzić czy wartość zmiennej tasksCount jest mniejsza od długości tablicy zadań – aby nie wyjść poza zakres tablicy! Krótko mówiąc nasz index nie może przekroczyć 99 – bo nasza tablica jest indeksowana od 0 do 99.

boolean hasCapacityForNewTask = tasksCount < tasks.length;

Jeżeli otrzymamy prawdę to czas na dodanie zmiennej do tablicy:

if (hasCapacityForNewTask) {
    tasks[tasksCount] = task;
}

Do odpowiedniego miejsca w tablicy przypisujemy nasze zadanie podane przez argument do funkcji.

Jeszcze musimy tylko zwiększyć nasz taskCount – aby podczas następnego dodawania zadania trafić na puste miejsce w tablicy.

tasksCount++;

Operator ++ zwiększa wartość zmiennej o 1.

Czyli nasza metoda wygląda tak:

public static void addTask(String task) {
    boolean hasCapacityForNewTask = tasksCount < tasks.length;
    if (hasCapacityForNewTask) {
        tasks[tasksCount] = task;
        tasksCount++;
    }
}

Dodawanie zadań mamy za sobą, czas przejść dalej.

Usuwanie zadania

Trochę trudniejszą funkcjonalnością jest usuwanie zadania – szczególnie, gdzie tak jak my przechowujemy nasze zadania w tablicy. Musimy zadbać o to, aby między zadaniami nie było żadnych luk – czyli usuwanie zadań będzie polegało na przesuwaniu zadań „w dół” o jeden index, które są „nad” usuwanym zadaniem. Trochę zagmatwane, przejdźmy jednak do przykładu.

public static void removeTask(int indexTask) {
}

Tak wygląda sygnatura naszej metody – jako argument przekazujemy index elementu, który ma zostać usunięty.

Przed samym usunięciem musimy sprawdzić czy w ogóle podany indexTask wskazuje na istniejące zadanie – czyli indexTask będzie musiał być mniejszy od tasksCount. Taka zależność musi być spełniona, ponieważ tasksCount wskazuje na pierwsze puste miejsce w tablicy – czyli stosujemy operator <, aby nasz index był o jeden mniejszy od tasksCount – czyli ostatni task w tablicy.

boolean isTaskExist = indexTask < tasksCount;

Jeżeli ten warunek jest spełniony to możemy brać się za usuwanie – czyli przesuwanie tablicy.

if (isTaskExist) {
    for(int i=indexTask; i<tasksCount-1;i++) {
        tasks[i] = tasks[i+1];
    }
}

Już tłumaczę co tu się dzieje.

Naszym punktem startowym pętli jest i = indexTask – czyli startujemy od indexu, który jest podany jako argument. To właśnie to miejsce w tablicy chcemy „przycisnąć” innymi zadaniami.

Następnym warunkiem jest i < tasksCount  – czyli dzięki temu warunkowi przejdziemy po wszystkich zadaniach, które występują – nie do końca tablicy, tylko do miejsca, gdzie znajduję się ostatnie zadanie! W warunku występuje – 1, ponieważ w pętli mamy i + 1 – gdybyśmy nie mieli -1 to wyszlibyśmy poza zakres zadań w tablicy.

tasks[i] = tasks[i+1] odpowiada za przesuwanie wartości w tablicy – czli np. do tasks[0] przypisujemy wartość tasks[1] itd. – przesuwamy nasze zadania ” w dół”.

Po przesunięciu wszystkich zadań nie możemy oczywiście zapomnieć o zmniejszeniu indeksu ostatniego zadania – w końcu, wszystkie zadania zostały przesuniętę w dół.

tasksCount--;

I teraz cała nasza funkcja odpowiedzialna za usuwanie zadania wygląda tak:

public static void removeTask(int indexTask) {
    boolean isTaskExist = indexTask < tasksCount;
    if (isTaskExist) {
        for(int i=indexTask; i<tasksCount-1;i++) {
            tasks[i] = tasks[i+1];
        }
        tasksCount--;
    }
}

Wyświetlanie zadań

Na sam koniec musimy wyświetlić tą naszą całą listę zadań użytkownikowi – w końcu musi wiedzieć jakie zadania ma do wykonania.

Będzie to po prostu metoda, zawierająca pętle, w której wyświetlimy istniejące zadania.

public static void displayTasks() {
}

W niej dajmy informację o tym co się dzieje:

System.out.println("List of tasks: ");

A w pętli wyświetlmy zadania:

for (int i=0; i<tasksCount;i++) {
    System.out.println(tasks[i]);
}

Spójrz na warunek graniczny – wyświetlamy do końca zakresu występowania zadań w tablicy – a nie do samego końca tablicy. Nie wyświetlamy całej tablicy, ponieważ od tasks[tasksCount] znajdują się puste wartości – a dokładnie null.

Czyli cała nasza metoda wyświetlająca wygląda tak:

public static void displayTasks() {
    System.out.println("List of tasks: ");
    for (int i=0; i<tasksCount;i++) {
        System.out.println(tasks[i]);
    }
}

Mamy już trzy podstawe metody naszej aplikacji – czas je sprawdzić w działaniu.

Uruchamiamy

Zanim jeszcze uruchomimy musimy wszystko ładnie zgrać w metodzie main, skąd startuje aplikacja.

Na początek będziemy potrzebowali Scannera do odczytu wartości od użytkownika oraz pętli while, która ma za zadanie utrzymywać naszą aplikację „przy życiu” – czyli będzie pętlą (prawie) nieskończoną – dzięki za każdym razem będziemy dostawać wyświetlone menu oraz będziemy mogli skorzystać z aplikacji.

Scanner scanner = new Scanner(System.in);
while(true) {
}

Aby faktycznie pętla była prawie nieskończona, dodajmy w warunku zmienną boolean, która pozwoli na wyjście z pętli – czyli zakończenie działania aplikacji.

Scanner scanner = new Scanner(System.in);
boolean isApplicationRun = true;
while(isApplicationRun) {
}

Wystarczy tylko teraz zmienić isApplicationRun na false i aplikacja zakończy swoje działanie – ponieważ pętla while wykonuje się dopóki warunek jest prawdziwy!

Dodajmy jeszcze zmienną radix, do której będziemy wczytywać numer opcji wybranej przez użytkownika.

Scanner scanner = new Scanner(System.in);
int radix;
boolean isApplicationRun = true;
while(isApplicationRun) {
}

Czas przejść do „wrzucenia” naszej aplikacji do pętli while. Wyświetlmy użytkownikowi menu oraz poprośmy o to jaką opcję chce wybrać.

while(isApplicationRun) {
  displayMenu();
  System.out.print("Type number to choose option: ");
  radix = scanner.nextInt();
}

Skoro mamy już opcję jaką użytkownik wybrał to czas na sterowanie działania programu – możemy do tego użyć if lub switch. My wybierzemy switch.

    while(isApplicationRun) {
      displayMenu();
        System.out.print("Type number to choose option: ");
      radix = scanner.nextInt();

      switch(radix) {
            
            default:
                System.out.println("Nie ma takiej opcji.");
                break;
        }
    }

}

Od razu dodałęm do niej default, który wyświetli się, gdy żadna z wymienionych opcji nie zostanie wybrana.

Dodajmy teraz nasze funkcjonalności do switcha.

Na początku dodawanie zadania – będzie to case 1, ponieważ nasze menu mówi, że po naciśnięciu można dodać zadanie.

case 1:
     System.out.println("Podaj nazwę zadania do dodania: ");
     String task = scanner.next();
     addTask(task);
     break;

Czyli prosimy o podanie nazwy zadania, wczytujemy go oraz wywołujemy naszą napisaną metodę addTask. I mamy już opanowane dodawanie zadań.

Dodajmy jeszcze możliwość usuwania zadań – czyli case 2:

case 2:
    System.out.println("Podaj index zadania do usunięcia: ");
    int index = scanner.nextInt();
    removeTask(index);
    break;

Działa to analogicznie do case 1 – prosimy o index do usunięcia, wczytujemy go i wywołujemy wcześniej napisaną metodę removeTask.

Musimy jeszcze zapewnić case 5 – wyświetlanie listy zadań.

case 5:
    displayTasks();
    break;

Jest to tylko wywołanie metody displayTasks() – ona już odpowiada za wyświetlanie zadań.

Zakończenie aplikacji

Nie mamy jeszcze obsłużonego zamknięcia aplikacji – czyli case 0.

Co musimy zmienić, aby zakończyć naszą aplikację? Musimy wyjść z pętli while – która jest sterowana warunkiem isApplicationRun – aktualnie jest ustawiona na true. Wystarczy ją zmienić na false i nasza pętla już się nie wykona.

case 0:
    isApplicationRun=false;
    break;

Czyli ostatecznie nasza funkcja main wygląda tak:

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
  int radix;
  boolean isApplicationRun = true;
    while(isApplicationRun) {
      displayMenu();
        System.out.print("Type number to choose option: ");
      radix = scanner.nextInt();

      switch(radix) {
            case 1:
                System.out.println("Podaj nazwę zadania do dodania: ");
                String task = scanner.next();
                addTask(task);
                break;
            case 2:
                System.out.println("Podaj index zadania do usunięcia: ");
                int index = scanner.nextInt();
                removeTask(index);
                break;
            case 5:
                displayTasks();
                break;
            case 0:
                isApplicationRun=false;
                break;
            default:
                System.out.println("Nie ma takiej opcji.");
                break;
        }
    }

}

Testowanie

Nasza cała aplikacja aktualnie powinna wyglądać tak:

import java.util.Scanner;

public class Main {

    static String [] tasks = new String[100];
    static String [] projects = new String[100];
    static String [] changeLog = new String[100];

    static int tasksCount = 0;
    static int projectsCount = 0;
    static int changeLogCount = 0;

    public static void displayMenu() {
        System.out.println("1 - Create new task");
        System.out.println("2 - Remove task");
        System.out.println("3 - Create new project");
        System.out.println("4 - Remove project");
        System.out.println("5 - Display all tasks");
        System.out.println("6 - Display all projects");
        System.out.println("7 - Display change log");
        System.out.println("0 - Exit app");
    }

    public static void addTask(String task) {
        boolean hasCapacityForNewTask = tasksCount < tasks.length;
        if (hasCapacityForNewTask) {
            tasks[tasksCount] = task;
            tasksCount++;
        }
    }

    public static void removeTask(int indexTask) {
        boolean isTaskExist = indexTask < tasksCount;
        if (isTaskExist) {
            for(int i=indexTask; i<tasksCount-1;i++) {
                tasks[i] = tasks[i+1];
            }
            tasksCount--;
        }
    }

    public static void displayTasks() {
        System.out.println("List of tasks: ");
        for (int i=0; i<tasksCount;i++) {
            System.out.println(tasks[i]);
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
      int radix;
      boolean isApplicationRun = true;
        while(isApplicationRun) {
          displayMenu();
            System.out.print("Type number to choose option: ");
          radix = scanner.nextInt();

          switch(radix) {
                case 1:
                    System.out.println("Podaj nazwę zadania do dodania: ");
                    String task = scanner.next();
                    addTask(task);
                    break;
                case 2:
                    System.out.println("Podaj index zadania do usunięcia: ");
                    int index = scanner.nextInt();
                    removeTask(index);
                    break;
                case 5:
                    displayTasks();
                    break;
                case 0:
                    isApplicationRun=false;
                    break;
                default:
                    System.out.println("Nie ma takiej opcji.");
                    break;
            }
        }

    }
}

Uruchommy ją w celu jej przetestowania – dodajmy 5 zadań, wyświetlmy je oraz usuńmy dwa ze środka i na koniec zobaczmy stan listy zadań.

Stan zadań po dodaniu zadań:

List of tasks: 
Zakupy
Pranie
Prasowanie
Bieganie
Taniec

*Usuń 2 – Pranie i prasowanie i sprawdźmy stan listy.

List of tasks: 
Zakupy
Bieganie
Taniec

Wszystko śmiga jak powinno – podstawowa wersja aplikacji działa.

Podsumowanie

Wspólnie przygotowaliśmy pierwszą część aplikacji – zarządzanie zadaniami – teraz czas na Ciebie. Twoim zadaniem jest obsługa projektów – ma to wyglądać w taki sam sposób jak w przypadku zadań. Dodatkowo musisz obsłużyć change log – czyli w tablicy musimy przechowywać listę wykonanych operacji np. Dodano zadanie X. Usunięto zadanie Y. 

Pamiętaj, aby dodawanie wartości do change log napisać oddzielną metodę, która będzie wywoływana podczas wykonywania operacji – jako argument przekazuj wiadomość jaka ma być zapisana.

Przykładowe rozwiązanie aplikacji możesz znaleźć tutaj.

Jeżeli jest coś dla Ciebie niezrozumiałe lub masz z czymś problemy to proszę Cię o pisanie na grupie Facebook, ponieważ wtedy szybciej otrzymasz pomoc – nie tylko ode mnie, ale również od innych kursantów!