Ewan10 / Task-Manager

0 stars 0 forks source link

Enable tasks to be marked as "Done" #10

Closed thompsy closed 2 years ago

thompsy commented 2 years ago

The current implementation allows a user to add and view tasks. It would also be useful for users to be able to mark tasks as "Done." The command line should look something like this:

task.sh --task XXX --done

This would mark a specific task XXX as complete. Note that when the users views the tasks completed tasks should still appear but should be clearly marked as "Done."

You'll need to think a bit about how you're going to implement this. Specifically, how will you uniquely identify which task the user wants to complete?

I suggest you have a think and propose some possible solutions in this thread before you start implementing one of them.

Ewan10 commented 2 years ago

According to the previous implementation I declared a string field inside the Task class that a method could set it to "Done" when the user invoked that method.

The user chose the task by entering the task title. Then the method would search the list of tasks whether that task existed and if it did its string named "status" would be set to "Done". So the choice of the task will exploit a field from the task class.

Another way to identify uniquely a stored task is to create a static integer inside the TaskManager class and an int field inside the Task class. Whenever the user creates a new task the static integer could be incremented and its updated value stored in the int field of the new task. In this way each task will have a unique integer id which can be printed along with the title, the date and the time of the task so that the user can see which id corresponds to which task.

What approach do you suggest to follow?

thompsy commented 2 years ago

Ok, there are two concerns here:

  1. How do we store the task status data? Previously you were using a String which could be set to "Done". Are there any drawbacks to this approach? Are there any other possibly approaches you could take here? Other data types that could be used? If so, what would the pros and cons of each approach be? Think about possible future extensibility too. What if we wanted to enable the user to mark tasks as "Waiting" or "Blocked" or something else...

  2. How do we uniquely identify tasks? You've identified two reasonable possibilities here: using the title of the task or using a unique id. The next step is to think a bit about the pros and cons of each approach. Are there any cases where using the title wouldn't work? Are there any reasons why using a unique id wouldn't work? Think about the kind of data structures that each approach could use, are there any performance characteristics that might impact our choice?

A lot of software engineering is about understanding the different options and choosing the best one for the given context. There is very seldom a "correct" approach to solving something but often there are better and worse ones each with different trade-offs. It would be good for you to start developing this kind of thought process.

So, I suggest you have a think through the questions above and take a stab at writing out the pros and cons of each approach. This doesn't need to be perfect and it's likely there'll be things that you aren't aware of yet, but it's a good discipline to get into and develop!

Ewan10 commented 2 years ago

If we print the size of a string on a C++ compiler it is 32 bytes, the size of an integer is 4 and the size of a char is 1 byte. In Java "a simple "ASCII" string can have different number of bytes in its representation, depending which encoding is used. That means each character in a string may not be represented always by one byte.".StaclkOverflow. Creating one field more per Task object raises the resources of memory consumed by the program.

Regarding the efficiency of data structures in "Thinking in Java", Bruce Eckel, on p.863-870 a program is executed which tests various linear data structures for various operations such as insertions and deletions of elements and for various sizes of elements they hold. The output of the time it takes for the operations to be completed also is printed and the performance of each data structure is analyzed.

For a list backed by an array the access of the list is very fast regardles of the size of the list for 10-10000 elements. For inserting elements in the middle of a list with an iterator that becomes very expensive for an ArrayList for 10000 elements while for a LinkedList the access is equally fast as for 100 elements and about 10 times faster than the ArrayList. "An ArrayList must create space and copy all its references forward during an insertion and this becomes expensive as the list gets bigger whereas a LinkedList only needs to link a new element and doesn't have to modify the rest of the list so the cost of the access remains constant and invariable with the list size.", p.869, "Thinking in Java", B. Eckel.

Conclusion: a LinkedList should be preferred for operations in the middle of the list otherwise an ArrayList can be used as a default list. A list backed by an array (produced by Arrays.asList()) or an actual array can be used for fixed-size set of elements.

As for the issue the user may want to add different types of status to the tasks one method would be to provide an array of strings which will hold the type of status for a task, a method that will initialize a string of how the user wants to mark a task and another method and a HashMap that will create a key-value pair of a task title and a status.

How do you suggest me to go with the implementation of the complete method? Shall I go for the last implementation with a string field for the status of a task?

thompsy commented 2 years ago

There's another option here that would be even better, an Enum. This is ideal for these kind of situations where there is a list of possible states that are non-overlapping and known in advance. So, I suggest you create a Status enum that captures a number of states. Probably, Open, Done and Blocked would be fine for now. Blocked represents a task that I can't do anything about yet e.g. I'm waiting for someone to give me more information or something. If we need to, we can expand on this later. Note that you'll want to add a new command line option to allow for tasks to be moved to a different state.

I suggest that you don't use the task title to uniquely identify them. There are several issues with this approach:

  1. String comparison is relatively slow!
  2. This rules out the possibility of having tasks with the same title. It also means you will need to prevent this happening, so you'll need to check every new title that the user enters to make sure it doesn't already exist. This will be expensive from a performance perspective and is a lot of work that you don't need to do.

A better approach would be to use a unique Id number to refer to each task. There are several benefits to this:

  1. The TaskManager can assign the Ids automatically so the user doesn't have to think about them.
  2. This would enable you to use a Map to store the Tasks which allows for much more efficient lookup and avoids you needing to do any sorting of your current List<Task>.
  3. It will also open the way in due course for us to use a database to store the tasks.

So, what you need to do is:

  1. Add an id field to the Task
  2. Have the TaskManager keep track of the last id it assigned or read in from an existing file.
  3. Have TaskManager store the tasks in a Map keyed by the new id field.
  4. Add an Enum with the statuses and add that as a field to Task
  5. Add a new command line option that takes a task id and moves it to a different state.

Hopefully that all makes sense, but if not do ask!

Ewan10 commented 2 years ago

I have finished the implementation, I tested it with input and it is functional. I am checking for any points requiring potentially refactoring or correction. When I open the PR I will ask your opinion for some candidate points requiring refactoring in case they do not conform with the software design principles.