System wypożyczeń
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 100 z 107.
Wyzwanie
ŁatwyZbudujmy system wypożyczania na potrzeby naszego projektu zarządzania biblioteką! Utworzysz strukturę Library, która zarządza zbiorami książek i użytkowników, oraz metody obsługujące wypożyczanie i zwroty — z odpowiednią walidacją.
Rozmieścisz kod w czterech plikach:
book.go: StrukturaBookz poprzedniej lekcji, zawierająca polaID,Title,Author,ISBNiAvailable. Dodaj konstruktorNewBook, który inicjalizuje książki jako dostępne.user.go: StrukturaUserz polamiID,Name,EmailiBorrowedBooks(wycinek identyfikatorów książek). Dodaj konstruktorNewUser, który inicjalizuje użytkowników z pustą listą wypożyczonych książek.library.go: Utwórz strukturęLibrary, która przechowuje książki i użytkowników w mapach (z identyfikatorami jako kluczami). Zbuduj następujące elementy:- Konstruktor
NewLibrary, który inicjalizuje puste mapy książek i użytkowników - Metodę
AddBook, która dodaje książkę do biblioteki - Metodę
AddUser, która dodaje użytkownika do biblioteki - Metodę
Borrow, która przyjmuje identyfikator użytkownika i książki, sprawdza poprawność operacji, oznacza książkę jako niedostępną, dodaje jej identyfikator do listy wypożyczonych książek użytkownika i zwraca błąd, jeśli coś pójdzie nie tak - Metodę
Return, która cofa proces wypożyczenia: oznacza książkę jako dostępną i usuwa ją z listy wypożyczonych książek użytkownika
Metoda
Borrowpowinna zwracać następujące błędy:user not found, jeśli identyfikator użytkownika nie istniejebook not found, jeśli identyfikator książki nie istniejebook not available, jeśli książka jest już wypożyczona
Metoda
Returnpowinna zwracać następujące błędy:user not found, jeśli identyfikator użytkownika nie istniejebook not found, jeśli identyfikator książki nie istniejebook not borrowed, jeśli książka jest już dostępna
- Konstruktor
main.go: Połącz wszystkie elementy: utwórz bibliotekę, dodaj książki i użytkowników, a następnie obsłuż operacje wypożyczenia i zwrotu.Wczytaj liczbę książek, a następnie dla każdej książki wczytaj jej identyfikator, tytuł, autora i numer ISBN. Następnie wczytaj liczbę użytkowników, a dla każdego użytkownika jego identyfikator, imię i nazwisko oraz adres e-mail. Na koniec wczytaj liczbę operacji, z których każda ma postać
borrow [userID] [bookID]lubreturn [userID] [bookID].Dla każdej operacji wypisz
OK, jeśli zakończy się powodzeniem, lubError: [message], jeśli się nie powiedzie.
Dane wejściowe będą miały następującą postać:
- Liczba książek, a następnie identyfikator, tytuł, autor i numer ISBN każdej z nich (każda wartość w osobnym wierszu)
- Liczba użytkowników, a następnie identyfikator, imię i nazwisko oraz adres e-mail każdego z nich (każda wartość w osobnym wierszu)
- Liczba operacji, a następnie każda operacja w osobnym wierszu
Na przykład dla danych:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.com
4
borrow U001 B001
borrow U001 B001
return U001 B001
borrow U002 B001Oczekiwany wynik:
OK
Error: book not available
OK
Error: user not foundA dla danych:
1
B100
Design Patterns
Gang of Four
978-0201633610
2
U001
Bob Jones
bob@email.com
U002
Carol White
carol@email.com
3
borrow U001 B100
borrow U002 B100
return U001 B100Oczekiwany wynik:
OK
Error: book not available
OKA dla danych:
1
B001
Go in Action
William Kennedy
978-1617291784
1
U001
Dave Miller
dave@test.com
2
return U001 B001
borrow U001 B999Oczekiwany wynik:
Error: book not borrowed
Error: book not foundZwróć uwagę, że Library pełni rolę centralnego koordynatora. Sprawdza poprawność wszystkich operacji przed zmianą stanu, dzięki czemu dane o książkach i użytkownikach pozostają zsynchronizowane. Odbiorniki wskaźnikowe pozwalają metodom modyfikować rzeczywiste dane przechowywane w mapach!
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Wczytaj liczbę książek
var numBooks int
fmt.Fscanln(reader, &numBooks)
// TODO: Utwórz nową bibliotekę
// TODO: Wczytaj dane każdej książki (ID, tytuł, autor, ISBN — każde w osobnym wierszu)
// i dodaj je do biblioteki
for i := 0; i < numBooks; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
author, _ := reader.ReadString('\n')
author = strings.TrimSpace(author)
isbn, _ := reader.ReadString('\n')
isbn = strings.TrimSpace(isbn)
// TODO: Utwórz książkę i dodaj ją do biblioteki
}
// Wczytaj liczbę użytkowników
var numUsers int
fmt.Fscanln(reader, &numUsers)
// TODO: Wczytaj dane każdego użytkownika (ID, imię i nazwisko, adres e-mail — każde w osobnym wierszu)
// i dodaj je do biblioteki
for i := 0; i < numUsers; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
email, _ := reader.ReadString('\n')
email = strings.TrimSpace(email)
// TODO: Utwórz użytkownika i dodaj go do biblioteki
}
// Wczytaj liczbę operacji
var numOps int
fmt.Fscanln(reader, &numOps)
// TODO: Przetwórz każdą operację
// Format: "borrow [userID] [bookID]" lub "return [userID] [bookID]"
for i := 0; i < numOps; i++ {
line, _ := reader.ReadString('\n')
line = strings.TrimSpace(line)
parts := strings.Split(line, " ")
// TODO: Przeanalizuj operację i wywołaj odpowiednią metodę
// Wypisz "OK" w przypadku powodzenia lub "Error: [message]" w przypadku niepowodzenia
}
}
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania kodu15Projekt: Zarządzanie biblioteką
Przegląd i struktura projektuStruktury książek i użytkownikówPoćwicz samodzielnie: Kompilator Go online