Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submit #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Submit #1

wants to merge 2 commits into from

Conversation

niloufarmp
Copy link

Here is my solution.

Copy link
Member

@polad polad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big pull request (over 50 files). So tried to focus on the key areas. Things that we pay attention to in this exercise are "data structures", "separation of concerns", "single responsibility principle", "code reuse" and preferrably utilization of the "design patterns".

The Good Stuff 👍
Your solution is functioning as expected and successfully processes the provided JSON file as input. You used the right data structures here for example a Map for storing statuses by asset id. You also utilised the "Observer" pattern along with other design patterns like "Dependency Injection" which we expected. Also liked that you chose composition over inheritance in a few places in your implementation. Looks like you also implemented the web service which is a good addition although I hope you did not spend too much time on it since it was not required.

Not sure about 😕
It feels like the implementation is overly complicated and could be further simplified. For example the usage of queues for handling asset status changes as well as other code associated with it was excessive and unnecessary for this exercise. Also the requirements were probably not clear enough since you implemented the "Observer" pattern to subscribe to status changes per asset instead of all assets. Understanding requirements is very important and if something is unclear (we deliberately miss some details in these exercises) developer should ask the right questions to fill the gaps. Overall the code could have been separated into classes and interfaces differently for more clarity, better separation of concerns and classes and methods could be named differently to improve readability.

sendMessages(file, applicationContext);
}

waitForProcessToFinish(applicationContext);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary to wait here for 10ms? Doesn't look like sendMessage() called asynchronously so the execution would not proceed untill the for loop finishes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this waiting time so the application will finish all its process before I start to get reports. It is not necessary for the outcome but I wanted the console output to be clear and readable. First program will do all the process and print notifications in the console. Then it will print the reports.
I could have also divide output to separate files for this matter.

}
}

private static void sendMessages(File file, ConfigurableApplicationContext applicationContext) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for the whole ConfigurableApplicationContext here? Looks like you're just using an instance of a MessageHandler

JsonArray jsonArray = (JsonArray) obj;
for (JsonElement element : jsonArray) {
AssetStatusMessage message = gson.fromJson(element, AssetStatusMessage.class);
MessageHandler messageHandler = applicationContext.getBean(MessageHandler.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This messageHandler is going to be redefined on every JSON message since it's in the loop. Why not pull it outside of the for loop?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry I missed it.

assetsInStateReport(applicationContext);
}

private static void subscribeSomeListeners(ConfigurableApplicationContext applicationContext) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for the whole ConfigurableApplicationContext here? Looks like you're just using an instance of an EventHandlerFacade

for (JsonElement element : jsonArray) {
AssetStatusMessage message = gson.fromJson(element, AssetStatusMessage.class);
MessageHandler messageHandler = applicationContext.getBean(MessageHandler.class);
messageHandler.assetStatus(message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a message handler I'd call this method:

messageHandler.handle(message);

usually we use nouns for constructors but methods are named after their actions using verbs

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your advice, I will keep that in mind.

try {
AssetStatusEvent event = AssetStatusEvent.valueOf(subscribeInfo[1]);
String assetId = subscribeInfo[0];
SubscribeRequest subscribeRequest = new SubscribeRequest(assetId, event, new SampleListener());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would subscribe to events for a particular asset only. We need to be able to subscribe to status changes of any asset since we don't know about possible assets in advance. Also, given that there could be many assets, we would require a lot of subscriptions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have ask you to clarify this for me. Actually, in a real situation I would ask what does "subscribe to certain asset status events" mean exactly. But here I made an assumption that it means "subscribe to a specific asset rather than all assets". My mistake.

Date updatedAt = assetStatusMessage.getCreatedAt();
Status currentStatus = assetStatusMap.get(assetId);
//updateStatus asset status in map if it's a new status
if (currentStatus == null || updatedAt.getTime() >= currentStatus.getCreatedAt()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a need to comment any code it's an indicator that this code can be improved for readability. In this case, we could extract the condition into a method:

Suggested change
if (currentStatus == null || updatedAt.getTime() >= currentStatus.getCreatedAt()) {
if (isNewStatus(assetStatusMessage)) {

Status currentStatus = assetStatusMap.get(assetId);
//updateStatus asset status in map if it's a new status
if (currentStatus == null || updatedAt.getTime() >= currentStatus.getCreatedAt()) {
assetStatusMap.put(assetId, newStatusType, updatedAt);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To simplify the put() method signature I'd suggest the following:

Suggested change
assetStatusMap.put(assetId, newStatusType, updatedAt);
assetStatusMap.put(assetId, new Status(assetStatusMessage));

To be able to do the above I would provide additional constructor for the Status class that could populate a Status instance from AssetStatusMessage:

    public Status (AssetStatusMessage message) {
        this(message.statusType, message.createdAt);
    }

public GeneralResponse assetStatus(AssetStatusMessage assetStatusMessage) {
try {
//store asset status
StatusType oldStatusType = assetStatusService.updateStatus(assetStatusMessage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two collaborators in this method one is AssetStatusService which contains asset statuses and is essentially a "state object". The second collaborator here is EventHandlerFacade which is used to notify observers/listeners about state changes. Observers use EventHandlerFacade to subscribe to events hence it is a "Subject" that observers listening to. Since all the state is stored in AssetStatusService and the EventHandlerFacade is not aware of those state changes this MessageHandler class is used to bridge the gap between the two collaborators.

I see a discrepancy here. Usually, the "Subject" is something that is aware of the state and can produce those events about the state change. I think in this case the implementation would be simpler if the observers would subscribe to events on AssetStatusService which would be able to produce those status change events. Then we probably wouldn't need this MessageHandler class as well as the EventHandlerFacade.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree with you. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants