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

Development: Replace if-else chains with pattern matching #9303

Conversation

SamuelRoettgermann
Copy link

@SamuelRoettgermann SamuelRoettgermann commented Sep 10, 2024

Checklist

General

Server

Motivation and Context

If-else chains aren't pleasant to read, nor are they particularly efficient, and since Java 21 we have pattern matching, so let's use that.
Switch-statements may be optimized to run in O(1), whereas if-else chains will typically lead to O(n) runtime complexity, however this isn't guaranteed as it depends on the respective Java implementation.
Either way, the benefit in terms of performance will probably be negligible, however the readability improvement alone justifies this change.

Description

Changed if-else-instanceof-chains to utilize pattern matching (introduced by Java 21) instead. No logic was changed.

Steps for Testing

Since this doesn't affect any logic, there are no observable changes.
Only ways to test are code review and running the server tests, which do all pass for me locally, except for 8 unrelated tests.

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Summary by CodeRabbit

  • New Features

    • Improved handling of complaints and filtering of sensitive information.
    • Enhanced clarity in the logic for determining participant types and submission types.
  • Bug Fixes

    • Improved error handling for unrecognized submission and exercise types.
  • Refactor

    • Simplified code structure with early returns and switch-case statements for better readability and maintainability.

Copy link

⚠️ Unable to deploy to test servers ⚠️

The docker build needs to run through before deploying.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Sep 10, 2024
@SamuelRoettgermann SamuelRoettgermann removed the deployment-error Added by deployment workflows if an error occured label Sep 10, 2024
@Strohgelaender
Copy link
Contributor

Nice. Is this the only place where the new style can be used? If not, can you convert other places as well?

@SamuelRoettgermann
Copy link
Author

Nice. Is this the only place where the new style can be used? If not, can you convert other places as well?

Good idea, I actually didn't check. I'll try to find other suitable places.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

Commits

Files that changed from the base of the PR and between 4a139b7 and ff47d77.

Files selected for processing (1)
  • src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java (1 hunks)
Additional context used
Path-based instructions (1)
src/main/java/de/tum/in/www1/artemis/service/exam/StudentExamService.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

Copy link

github-actions bot commented Oct 5, 2024

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions bot added the stale label Oct 5, 2024
this.student = null;
this.team = null;
}
default -> throw new Error("Unknown participant type");
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to seal the participant in order to remove the default case?

Copy link
Author

@SamuelRoettgermann SamuelRoettgermann Oct 18, 2024

Choose a reason for hiding this comment

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

Ah sorry, I'm sick rn so I'm not too active on my computer hence the delay.

What exactly do you mean by "seal"? Do you mean the Java 17 sealed keyword and that I should make Participant a sealed class so that we can remove the default cases?

Copy link
Contributor

@dfuchss dfuchss Oct 18, 2024

Choose a reason for hiding this comment

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

No problem at all. Yes, I mean "sealed". I think the pattern matching should detect that you have all cases. I'm not 100% sure whether the base is an interface, abstract class or normal class, but you have some possibilities with sealed here. See sample below :)

Copy link
Contributor

Choose a reason for hiding this comment

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

image

Copy link
Author

Choose a reason for hiding this comment

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

Oh yeah, that looks really nice, thx for the suggestion.
I'll look more into sealed. I've looked at it on Java 17 release, but haven't ever actually used it in a project since, so I just want to make sure that by adding this, I don't accidently "destroy" something else (be it compatibility, performance, etc.).

I'll push the changes tomorrow (if my research didn't reveal something bad).

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem could be the package structure of Artemis here. Maybe that's something to discuss with @krusche from a programming perspective, it would be really good to use sealed here because it ensures that you'll find all places if another type of participant occurs. You are also welcome to contact me via Slack in this regard.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, the package structure is a problem.

I tried a few things, but we'd basically have to move de.tum.in.www1.artemis.domain.User.java and de.tum.in.www1.artemis.domain.Team.java into the de.tum.in.www1.artemis.domain.participation folder, otherwise it complains about Class is not allowed to extend sealed class from another package.

But yeah, in order to address that we'd definitely need to talk to @krusche to ensure we even can make such changes.

Copy link
Author

Choose a reason for hiding this comment

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

Errrr. I also just realized that since this PR had been created some time ago (but I couldn't work on it cause vacation, into retake preparation into sickness) the entire file structure changed.

Like, it's no longer de.tum.in.www1.artemis, but now it's de.tum.cit.aet.artemis.

So yeah... I guess I'll need to spend some time again to manually apply all the changes to the new files, cause I honestly have no idea how to automatically merge this, if it's even possible.

Copy link
Author

Choose a reason for hiding this comment

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

This also means that the locations I said earlier, i.e.

we'd basically have to move de.tum.in.www1.artemis.domain.User.java and de.tum.in.www1.artemis.domain.Team.java into the de.tum.in.www1.artemis.domain.participation folder

should've said this:

we'd basically have to move de.tum.cit.aet.artemis.core.domain.User.java and de.tum.cit.aet.artemis.exercise.domain.Team.java into the de.tum.cit.aet.artemis.exercise.domain.participation folder

@@ -450,25 +450,14 @@ SELECT COUNT(DISTINCT s)
* @return a new submission for the given type connected to the given participation
*/
default Submission initializeSubmission(Participation participation, Exercise exercise, SubmissionType submissionType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same here. you could think about sealing the submissions. this should remove the default case

Copy link
Author

Choose a reason for hiding this comment

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

Actually here we would need to seal the Exercise, not the Submission, but it wouldn't give us too much, because we would still need the null case anyways.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but not the default case.

@github-actions github-actions bot removed the stale label Oct 17, 2024
coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 20, 2024
coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 20, 2024
@github-actions github-actions bot added assessment Pull requests that affect the corresponding module core Pull requests that affect the corresponding module exam Pull requests that affect the corresponding module exercise Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module quiz Pull requests that affect the corresponding module labels Oct 20, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java (2)

340-357: Excellent refactoring using pattern matching and switch expression!

The changes improve readability and adhere to the KISS principle. The early return for null originalSubmission simplifies the logic flow. However, consider extracting the switch expression into a separate method for better modularity and reusability.

Consider extracting the switch expression into a separate method:

private Submission createSubmissionWithOnlyId(Submission originalSubmission) {
    return switch (originalSubmission) {
        case TextSubmission ignored -> new TextSubmission();
        case ModelingSubmission ignored -> new ModelingSubmission();
        case FileUploadSubmission ignored -> new FileUploadSubmission();
        case ProgrammingSubmission ignored -> new ProgrammingSubmission();
        default -> null;
    };
}

Then, you can simplify the main method:

Submission submissionWithOnlyId = createSubmissionWithOnlyId(originalSubmission);
if (submissionWithOnlyId == null) {
    return;
}

This change would improve modularity and make the code more reusable.


362-367: Good use of switch statement for type checking and object creation!

The refactoring improves readability and error handling. However, for consistency with the previous segment and to further improve readability, consider using a switch expression here as well.

Consider refactoring to use a switch expression:

Exercise exerciseWithOnlyTitle = switch (originalParticipation.getExercise()) {
    case TextExercise ignored -> new TextExercise();
    case ModelingExercise ignored -> new ModelingExercise();
    case FileUploadExercise ignored -> new FileUploadExercise();
    case ProgrammingExercise ignored -> new ProgrammingExercise();
    case null, default -> {
        log.error("Invalid exercise {} passed", originalParticipation.getExercise().getType());
        yield new Exercise();
    }
};

This change would make the code more consistent with the previous segment and potentially more readable.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3b48009 and e832686.

📒 Files selected for processing (2)
  • src/main/java/de/tum/cit/aet/artemis/assessment/domain/Complaint.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/de/tum/cit/aet/artemis/assessment/domain/Complaint.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

📓 Learnings (1)
src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java (2)
Learnt from: julian-christl
PR: ls1intum/Artemis#7829
File: src/main/java/de/tum/in/www1/artemis/web/rest/ComplaintResponseResource.java:0-0
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The user has fixed the issue regarding the redundant wildcard import in `ComplaintResponseResource.java` by removing it in commit 7e392e0.
Learnt from: julian-christl
PR: ls1intum/Artemis#7829
File: src/main/java/de/tum/in/www1/artemis/web/rest/ComplaintResponseResource.java:0-0
Timestamp: 2024-01-08T18:32:55.982Z
Learning: The user has fixed the issue regarding the redundant wildcard import in `ComplaintResponseResource.java` by removing it in commit 7e392e0.
🔇 Additional comments (1)
src/main/java/de/tum/cit/aet/artemis/assessment/web/ComplaintResource.java (1)

369-369: Good addition of whitespace for improved readability.

The empty line after the switch statement helps to separate logical blocks of code, enhancing overall readability and structure.

this.student = null;
this.team = null;
}
default -> throw new Error("Unknown participant type");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Throw an appropriate exception instead of 'Error' in the default case

Throwing an Error is generally reserved for serious system failures and is not recommended for handling unexpected input or states. Instead, use a more appropriate unchecked exception like IllegalArgumentException to indicate that the participant type is not recognized.

Apply this diff to fix the issue:

                 default -> throw new Error("Unknown participant type");
+                default -> throw new IllegalArgumentException("Unknown participant type");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
default -> throw new Error("Unknown participant type");
default -> throw new IllegalArgumentException("Unknown participant type");

throw new Error("Unknown participant type");
switch (participant) {
case User user -> this.student = user;
case Team team1 -> this.team = team1;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use consistent variable naming in switch-case patterns

In the switch statement, the variable team1 is used in case Team team1 -> this.team = team1;. For consistency and clarity, consider renaming team1 to team, aligning it with the field name this.team and improving readability.

Apply this diff to rename the variable:

             switch (participant) {
                 case User user -> this.student = user;
-                case Team team1 -> this.team = team1;
+                case Team team -> this.team = team;
                 case null -> {
                     this.student = null;
                     this.team = null;
                 }
                 default -> throw new Error("Unknown participant type");
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case Team team1 -> this.team = team1;
case Team team -> this.team = team;

Copy link
Contributor

@SimonEntholzer SimonEntholzer left a comment

Choose a reason for hiding this comment

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

Code lgtm. I assume I integrating the "sealed" classes didn't work because of our package structure?

Copy link

github-actions bot commented Nov 4, 2024

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions bot added the stale label Nov 4, 2024
@github-actions github-actions bot closed this Nov 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assessment Pull requests that affect the corresponding module chore code quality core Pull requests that affect the corresponding module exam Pull requests that affect the corresponding module exercise Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module performance quiz Pull requests that affect the corresponding module server Pull requests that update Java code. (Added Automatically!) stale tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants