Conteúdo do Curso
Java Data Structures
Java Data Structures
Challenge: Implementing a TaskManager Service
Practice
It's time to practice. You have a task to write a TaskManager
service that will keep track of your tasks.
With this service, the user can add tasks, assign them to themselves, and then complete them in a sequential order.
Let's start working on this task together, and then you'll continue on your own.
But first, you need to create the task model itself. Let's create a Task
class:
Main
public class Task { private int id; private String name; private String description; }
The Task
class contains three attributes: id
, name
, and description
. Each task must have a name and description, and id
is a necessary attribute for any data structure.
How else would you access the elements?
The attributes are also protected by the private
access modifier because you don’t want other classes to have direct access to the fields of this class, except through the constructor or getters.
Now, let's implement these getters and the constructor. This can be easily done using the combination of Control + Return
on a MacBook or Alt + Ins
on a Windows .
After the performed operations, you have the following Task
class:
Task
package codefinity.taskManager; public class Task { private int id; private String name; private String description; public Task(int id, String name, String description) { this.id = id; this.name = name; this.description = description; } public int getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } @Override public String toString() { return "Task{" + "id=" + id + ", name='" + name + '\'' + ", description='" + description + '\'' + '}'; } }
Services
You will have a very simplified task board service, so let's implement a service that will add tasks, mark them as completed, and check if there are tasks in the queue.
Let's call this class TaskQueueService
.
To begin with, we need to define an interface with these tasks:
TaskQueueService
package codefinity.taskManager; public interface TaskQueueService { void addTask(Task task); Task getNextTask(); boolean isEmpty(); }
This interface defines 3 methods that implementation classes must implement. These methods include adding a task, moving to the next task (implying that the user has already completed the previous task), and a method to check if there are tasks in the queue.
Great, let's now create an implementation class that will override and implement all these methods:
TaskQueueServiceImpl
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.Task; import java.util.LinkedList; import java.util.Queue; public class TaskQueueServiceImpl { private Queue<Task> taskQueue = new LinkedList<>(); }
We create a private attribute that will be used only within this service class.
It's worth making a small digression to talk about service classes, which play a key role in OOP.
What Are Service Classes?
Service classes are designed to perform specific operations. They inherit from service interfaces and implement their methods. This approach enhances code maintainability and scalability while following SOLID principles, which you will explore in a separate course.
For now, it's crucial to understand that service classes exist solely to perform operations on other objects.
For example, If you write a calculator, you will have a separate class that will store the value of the number you operate on. The actual operations will be performed using a service class where addition, subtraction, and so on are defined.
I hope this clarifies the purpose of service classes in solving the task at hand.
Service Implementation
Our next step is to explicitly show that this class implements the TaskQueueService
interface.
To achieve this, we use the implements keyword followed by the interface name. This ensures that our class inherits the behavior defined by the interface.
Since interfaces only provide method declarations, you must override and implement these methods in our class.
Now, in our class, all the methods that need to be overridden are shown. Let's go ahead and do it!
TaskQueueServiceImpl
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.Task; import codefinity.taskManager.TaskQueueService; import java.util.LinkedList; import java.util.Queue; public class TaskQueueServiceImpl implements TaskQueueService { private Queue<Task> taskQueue = new LinkedList<>(); @Override public void addTask(Task task) { taskQueue.offer(task); } @Override public Task getNextTask() { return taskQueue.poll(); } @Override public boolean isEmpty() { return taskQueue.isEmpty(); } }
In our case, nothing overly complicated was done. We simply used the methods of the Queue
class to implement our methods correctly. Thus, a data structure like a queue makes life easier for all Java programmers.
We use the offer()
method for adding, the poll()
method for removal, and the isEmpty()
method to check if the queue is empty.
Now it's your turn to play a role in solving this problem. You'll need to implement a service class, the interface of which we will create together:
Main
package codefinity.taskManager; public interface TaskProcessorService { void processTasks(); }
We've created a service interface with one method: processTasks()
, which, when invoked, should start executing all tasks until the task list is empty. This method represents a user starting to perform tasks.
Task
Your task is to write a class TaskProcessorServiceImpl
that should implement the TaskProcessorService
interface. This class should have a method to process all tasks, meaning it should use methods from the TaskQueueService
service. You can use composition by creating an instance of this class within the class you need to implement, for example:
Main
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.TaskQueueService; public class TaskProcessorServiceImpl { TaskQueueService taskQueueService = new TaskQueueServiceImpl(); }
In general, there is already a prepared composition at the top for your task. Next, you just need to implement one method using an instance of this class.
After that, you need to create a constructor that will initialize this object of the taskQueueService
class.
This way, you'll be able to use the methods of this object. It's also evident that, for task processing, you need to pass the queue with which the taskProcessor
will work.
The rest of the task is left to you. Hints will be provided in the README.md file. Once your solution is ready, click on the "Run Tests" button, and the unit tests I have written will check your solution.
- First, you should use composition and create an instance of the
TaskQueue
class. Then, also add its initialization in the constructor. - Next, implement the
TaskProcessorImpl
interface and override its methods. - In the implementation of the interface, use a while loop with the
isEmpty()
method as the condition. - Inside the
while
loop, use thetaskQueueService.getNextTask()
method, indicating that the task is completed. Output information to the screen usingSystem.out.println()
-"Processing Task: " + task
. - When the loop finishes its work, output
"All tasks processed."
to the screen. - Run the tests and check the correctness of your solution.
java
Obrigado pelo seu feedback!