mardi 23 décembre 2014

Reorganize code: from many function to interface system

I currently have a class with some code which basically calls some functions and then perform the max/min on all the results



float limitOtherSide = limitImposedByPreviousVerticesOnOtherSide(other, pNewX, minimumDistance, pIsUpper);
float limitInterpolation = limitToPreventInterpolatedPointsTooClose(other, pNewX, temp, minimumDistance, pIsUpper);
float limitToPreventChocksOnMiddleSCurve = limitToAvoidChockesAlongAllTheSCurve(other, pNewX, temp, minimumDistance, playerWidth, pIsUpper);
float limitToGrantMinimumDistanceVertically = limitToAvoidChockesAlongAllTheSCurve(other, pNewX, temp, minimumDistance, 0, pIsUpper);
float limitOutOfScreenInterpolation = limitToPreventeForcedOutOfScreenNextOppositeVertex(other, pNewX, minimumDistance, pIsUpper);
float limitChocke = limitToAvoidChocke(other, pNewX, minimumDistance, pIsUpper);

if(pIsUpper) {
float max1 = Math.max(limitOtherSide, limitInterpolation);
float max2 = Math.max(limitToPreventChocksOnMiddleSCurve, limitOutOfScreenInterpolation);
float minimumY = Math.max(Math.max(max1, limitToGrantMinimumDistanceVertically), Math.max(max2, limitChocke));
newLowerY = MathUtilities.clamp(minimumY, 0.1f, 1);
newUpperY = 1.f;
} else {
float min1 = Math.min(limitOtherSide, limitInterpolation);
float min2 = Math.min(limitToPreventChocksOnMiddleSCurve, limitOutOfScreenInterpolation);
float maximumY = Math.min(Math.min(min1, limitToGrantMinimumDistanceVertically), Math.min(min2, limitChocke));
newUpperY = MathUtilities.clamp(maximumY, 0 , 0.9f);
newLowerY = 0.f;
}
float newY = (float) RANDOM.nextGaussian(newLowerY, newUpperY);


I'd like to reorganize it for several reasons:



  • the functions are almost pure and self contained

  • I'd like to be able to switch on and off easily which checks to perform (even if it's a matter of changing the source code)

  • I'd like to separate the limit checking from the rest of the code of the class which is more general about how to generate points

  • I plan on adding more functions, which will clutter the class even more

  • Being able to change only one part of the code to add a new check instead of having to concatenate even more Math.max and add variables and so on in the calling function


So, I was thinking about changing the code to something like



List<Filter> mFilters; //initialized in the constructor
[...]
if(pIsUpper) {
for(Filter f : mFilters) {
newLowerY = Math.max(newLowerY, f.doFilter(args..));
}
newLowerY = MathUtilities.clamp(newLowerY , 0.1f, 1);
newUpperY = 1.f;
} else {
for(Filter f : mFilters) {
newUpperY = Math.min(newUpperY , f.doFilter(args..));
}
newUpperY = MathUtilities.clamp(newUpperY , 0.1f, 1);
newLowerY = 0.f;
}


The point is: I don't know how to define the interface. As you might have noticed all the functions have some common basic arguments, but then they differ for a few of them.


My options:




  1. Make the interface take all the possible arguments, and then ignore them in the implementation. This is not doable as in my current code I call the same function with 2 different arguments, and thus I'd need to make 2 separate implementations of that functions that use 2 separate arguments and put both of them in the interface (really ugly solution)




  2. Implement the interface with 2 methods, one is a doFilter(), the other is a generic passArgument(Object ...), but this is not different than calling all the functions like now, but the result in a list, and then compute the max/min of it. Moreover I'd like to keep code "clean" and passing unchecked objects looks like a really ugly solution




  3. Implement the interface as doFilter with all the common args, and for the remaining specific arguments pass a callback to the constructor of the filter, which can be used to get the additional parameters. I originally went for this solution, but then noticed one of the functions need a parameter that is calculated inside the calling function, so I either make that parameter a field in the class and return the field in the callback or I pass a new callback which passes the new value every time. I don't really like this last change I'd need to do.




So, I'm asking you, is there a paradigm/best practice/design pattern to solve this kind of problem?


Am I worrying over a non-problem? Right now the class is not huge, but at least half of it (if not more) is composed by these functions.


Aucun commentaire:

Enregistrer un commentaire