首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >面向对象的图书馆管理系统

面向对象的图书馆管理系统
EN

Code Review用户
提问于 2020-10-18 23:42:25
回答 2查看 393关注 0票数 4

图书馆管理系统是一个面向对象的程序,负责图书馆的基本事务。这是系列文章的第三部分。项目的第一次迭代是这里,第二次迭代也是这里

主要参与者是图书馆员、成员和系统。

主要关注事项

我结交了系统类的成员和图书馆员朋友,并与图书馆员和成员共同组成了系统课程。我觉得“系统是由图书馆员和会员组成的”听起来比“系统是图书馆员和会员的朋友”更好。这是否遵循常见的设计模式?

在我的上一篇文章中,由于系统会频繁执行插入,所以在这种情况下,我没有很好地了解为什么std::vector应该比std::list更可取。在考虑到速度和效率的同时,向量是否更具有可伸缩性和通用性?

关于潜在陷阱、陷阱和常见不良做法的任何其他观察都可以指出。

Date.hh

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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";
    }
}
```
代码语言:javascript
复制
EN

回答 2

Code Review用户

回答已采纳

发布于 2020-10-19 17:05:46

Date:

  • Date.hh缺少一些包含(<iostream><string>)。
  • 不要提供默认构造函数。有一个默认日期是没有意义的。
  • 不要提供一个和两个参数的构造函数。在1970年指定一个月和日期是不太可能的。
  • 我们应该支持1970年之前的几年。那时候有书!
  • year应该是一个有符号的数字(即使不太可能使用该功能)。
  • daymonth可以是较小的类型(例如std::uint8_t)。
  • setDate()是不必要的,因为我们有构造函数和赋值。
  • 人们会期望一个名为nextDay()的函数返回一个副本,而不是修改Date实例(c.f )。标准库迭代器nextadvance)。
  • 如果析构函数什么也不做,我们可以省略它。
  • validateDate永远不能返回false,因此它应该有一个void返回值(并且可能被称为throwIfInvalid或类似的东西)。
  • 不需要访问类实例的成员变量的成员函数(validateDate等)可以制成static
  • 我建议把日期打印为“yyyy”(或者按名字打印这个月)。把这一天放在中间是非常不合逻辑的。
  • 如果您有C++20,那么使用std::chrono::year_month_day代替!

BookItem:

  • 我们应该使用构造函数初始化程序列表来初始化成员变量。
  • 同样,我们不需要默认的构造函数。
  • 我们不需要指定析构函数。
  • 请注意,图书馆通常有几本相同的书。当一本书有ISBN (1970年以后的主流出版物)时,我们不需要复制书籍数据(书名、作者等)。图书馆里的每一本书。也许我们应该将图书数据移动到一个单独的类中,并在std::variant<ISBN, BookData>中使用BookItem?(但对于这个实现来说,这可能太过分了)。
  • 我们应该为库中包含的每个项目添加一个唯一标识符。

Librarian:

  • addBookremoveBook不应该是这个类的一部分。它们修改System类内部,并且应该是System类的一部分。auditLibrary也应该搬到那里去。
  • 默认构造函数不应该存在。析构函数不需要存在。
  • 对于当前的库功能,我认为这个类根本不需要存在。

Member:

  • 默认构造函数不好。没有必要。
  • 我们真的不想把BookItems的价值放在这里。我们只需要为他们结账的每一件物品存储一个ID。
  • checkOutreturnBook不应该在这里,他们应该是System的一部分。

System:

  • 不应该有friends。
  • 也许应该称为Library
  • 不要担心速度,除非它真的成为一个问题。甚至按标题存储书籍也是没有意义的(我们很可能希望按作者、类别或出版日期或.)进行搜索。
  • (请注意,标题搜索没有考虑到列表是按标题排序的!)
  • std::list几乎没有什么有效的用途。在从列表中间插入和删除许多(数十万)项时,它只变得比std::vector更快。std::vector在这里会很好。
  • 在适当的地方使用基于范围的循环:for (auto const& i: books) { ... }
  • 注意,<algorithm>头提供了各种函数来查找和复制东西。
票数 3
EN

Code Review用户

发布于 2020-10-19 18:13:25

我读过@user673679的答案,只想解决几个问题。

我强烈反对在类(如Member/Date/BookItem )中禁用默认构造函数。如果类没有默认构造函数,那么与std::vectorstd::map和其他模板容器一起使用它通常会变得非常尴尬。所以这是个糟糕的建议。

相反,您应该使默认构造的类(如这些类)明显未初始化,并添加测试它的函数/方法。

另一个注意事项:我想详述一下std::vectorstd::list之间的关系。std::list是一个非常慢的容器,不适合大多数用途。它需要对每个元素进行新的分配,并且要找到一个中间元素,需要遍历列表的一半。因此,使用std::list几乎是一种亵渎。在一些罕见的情况下,列表可能是有益的,但在这段代码中绝对不是。

使用std::vector代替。您最终需要弄清楚如何正确地管理内存和搜索查找,但需要使用std::vector来存储基类。例如,删除单个元素不值得重新排列整个向量。相反,只需计算空位置的数量,如果数量超过总大小的一半,那么重新排列它。

您仍然缺乏用于BookItem的移动构造函数和移动分配。拥有它将提高保存图书项的类(如std::vector )的性能。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/250850

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档