Катя обнови решението на 05.12.2015 17:31 (преди над 2 години)
+package main
+
+import (
+        "encoding/json"
+        "encoding/xml"
+        "errors"
+        "fmt"
+        "sync"
+)
+
+// Creates a new library
+func NewLibrary(librarians int) Library {
+        m := make(map[string]*Book)
+        librariansChan := make(chan Librarian, librarians)
+        return &FancyLibrary{books: m, librarians: librariansChan, mutex: sync.Mutex{}}
+}
+
+// Librarian working in the library
+type Librarian struct {
+        request  chan LibraryRequest
+        response chan LibraryResponse
+        library  *FancyLibrary
+}
+
+// The librarian waits for requests and helps borrowing a book,
+// returning a book, getting information about a book
+func (librarian *Librarian) serve() {
+        go func() {
+                for {
+                        message, ok := <-librarian.request
+                        if ok {
+                                isbn := message.GetISBN()
+                                requestType := message.GetType()
+                                switch requestType {
+                                case BorrowBook:
+                                        libraryResponse := librarian.borrowBook(isbn)
+                                        librarian.response <- &libraryResponse
+                                case ReturnBook:
+                                        libraryResponse := librarian.returnBook(isbn)
+                                        librarian.response <- &libraryResponse
+                                case GetAvailability:
+                                        libraryResponse := librarian.getAvailability(isbn)
+                                        librarian.response <- &libraryResponse
+                                default:
+                                        librarian.response <- &CoolLibraryResponse{book: nil,
+                                                err: errors.New("Невалидна заявка")}
+                                }
+                        } else {
+                                break
+                        }
+                }
+        }()
+}
+
+// Reduces the available count for book with given isbn
+// Returns information about the book
+// Returns error if the book is not in the library or all copies are taken
+func (librarian *Librarian) borrowBook(isbn string) CoolLibraryResponse {
+        librarian.library.mutex.Lock()
+        book, ok := librarian.library.books[isbn]
+        var err error = nil
+        if !ok {
+                err = errors.New("Непозната книга " + isbn)
+        } else {
+                if book.availableCount == 0 {
+                        err = errors.New("Няма наличност на книга " + isbn)
+                }
+
+                book.availableCount -= 1
+                librarian.library.books[isbn] = book
+        }
+        librarian.library.mutex.Unlock()
+        return CoolLibraryResponse{book: book, err: err}
+}
+
+// Increases the available count for book with given isbn
+// Returns information about the book
+// Returns error if the book is not in the library
+// or all the books of this type are already in the library
+func (librarian *Librarian) returnBook(isbn string) CoolLibraryResponse {
+        librarian.library.mutex.Lock()
+        book, ok := librarian.library.books[isbn]
+        var err error = nil
+        if !ok {
+                err = errors.New("Непозната книга " + isbn)
+        } else {
+                if book.availableCount == book.registeredCount {
+                        err = errors.New("Всички копия са налични " + isbn)
+                }
+
+                book.availableCount += 1
+                librarian.library.books[isbn] = book
+        }
+        librarian.library.mutex.Unlock()
+        return CoolLibraryResponse{book: book, err: err}
+}
+
+// Returns information about the book
+// Returns error if the book is not in the library
+func (librarian *Librarian) getAvailability(isbn string) CoolLibraryResponse {
+        librarian.library.mutex.Lock()
+        book, ok := librarian.library.books[isbn]
+        var err error = nil
+        if !ok {
+                err = errors.New("Непозната книга " + isbn)
+        }
+        librarian.library.mutex.Unlock()
+        return CoolLibraryResponse{book: book, err: err}
+}
+
+type Person struct {
+        FirstName string `xml:"first_name" json:"first_name"`
+        LastName  string `xml:"last_name" json:"last_name"`
+}
+
+// The type for the books that are stored in the library
+type Book struct {
+        ISBN            string `xml:"isbn,attr"`
+        Title           string `xml:"title"`
+        Author          Person `xml:"author"`
+        registeredCount int
+        availableCount  int
+}
+
+// Returns a string representation of the book
+func (book Book) String() string {
+        return "Book: [" + book.ISBN + "] " + book.Title + " от " + book.Author.FirstName +
+                " " + book.Author.LastName
+}
+
+// The library
+type FancyLibrary struct {
+        books      map[string]*Book
+        librarians chan Librarian
+        mutex      sync.Mutex
+}
+
+// Adds a book to the library
+// Returns the count of all available copies in the library
+// Return an error of the number of copies is more than 4
+func (library *FancyLibrary) addBook(book *Book) (int, error) {
+        // Assuming that books with same ISBN are the same
+        library.mutex.Lock()
+        same_book := library.books[book.ISBN]
+        if same_book != nil {
+                // there are already 4 copies of the book - return error
+                if same_book.registeredCount == 4 {
+                        return 4, errors.New("Има 4 копия на книга " + book.ISBN)
+                }
+                same_book.registeredCount += 1
+                same_book.availableCount += 1
+        } else {
+                book.registeredCount = 1
+                book.availableCount = 1
+                library.books[book.ISBN] = book
+        }
+        library.mutex.Unlock()
+        return library.books[book.ISBN].availableCount, nil
+}
+
+// Add a book from json
+// Returns the count of all available copies in the library
+// Return an error of the number of copies is more than 4
+func (library *FancyLibrary) AddBookJSON(data []byte) (int, error) {
+        book := new(Book)
+        err := json.Unmarshal(data, book)
+        if err != nil {
+                return 0, err
+        }
+        return library.addBook(book)
+}
+
+// Add a book from xml
+// Returns the count of all available copies in the library
+// Return an error of the number of copies is more than 4
+func (library *FancyLibrary) AddBookXML(data []byte) (int, error) {
+        book := new(Book)
+        err := xml.Unmarshal(data, book)
+        if err != nil {
+                return 0, err
+        }
+        return library.addBook(book)
+}
+
+// Gets a free librarian to serve requests
+// Librarians are fixed - passed as an argument to NewLibrary func
+// Blocks if all librarians are busy
+// Returns two channels
+// write channel -  for sending of requests
+// read channel - for receiving results
+// On closing the request (write channel) the librarian is released
+func (library *FancyLibrary) Hello() (chan<- LibraryRequest, <-chan LibraryResponse) {
+        // The librarians can get up to 100 requests before someone reads the response
+        request := make(chan LibraryRequest, 100)
+        response := make(chan LibraryResponse, 100)
+        librarian := Librarian{request: request, response: response, library: library}
+        library.librarians <- librarian
+        librarian.serve()
+        return librarian.request, librarian.response
+}
+
+// Available types of requests
+const (
+        _ = iota
+        BorrowBook
+        ReturnBook
+        GetAvailability
+)
+
+// The type for the requests the librarians work with
+type CoolLibraryRequest struct {
+        isbn        string
+        requestType int
+}
+
+// Return the type of the request:
+// 1 - Borrow book
+// 2 - Return book
+// 3 - Get availability information about book
+func (request *CoolLibraryRequest) GetType() int {
+        return request.requestType
+}
+
+// Return the isbn of the book for which is the request
+func (request *CoolLibraryRequest) GetISBN() string {
+        return request.isbn
+}
+
+// The type for the requests the librarians work with
+type CoolLibraryResponse struct {
+        book *Book
+        err  error
+}
+
+// gets book content, an object implementing Stringer
+// If the book does not exist the first result is nil
+// returns an error if it has occured
+// when "Return book", the content is not attached
+func (response *CoolLibraryResponse) GetBook() (fmt.Stringer, error) {
+        if response.err != nil {
+                return nil, response.err
+        }
+        if response != nil && response.book != nil {
+                return response.book, nil
+        } else {
+                return nil, errors.New("Празен отговор")
+        }
+}
+
+// available - how many books are available after the request is performed
+// registered - how many copies are registered from this book (маx 4).
+func (response *CoolLibraryResponse) GetAvailability() (available int, registered int) {
+        if response != nil && response.book != nil {
+                return response.book.availableCount, response.book.registeredCount
+        } else {
+                return 0, 0
+        }
+}
+
+type Library interface {
+
+        // Добавя книга от json
+        // Oтговаря с общия брой копия в библиотеката (не само наличните).
+        // Aко са повече от 4 - връща грешка
+        AddBookJSON(data []byte) (int, error)
+
+        // Добавя книга от xml
+        // Oтговаря с общия брой копия в библиотеката (не само наличните).
+        // Ако са повече от 4 - връщаме грешка
+        AddBookXML(data []byte) (int, error)
+
+        // Ангажира свободен "библиотекар" да ни обработва заявките.
+        // Библиотекарите са фиксиран брой - подават се като параметър на NewLibrary
+        // Блокира ако всички библиотекари са заети.
+        // Връщат се два канала:
+        // първият е само за писане -  по него ще изпращаме заявките
+        // вторият е само за четене - по него ще получаваме отговорите.
+        // Ако затворим канала със заявките - освобождаваме библиотекаря.
+        Hello() (chan<- LibraryRequest, <-chan LibraryResponse)
+}
+
+type LibraryRequest interface {
+        // Тип на заявката:
+        // 1 - Borrow book
+        // 2 - Return book
+        // 3 - Get availability information about book
+        GetType() int
+
+        // Връща isbn на книгата, за която се отнася Request-a
+        GetISBN() string
+}
+
+type LibraryResponse interface {
+        // Ако книгата съществува/налична е - обект имплементиращ Stringer (повече информация по-долу)
+        // Aко книгата не съществува първият резултат е nil.
+        // Връща се и подобаващa грешка (виж по-долу) - ако такава е възникнала.
+        // Когато се е резултат на заявка от тип 2 (Return book) - не е нужно да я закачаме към отговора.
+        GetBook() (fmt.Stringer, error)
+
+        // available - Колко наличности от книгата имаме останали след изпълнението на заявката.
+        // Тоест, ако сме имали 3 копия от Х и това е отговор на Take заявка - тук ще има 2.
+        // registered - Колко копия от тази книга има регистрирани в библиотеката (макс 4).
+        GetAvailability() (available int, registered int)
+}
Знам, че е малко късно да го променяш. Но все пак искам да ти обърна внимание на едно нещо.
Ето това парче код
for {
        message, ok := <-librarian.request
        if ok {
                isbn := message.GetISBN()
                requestType := message.GetType()
                switch requestType {
                case BorrowBook:
                        libraryResponse := librarian.borrowBook(isbn)
                        librarian.response <- &libraryResponse
                case ReturnBook:
                        libraryResponse := librarian.returnBook(isbn)
                        librarian.response <- &libraryResponse
                case GetAvailability:
                        libraryResponse := librarian.getAvailability(isbn)
                        librarian.response <- &libraryResponse
                default:
                        librarian.response <- &CoolLibraryResponse{book: nil,
                                err: errors.New("Невалидна заявка")}
                }
        } else {
                break
        }
}
Би било много по - ясно написано като
for message := range librarian.request {
        isbn := message.GetISBN()
        requestType := message.GetType()
        switch requestType {
        case BorrowBook:
                libraryResponse := librarian.borrowBook(isbn)
                librarian.response <- &libraryResponse
        case ReturnBook:
                libraryResponse := librarian.returnBook(isbn)
                librarian.response <- &libraryResponse
        case GetAvailability:
                libraryResponse := librarian.getAvailability(isbn)
                librarian.response <- &libraryResponse
        default:
                librarian.response <- &CoolLibraryResponse{book: nil,
                        err: errors.New("Невалидна заявка")}
        }
}
Моето лично правило е, че ако забележа в кода си регион с 4-5 идентации, то значи трябва да го огледам много внимателно дали не правя нещо по - сложно от колкото може да е.
Ще ми трябва малко повече четене за да разбера къде имаш race condition или безкрайно зацикляне. Но изглежда, че има нещо такова.
