C++: Late fines in the library

Extend the library design with a new rule, and see how little of the code has to change.

What to build

A library that charges a fine for each day an item is kept past its loan period.

Requirements

  • Support add KIND T for book, dvd and magazine, with loans of 14, 7 and 3 days and fines of 5, 10 and 2 per late day.
  • lend T prints lent T for N days. return T D, where D is the number of days the item was kept, prints returned T, fine F.
  • Report an unknown title, lending an item that is out, and returning one that is in with exceptions, printing sorry: with the message.
  • After the last command, print fines total T. The library owns and deletes its items, and cannot be copied.

Input

The first line has q. Each of the next q lines is a command. Titles are single words.

Output

One line per lend, return or failed command, then fines total T.

Constraints

1 ≤ q ≤ 100, and 0 ≤ D ≤ 1000.

Example

Input:

6
add book Dune
add dvd Cosmos
lend Dune
lend Cosmos
return Dune 20
return Cosmos 7

Output:

lent Dune for 14 days
lent Cosmos for 7 days
returned Dune, fine 30
returned Cosmos, fine 0
fines total 30

Why it works

The new rule is one more virtual function. The library's code asks each item for its own fine, so it never needs to know which kind of item it is handling.

Examples

Example 1 · Input
6
add book Dune
add dvd Cosmos
lend Dune
lend Cosmos
return Dune 20
return Cosmos 7
Expected output
lent Dune for 14 days
lent Cosmos for 7 days
returned Dune, fine 30
returned Cosmos, fine 0
fines total 30
Example 2 · Input
3
add magazine Time
lend Time
return Time 10
Expected output
lent Time for 3 days
returned Time, fine 14
fines total 14

Hints

Hint 1

Add a pure virtual `int finePerDay() const` beside `loanDays()`: 5 for a book, 10 for a DVD, 2 for a magazine.

Hint 2

On return, the days late are the days kept minus the loan period, and nothing if that is not positive.

Hint 3

Keep the running total of fines in the `Library`, and print it after the last command.

Solution

Show one correct solution (try it yourself first)
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
using namespace std;

class Item {
    string title;
    bool out;
public:
    Item(string t) : title(t), out(false) {}
    virtual ~Item() {}
    string getTitle() const { return title; }
    void borrow() {
        if (out) throw runtime_error(title + " is already borrowed");
        out = true;
    }
    int giveBack(int days) {
        if (!out) throw runtime_error(title + " is not borrowed");
        out = false;
        int late = days - loanDays();
        return late > 0 ? late * finePerDay() : 0;
    }
    virtual int loanDays() const = 0;
    virtual int finePerDay() const = 0;
};

class Book : public Item {
public:
    Book(string t) : Item(t) {}
    int loanDays() const override { return 14; }
    int finePerDay() const override { return 5; }
};

class DVD : public Item {
public:
    DVD(string t) : Item(t) {}
    int loanDays() const override { return 7; }
    int finePerDay() const override { return 10; }
};

class Magazine : public Item {
public:
    Magazine(string t) : Item(t) {}
    int loanDays() const override { return 3; }
    int finePerDay() const override { return 2; }
};

class Library {
    vector<Item*> items;
    int fines;
public:
    Library() : fines(0) {}
    Library(const Library&) = delete;
    Library& operator=(const Library&) = delete;
    ~Library() {
        for (Item* it : items) delete it;
    }
    void add(Item* it) { items.push_back(it); }
    Item& find(const string& title) {
        for (Item* it : items)
            if (it->getTitle() == title) return *it;
        throw invalid_argument("no item called " + title);
    }
    void lend(const string& title) {
        Item& it = find(title);
        it.borrow();
        cout << "lent " << title << " for " << it.loanDays() << " days" << endl;
    }
    void takeBack(const string& title, int days) {
        int fine = find(title).giveBack(days);
        fines += fine;
        cout << "returned " << title << ", fine " << fine << endl;
    }
    int totalFines() const { return fines; }
};

int main() {
    int q;
    cin >> q;
    Library lib;
    while (q-- > 0) {
        string cmd, title;
        cin >> cmd;
        try {
            if (cmd == "add") {
                string kind;
                cin >> kind >> title;
                if (kind == "book") lib.add(new Book(title));
                else if (kind == "dvd") lib.add(new DVD(title));
                else lib.add(new Magazine(title));
            } else if (cmd == "lend") {
                cin >> title;
                lib.lend(title);
            } else {
                int days;
                cin >> title >> days;
                lib.takeBack(title, days);
            }
        } catch (const exception& e) {
            cout << "sorry: " << e.what() << endl;
        }
    }
    cout << "fines total " << lib.totalFines() << endl;
    return 0;
}

Lesson: Project: a small library system →