Industry standard rarely would restrict you to sending and receiving only primitive data. There are a few reasons for it, but mostly its so that the code can be read easier, and meaning can be associated with numbers. So inIn your example instead of passing in a multi-dimensional array (which gives little in the way of meaning to numbers) you would instead pass in a collection of some class. It equates to the same in the end, but sure is easier to read. I know you said that it was part of the requirmentsrequirements, but I couldn't help but speak up to this. On to your code: I am not a big fan of having a class with only public static methods. There are reasons for it, but in general it's not good practice. I've been seeing more and more people starting to prefer constructor parameters for a class. The idea is that you give the class everything it requires to start work. You'd, then call a method to perform said calculation and. How you get the calculation depends you could either return the value, or have itthe method set a field in your class that has a public getter. Here is basically what I'm saying in code
#Option one
public class MarkCalculator {
private final int[][] studentData;
public MarkCalculator(int[][] studentData) {
this.studentData = studentData;
}
//option one
public int[] computeMarks() {
//...
}
}
#Option two
public class MarkCalculator {
//optionprivate twofinal int[][] studentData;
private int[] marks;
public MarkCalculator(int[][] studentData) {
this.studentData = studentData;
}
public void computerMarks(){
//...
}
public int[] getMarks(){
return marks;
}
}
For this scenario I think I would prefer to return void, and have a public getter for marks. The reason I would prefer that is it would appear (based on the commented out code, and the graph that you show) that you want to do different things based on thethis specific data. IfOn the classes that you want to use this data you would pass in aan instance of MarkCalculator
to whatever you. This tells people who are hoping to do with the data I believe it leaves less reasongoing to change in the future. for example: your data type could change, the format ofuse the data could change or any other number of reasons. The classes that depend on the data were built specifcallyit was made specifically for this data, so if you pass in the class that generates this data I believe that you'll have less reasons to changewas computed for MarkCalculator
. Now this isn't always the case and you'll want to weight the pro's and con's before you commit to one style or another.
I caught in the code that you have "debug" code in. I have two things to say about this.: 1) Create a class that shows said data, or debug data and keep it out of your MarkCalculator
. The second part is that I don't2) other programmers might not like using the console and visually inspecting your values. I would preferto inspect data nor do they want to see some sortthe clutter of unitcommented out debug code. Using an automated test framework can fix this. Java has a few different testing frameworks and, but I'm not going to promote any particular one since they are similar in syntax and in reality they accomplish the same thing. They allowThese tests are a way to address both points. The first point is covered because it is a class that, typically, has "test" in its name. Other programmers will want to look for youtests to write codeknow how to use the class and see what type and format the data needs to be in. With the tests for thatisolated from production code to give you don't clutter up the desired feedback to know that your classes work as intended. Soproduction code with "debug" code. With that in mind here is a test I started to make and came accrossacross a potential refactoring point/bug fix
I noticed that when writing this you require/state that you'll ALWAYS have 6 modules. This would mean that MarkCalculator
could be enhanced if you were to make it more dynammicdynamic. This test in place runsTests are a nice way of informing you (very quickly) if the changes made to a class are breaking anything (I say very quickly because most tests run in less than 1 second and Ithey can be run it over and over and over againrepetitively). When it fails my IDE gives meshows a big ol red X and red progress bar saying something failed and a description of why it failed. Likewise if it passes I get a nice green check mark. No guessing or reading required and progress bar. NowNow lets dig into your code. It takes less than a a second to run this test so I'll run it frequently.
I see that computedModuleMark
is declared outside of where it is used. It is aIts an integer and so does not need cleaningany resources cleaned up, nor is it used outside of that method. There for it'sTherefor its declaration and value should be on the same line. After doing that I check my test and it still passes. Also you have a hardcodedhard coded the size of marks, yet it should be dynammicdynamic to match the number of modules. quick fix, and run my tests andI changed it still works.to int[] moduleMarks = new int[studentData.length];
Movingand run my tests and it still works. Moving down we see some nicely declared variables stating what index 0,1, and 2 are. I like that! theThe last part to this method, though, has some logic that could and should be moved out to a small private method. This way you can replace the comment used to describe this next section with a method name that does just what the comment says. IMO pullingPulling out this small bit of logic into a small method also makes it easier to see how itthe logic can be rewritten (if possible). Indeed it can happen too. Often times when refactoring multiple conditionals it is worthwhile to reverse the conditional and see if it clears things up. I actually found that doing so removes the need both conditionals. Now unless I'm wrong (and you can proove otherwise) your new method would like The refactored code that I mentioned specifically looks like this:
I'dI have more to say, but I'll leave it for someone else.