Description

Purpose

This project is designed to teach you how to manipulate exceptions and properties. It reinforces your understanding of class design and implementation while introducing the handling of exceptions.

Challenge

In short

Develop a class that helps the user track the progress in their reading of books. Your class should allow users to track the book title, total page number, and the current page they stopped their reading at (as a bookmark!).

In more details

  1. Your goal is to design and implement a Bookmarker class containing three attributes and several methods (some of which may and should be implemented using properties).

  2. You must develop a Program class with a Main method to test your Bookmarker class, and this Program class will be part of your grade.

  3. An object in your Bookmarker must hold three attributes:

    • a title,
    • the total number of pages in the book,
    • the current page the user is reading.

    The title must be implemented using automatic properties.

  4. The current page must be less than the total number of pages: if at any point during creation or later modification the current page becomes strictly larger than the total number of pages, your class should throw an exception.

  5. The total number of pages must be strictly positive, but the current page can be positive (0 would be an acceptable value: it means the reader has not started yet).

  6. Finally, your class should have three methods:

    • a constructor that takes three arguments (one for the title, one for the total number of pages, and one for the current page),
    • a ToString method that returns a string containing the title, how much of the book was read, and how much of the book is left (the last two in percentages),
    • a method to increment the number of pages read by its argument.

Example

Remember that you have to develop a Program.cs Main method that will test your class. It will probably contain something along the lines of

Bookmarker book1 = new Bookmarker(title, tPages, cPages);
Console.WriteLine(book1);

to create the Bookmarker object with data given by the user, and to display the object created.

Executing your program with a properly implemented Bookmarker class should give something along the lines of:

Enter the title of the book.
T͟e͟s͟t͟1͟⏎͟
Enter the total number of pages.
1͟0͟0͟⏎͟
Enter the page you stopped your reading at.
3̲7̲⏎͟
You have read 37,00% of "Test1".
You have 63,00% to go!
How many pages did you read since?
1͟2͟⏎͟
You have read 49,00% of "Test1".
You have 51,00% to go!
 
Press any key to continue...

If the user enters incorrect data, then the error should be thrown when the object is created (do not perform user-input validation in the Main method: incorrect data should be handled by the class, not by the Main method).

For example, if the user enters a negative number of pages, you should get:

Enter the title of the book.
T͟e͟s͟t͟2͟⏎͟
Enter the total number of pages.
-̲2̲4̲⏎͟
Enter the page you stopped your reading at.
1͟2͟⏎͟
The total number of pages cannot be negative.

If the user enters a current page that is greater than the number of pages, you should get:

Enter the title of the book.
T͟e͟s͟t͟3͟⏎͟ ͟
Enter the total number of pages.
2͟5͟⏎͟
Enter the page you stopped your reading at.
3͟0͟⏎͟
You cannot have read more than the total number of pages!

If the user tries to go “above” the total number of pages, you should get:

Enter the title of the book.
T͟e͟s͟t͟4͟⏎͟
Enter the total number of pages.
2͟5͟⏎͟
Enter the page you stopped your reading at.
1͟7͟⏎͟
You have read 68,00% of "Test4".
You have 32,00% to go!
How many pages did you read?
9͟⏎͟
You cannot have read more than the total number of pages!

Last but not least (even if this is optional), if the user enters strings that are not numbers, your class (and not the Main method) should throw an exception:

Enter the title of the book.
T͟e͟s͟t͟5͟⏎͟
Enter the total number of pages.
T͟e͟s͟t͟6͟⏎͟
Enter the page you stopped your reading at.
T͟e͟s͟t͟7͟⏎͟
Input string was not in a correct format.

Note that it is ok if you cannot reproduce this output exactly.

Bonuses

  • Have your class handle strings, so that, for example, your Bookmarker constructor would take three strings as input,
  • Have your Main method ask the user how many books they want to track and create that number of objects,
  • Implement a static ISBN-13 checker: your method should take as input a string representing an ISBN-13, and return true if it is valid. Consult wikipedia on how a ISBN is determined to be valid, and try your best!

Submission

Please, follow our guideline on project submission. In particular, make sure you write your name and the date in a delimited comment at the beginning of your file.

Solution

A possible solution is shared in this archive:

using System;
 
class Bookmarker
{
  public string Title { get; set; }
  private int tPages; // Total number of pages.
  public int TPages // The property will check that the total number of page is "valid"
  {
    set
    {
      if (value <= 0) // This will throw an error if the value passed is negative.
        throw new ArgumentException(
          "The total number of pages cannot be negative."
        );
      else if (value < cPages) // This will throw an error if the value passed is less than the current page.
        throw new ArgumentException(
          "The total number of pages cannot be less than the current page."
        );
      else
        tPages = value; // If no errors were thrown, we set the value to the value passed.
    }
    get { return tPages; }
  }
  private int cPages; // Current page
  public int CPages // The property will check that the current number of page is "valid"
  {
    set
    {
      if (value < 0)
        throw new ArgumentException(
          "You cannot have read a negative number of pages!"
        );
      else if (value > tPages)
      {
        throw new ArgumentException(
          "You cannot have read more than the total number of pages!"
        );
      }
      else
        cPages = value;
    }
    get { return cPages; }
  }
 
  public Bookmarker(string titleP, int tPagesP, int cPagesP)
  {
    Title = titleP;
    TPages = tPagesP;
    CPages = cPagesP;
  }
 
  public override string ToString()
  {
    return $"You have read {((double)cPages / tPages):P} of \"{Title}\".\nYou have {(1 - ((double)cPages / tPages)):P} to go!";
  }
 
  public void Read(int pReadP)
  {
    if (pReadP + cPages > tPages)
      throw new ArgumentException(
        "You cannot have read more than the total number of pages!"
      );
    else
      cPages += pReadP;
  }
}
using System;
 
class Program
{
  static void Main()
  {
    string title,
      tPages,
      cPages;
    /*
     * To trigger failure to create the object,
     * test with the following values:
     */
    // "Random", "Test", "0"    to get "Input string was not in a correct format."
    // "Random", "-12",  "0"    to get "The total number of pages cannot be negative."
    // "Random", "12",   "Test" to get "Input string was not in a correct format."
    // "Random", "12",   "15"   to get "You cannot have read more than the total number of pages!
    // "Random", "12",   "-12"  to get "You cannot have read a negative number of pages!"
    /*
     * To trigger error when calling the "Read" method,
     * test with the following values, after having created an object
     * using "Test", "10", "5"
     */
    // "6"    to get "You cannot have read more than the total number of pages!"
    // "-3"   to get "Input string was not in a correct format."
    // "Test" to get "Input string was not in a correct format."
    /*
     * An additional test would be to add, for example
     *             book1.TPages = "3";
     * after
     *             Console.WriteLine(book1);
     * to make sure that setting the number of page to an "invalid"
     * value would trigger the error
     * "The total number of pages cannot be less than the current page."
     */
 
    try
    {
      Console.WriteLine("Enter the title of the book.");
      title = Console.ReadLine();
      Console.WriteLine("Enter the total number of pages.");
      tPages = Console.ReadLine();
      Console.WriteLine(
        "Enter the page you stopped your reading at."
      );
      cPages = Console.ReadLine();
      Bookmarker book1 = new Bookmarker(
        title,
        int.Parse(tPages),
        int.Parse(cPages)
      );
      Console.WriteLine(book1);
      Console.WriteLine("How many pages did you read?");
      book1.Read(int.Parse(Console.ReadLine()));
      Console.WriteLine(book1);
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex.Message);
    }
  }
}

(Download this code)

A solution completing the bonus

“Have your class handle strings, so that, for example, your Bookmarker constructor would take three strings as input”

is shared in this archive.