Course Content
Java Data Structures
Java Data Structures
Queue Practice
Practice
It's time to practice. You are given 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.
Note
In this assignment, we will use the interface-implementation structure. First, we'll write an interface that will define the methods to be implemented by the inheriting classes.
But first, we 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 3 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 we access the elements?
The attributes are also protected by the private
access modifier because we 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 key control + return on a MacBook.
After the performed operations, we 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
We will have a very simplified task board service, so let's implement a service that will add, mark 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 in this service class.
It's worth making a small digression and talking about service classes, which are a separate part of OOP. Service classes are created to perform specific operations. They inherit from service interfaces and implement their methods. This approach to writing an application helps maintain and extend it while adhering to the SOLID principles, which we will study in a separate course. At the moment, it's important to understand that such classes are created solely to perform operations on other objects.
For example: If we write a calculator, we will have a separate class that will store the value of the number we 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
Let's continue the task execution. Our next task is to show that this class implements the TaskQueueService
interface. To do this, we use the keyword implements
and specify the interface that the class implements. We also need to override the methods defined by the interface. IntelliJ IDEA also allows us to significantly simplify this task using the combination option + return.
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.
Note
If you want to make the task a bit more challenging, you can add a method for processing a single task. In that case, you need to create a separate method,
processTask()
, in this interface. This method should process the first task in the list.
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.
Thanks for your feedback!