Library management system is a object oriented program that takes care of the basic housekeeping of a library. This is the third part of a series. The first iteration of the project is found here and the second iteration is also found here
The major actors are the Librarian, Member, and the System.
Major Concern
I made member and Librarian friends of the system class and also composed the system class with Librarian and Member. I felt "System is composed of a Librarian and members" sounds better than "System is a friend of a Librarian and members". Does this follow the common design patterns?
In my previous post, I wasn't cleared well enough on why a std::vector should be preferable than std::list in this situation due to the fact that the system would perform insertion frequently. Would std::vector be more scalable and versatile whilst taking speed and efficiency into consideration?
Any other observation on potential pitfalls, traps and common bad practice can be pointed out.
Date.hh
#ifndef DATE_HH
#define DATE_HH
class Date {
friend std::ostream &operator<<( std::ostream &, const Date & );
private:
/* data-members */
unsigned month = 1;
unsigned day = 1;
unsigned year = 1970;
/* utility functions */
bool validateDate( unsigned m, unsigned d = 1, unsigned y = 1970 );
bool checkDay( unsigned m, unsigned d, unsigned y ) const;
bool isLeapYear( unsigned y ) const { return ( y % 400 == 0 ) || ( y % 4 == 0 && y % 100 != 0 ); }
public:
static constexpr unsigned int monthsPerYear = 12;
/* ctors */
Date() = default;
Date( unsigned m, unsigned d, unsigned y );
Date( unsigned m );
Date( unsigned m, unsigned d );
/* copy operations */
Date( const Date &d ) = default;
Date &operator=( const Date &d ) = default;
/* equality test operations */
bool operator==( const Date &d ) const;
bool operator!=( const Date &d ) const { return !( *this == d ); }
/* method-functions */
void setDate( unsigned m = 1, unsigned d = 1, unsigned y = 1970 );
unsigned getMonth() const;
unsigned getDay() const;
unsigned getYear() const;
void nextDay();
const std::string toString() const;
// dtor
~Date(){};
};
#endif
Date.cc
#include <iostream>
#include <string>
#include <stdexcept>
#include <array>
#include "../headers/Date.hh"
Date::Date( unsigned m, unsigned d, unsigned y ) {
if ( validateDate(m, d, y ) ) {
month = m; day = d; year = y;
}
}
Date::Date( unsigned m ) {
if( validateDate( m ) )
month = m;
}
Date::Date( unsigned m, unsigned d ) {
if ( validateDate( m, d ) ) {
month = m; day = d;
}
}
void Date::setDate( unsigned m, unsigned d, unsigned y ) {
if ( validateDate( m, d, y ) ) {
month = m; day = d; year = y;
}
}
void Date::nextDay() {
day += 1;
try {
checkDay( month, day, year );
} catch ( std::invalid_argument &e ) {
month += 1;
day = 1;
}
if ( month % 12 == 0 ) {
year += 1;
month = 1;
}
}
bool Date::operator==( const Date &d ) const {
if( month != d.month ) return false;
if ( day != d.day ) return false;
if ( year != d.year ) return false;
return true;
}
std::ostream &operator<<( std::ostream &os, const Date &d ) {
os << d.month << "/" << d.day << "/" << d.year;
return os;
}
// utility function
bool Date::validateDate( unsigned m, unsigned d, unsigned y ) {
// validate month
if ( m < 1 || m >= 13 )
throw std::invalid_argument( "Month must be between 1-12" );
// validate day
if ( checkDay( m, d, y ) == false )
throw std::invalid_argument( "Invalid day for current month and year" );
// validate year
if ( y < 1970 )
throw std::invalid_argument( "year must be greater than 1969" );
return true;
}
const std::string Date::toString() const {
return std::to_string(month) + "/" + std::to_string(day) + "/" + std::to_string(year);
}
bool Date::checkDay( unsigned testMonth, unsigned testDay, unsigned testYear ) const {
static const std::array < unsigned, monthsPerYear + 1 > daysPerMonth = { 0,31,28,31,30,31,30,31,31,30,32,30,31};
if ( testDay > 0 && testDay <= daysPerMonth[ testMonth ] )
return true;
if ( testMonth == 2 && testDay == 29 && isLeapYear( testYear ) )
return true;
return false;
}
BookItem.hh
#ifndef BOOKITEM_HH
#define BOOKITEM_HH
#include <iostream>
#include <string>
#include <string_view>
#include "Date.hh"
enum class BookStatus : unsigned { RESERVED, AVAILABLE, UNAVAILABLE, REFERENCE, LOANED, NONE };
enum class BookType : unsigned { HARDCOVER, MAGAZINE, NEWSLETTER, AUDIO, JOURNAL, SOFTCOPY };
class BookItem {
friend std::ostream &operator<<( std::ostream &, const BookItem & );
private:
/* data-members */
std::string title;
std::string author;
std::string category;
Date pubDate;
std::string isbn;
BookStatus status;
BookType type;
/* user connected to this book */
std::string bookcurrentUser;
public:
/* ctors */
BookItem() = default;
BookItem( const std::string &title, const std::string &author, const std::string &cat, const Date &pubDate, \
const std::string &isbn, const BookType type, const BookStatus status = BookStatus::AVAILABLE );
bool operator==( const BookItem &bookItem ) const;
bool operator!=( const BookItem &bookItem ) const { return !( *this == bookItem); };
/* method-functions */
void setStatus( BookStatus s ) { status = s; };
void setType( BookType t ) { type = t;};
void setCategory( const std::string &c ) { category = c; }
void setBookCurrentUser( std::string userName ) { bookcurrentUser = userName; }
std::string_view getBookCurrentUser() const { return bookcurrentUser; }
std::string_view getStatus() const;
std::string_view getType() const;
std::string_view getTitle() const { return title; }
std::string_view getAuthor() const { return author; }
std::string_view getCategory() const { return category; };
std::string_view getIsbn() const { return isbn; }
Date &getPubDate() { return pubDate; }
void printPubDate() const { std::cout << pubDate; }
const BookStatus getStatusByEnum() const { return status; }
const BookType getTypeByEnum() const { return type; }
// dtor
~BookItem() = default;
};
#endif
BookItem.cc
#include <iostream>
#include "../headers/BookItem.hh"
BookItem::BookItem( const std::string &t, const std::string &a, const std::string &c, const Date &d, \
const std::string &i, const BookType ty, const BookStatus s ) {
title = t, author = a, category = c, pubDate = d, isbn = i;
setStatus( s );
setType( ty );
}
bool BookItem::operator==( const BookItem &bookItem ) const {
if ( title != bookItem.title ) return false;
if ( author != bookItem.author ) return false;
if ( category != bookItem.category ) return false;
if ( pubDate != bookItem.pubDate ) return false;
if ( isbn != bookItem.isbn ) return false;
if ( status != bookItem.status ) return false;
if ( type != bookItem.type ) return false;
return true;
}
std::string_view BookItem::getStatus() const {
switch( status ) {
case BookStatus::AVAILABLE:
return "AVAILABLE";
case BookStatus::REFERENCE:
return "REFERENCE";
case BookStatus::UNAVAILABLE:
return "UNAVAILABLE";
case BookStatus::LOANED:
return "LOANED";
case BookStatus::RESERVED:
return "RESERVED";
default:
return "NONE";
}
}
std::string_view BookItem::getType() const {
switch( type ) {
case BookType::AUDIO:
return "AUDIO";
case BookType::HARDCOVER:
return "HARDCOVER";
case BookType::JOURNAL:
return "JOURNAL";
case BookType::MAGAZINE:
return "MAGAZINE";
case BookType::NEWSLETTER:
return "NEWSLETTER";
case BookType::SOFTCOPY:
return "SOFTCOPY";
default:
return "NONE";
}
}
std::ostream &operator<<( std::ostream &os, const BookItem &b ) {
os << "\nName of book: " << b.getTitle();
os << "\nAuthor of book: " << b.getAuthor();
os << "\nBook category: " << b.getCategory();
os << "\nPublication date: " << b.pubDate;
os << "\nISBN number: " << b.getIsbn();
os << "\nStatus of book: " << b.getStatus();
os << "\nType of book: " << b.getType();
return os;
}
Librarian.hh
#ifndef LIBRARIAN_HH
#define LIBRARIAN_HH
#include <iostream>
#include <string>
#include "BookItem.hh"
class System;
class Librarian {
public:
/* data-members */
std::string name;
Date dateOfHire;
/* ctors */
Librarian() = default;
Librarian( const std::string &name, const Date &dateOfHire );
// basic method-function
void printDateOfHire() const { std::cout << dateOfHire; }
/* core functionalities */
void addBook( System &sys, const BookItem &isbn );
void removeBook( System &sys, const std::string &isbn );
void auditLibrary( const System &sys ) const;
// dtor
~Librarian(){}
};
#endif
Librarian.cc
#include <iostream>
#include "../headers/System.hh"
#include "../headers/Librarian.hh"
Librarian::Librarian( const std::string &n, const Date &d ) {
name = n;
dateOfHire = d;
}
void Librarian::addBook(System &sys, const BookItem &book ) {
if ( sys.books.empty() ) {
sys.books.push_front( book );
return;
}
for ( auto bptr = sys.books.cbegin(); bptr != sys.books.cend(); ++bptr ) {
if( book.getTitle() <= bptr->getTitle() ) {
sys.books.insert(bptr, book);
return;
}
}
sys.books.push_back( book );
}
void Librarian::removeBook( System &sys, const std::string &isbn ) {
BookItem book = sys.getBook( isbn );
for ( auto bptr = sys.books.cbegin(); bptr != sys.books.cend(); ++bptr ) {
if ( book.getIsbn() == bptr->getIsbn() ) {
sys.books.remove(book);
std::cout << "Deleted { " << book.getAuthor() << " : " << book.getTitle() << " } \n";
return;
}
}
throw std::invalid_argument("Book not found");
}
void Librarian::auditLibrary( const System &sys ) const {
std::cout << "\nName of Library: " << sys.libraryName << ", Date created " << sys.dateCreated;
std::cout << "\nLibrarian: " << name << ", Date of hire: " << dateOfHire;
std::cout << "\n\nBooks: ";
for ( auto bptr = sys.books.cbegin(); bptr != sys.books.cend(); ++bptr ) {
std::cout << *bptr << "\n";
std::cout << "This book is linked to: "
<< ( ( bptr->getBookCurrentUser() == "" ) ? "None" : bptr->getBookCurrentUser() ) << "\n";
}
std::cout << "\n\nMembers: ";
for ( auto mPtr = sys.members.cbegin(); mPtr != sys.members.cend(); ++mPtr ) {
std::cout << *mPtr << "\n";
}
}
Member.hh
#ifndef MEMBER_HH
#define MEMBER_HH
#include <string>
#include <vector>
#include "Date.hh"
#include "BookItem.hh"
class System;
class Member {
friend std::ostream& operator<<( std::ostream&os, const Member &m );
private:
/* data-member */
std::string libraryNumber;
Date dateRegisted;
std::vector<BookItem> checkedOutBooks;
public:
/* data-member */
std::string name;
char sex;
/* ctors */
Member() = default;
Member( const std::string &n, const char s, Date d ) : dateRegisted( d ), name( n ), sex( s ) {}
/* method-functions */
std::string getLibraryNumber() const { return libraryNumber; }
void setLibraryNumber( const std::string &lNum ) { libraryNumber = lNum; };
void checkOut( System &sys, const std::string &isbn );
void returnBook( System &sys, const std::string &isbn );
bool operator==( const Member &m );
bool operator!=( const Member &m ) { return !( *this == m ); }
// dtor
~Member() = default;
};
#endif
System.cc
#ifndef SYSTEM_HH
#define SYSTEM_HH
#include <string>
#include <list>
#include <vector>
#include "Date.hh"
#include "BookItem.hh"
#include "Librarian.hh"
#include "Member.hh"
class System {
friend class Librarian;
friend class Member;
private:
/* data-members */
std::list<BookItem> books{};
std::vector<Member> members{};
Librarian librarian;
Member member;
public:
/* ctors */
System() = default;
System( const std::string &name, Date &date ) : libraryName( name ), dateCreated( date ) {};
/* method-functions */
const std::string generateLibraryNumber() const;
void addMember( Member &m ) { members.push_back( m ); };
void deleteMember( Member &m );
void displayMembers();
BookItem getBook( const std::string &isbn ) const;
void viewBook( const std::string isbn ) const;
void placeOnReserve( const std::string, const std::string &isbn );
void displayAllBooks() const;
/* data-members */
std::string libraryName;
Date dateCreated;
/* search functionalities */
std::list<BookItem> queryByTitle( const std::string &t ) const;
std::list<BookItem> queryByAuthor( const std::string &a ) const;
std::list<BookItem> queryByPubDate( const Date &d );
std::list<BookItem> queryByStatus( const BookStatus &s ) const;
std::list<BookItem> queryByType( const BookType &ty ) const;
// dtor
~System() = default;
};
#endif
System.cc
#include <iostream>
#include <set>
#include "../headers/System.hh"
std::list<BookItem> System::queryByTitle( const std::string &t ) const {
std::list<BookItem> queryList;
for ( auto bPtr = books.cbegin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getTitle().find(t) != std::string::npos )
queryList.push_back( *bPtr );
}
return queryList;
}
std::list<BookItem> System::queryByAuthor( const std::string &a ) const {
std::list<BookItem> queryList;
for ( auto bPtr = books.cbegin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getAuthor().find(a) != std::string::npos )
queryList.push_back( *bPtr );
}
return queryList;
}
std::list<BookItem> System::queryByPubDate( const Date &d ) {
std::list<BookItem> queryList;
for ( auto bPtr = books.begin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getPubDate().toString().find(d.toString()) != std::string::npos )
queryList.push_back( *bPtr );
}
return queryList;
}
std::list<BookItem> System::queryByStatus( const BookStatus &s ) const {
std::list<BookItem> queryList;
for ( auto bPtr = books.begin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getStatusByEnum() == s )
queryList.push_back( *bPtr );
}
return queryList;
}
std::list<BookItem> System::queryByType( const BookType &ty ) const {
std::list<BookItem> queryList;
for ( auto bPtr = books.begin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getTypeByEnum() == ty )
queryList.push_back( *bPtr );
}
return queryList;
}
void System::placeOnReserve( const std::string name, const std::string &isbn ) {
for ( auto bPtr = books.begin(); bPtr != books.end(); ++bPtr ) {
if ( bPtr->getIsbn() == isbn ) {
bPtr->setStatus( BookStatus::RESERVED );
bPtr->setBookCurrentUser( name );
}
}
}
BookItem System::getBook( const std::string &isbn ) const {
for ( auto bPtr = books.cbegin(); bPtr != books.cend(); ++bPtr ) {
if ( bPtr->getIsbn() == isbn )
return *bPtr;
}
throw std::invalid_argument("Book is not available at the library");
}
void System::viewBook( const std::string isbn ) const {
std::cout << getBook( isbn );
}
const std::string System::generateLibraryNumber() const {
static std::string Codes[10]{"XGS", "QWT", "OPI", "NMK", "DXF", "PXG", "OPI", "QPU", "IKL", "XYX" };
static std::set< unsigned, std::greater<unsigned> > idSet;
unsigned id;
bool unique = false;
unsigned index = 0 + rand() % 9;
std::string code = Codes[ index ];
while ( unique == false ) {
id = 10000000 + rand() % 9999999;
auto ret = idSet.emplace(id);
if ( !ret.second ) {
std::cout << "unique failed";
unique = false;
continue;
}
else
unique = true;
}
return code + std::to_string( id );
}
void System::deleteMember( Member &m ) {
for ( auto mPtr = members.begin(); mPtr != members.end(); ++mPtr ) {
if ( *mPtr == m ) {
members.erase( mPtr );
std::cout << "Deleted member: { Name: " << m.name << ", Library Number: " << m.getLibraryNumber() <<
" }\n";
return;
}
}
throw std::invalid_argument("No such member found");
}
void System::displayMembers() {
std::cout << "Members of Library: ( count : " << members.size() << " ) " << "\n";
for ( auto mPtr = members.cbegin(); mPtr != members.cend(); ++mPtr ) {
std::cout << *mPtr;
}
}
void System::displayAllBooks() const {
for ( auto bPtr = books.begin(); bPtr != books.end(); ++bPtr ) {
std::cout << *bPtr << "\n\n";
}
}
```