My situation is designing my game with Javas OOP as clean as I can I need to store static methods of the another class into a field of current without using try{}catch{}
. Being more specific I need to have multiple sort methods with multiple sortable classes like this:
//For storing all legal types of sorting
public enum SortType {
ByPositionX,
ByPositionY,
ByPositionZ,
ByPosition2d,
//...
}
Not all of the classes that may be sorted have to contain all the methods, so:
//for classes to sort
public interface Sortable {
boolean supportsSortType(SortType type);
double getSortValue(SortType type, double... args);
}
And then I remembered DRY and did the entire new class of functions for sorting:
public class Sorting {
//as you can see all the functions has the same return type and args
public static double getSortValueByPositionX(Object position, double... args) {
return (Point)(position).getX();
}
public static double getSortValueByPositionY(Object position, double... args) {
return (Point)(position).getY();
}
public static double getSortValueByPositionZ(Object position, double... args) {
return (Point)(position).getZ();
}
public static double getSortValueByPosition2d(Object position, double... args) {
double width = args[0];
return (Point)(position).getX() + position.getY() * width;
}
//may it work???
@FunctionalInterface
public interface SortingValueMethod {
double getSortValue(Object field, double... args);
}
}
I did all the functions with the same layout for easy storing, and now i have the issue:
public class Block implements Sortable {
//it actually has to be public static final Map<SortType, Method>
public static final SortType[] SUPPORTED_SORT_TYPES = {
SortType.ByPositionX,
SortType.ByPositionY,
SortType.ByPositionZ,
SortType.ByPosition2d,
};
@Override
public boolean supportsSortType(SortType type) {
for (SortType curSortType : SUPPORTED_SORT_TYPES)
if (curSortType.equals(type))
return true;
return false;
}
@Override
public double getSortValue(SortType type, double... args) throws UnsupportedSortTypeException {
if (!supportsSortType(type))
throw new UnsupportedSortTypeException("Type " + type.toString() + " is unsupported for class " + Block.class.getName());
//find a Sorting.method by a key and
return call_it_with(this.position, null);
}
}
I have to pair SortTypes and methods of sorting somehow and I need it as public static final
field for obvious reasons. Is there anything in Java that might help me with this crazy design?
You seem to be asking about commonly held OO principles. And yet your code is quite far removed from anything that would pass muster for the vast, vast majority of such tenets.
//for classes to sort
public interface Sortable {
boolean supportsSortType(SortType type);
double getSortValue(SortType type, double... args);
}
No; this violates Liskov Substitution Principle. throwing an UnsupportedOperationException
in a 'static' fashion (a given object, or even worse, any instance of a given class, will always throw it for a given method), is a pretty big code smell. If your class implements Sortable, and Sortable implies that you support getSortValue(SortType.ByPositionX, args)
, I expect that your type then supports that. I don't want to see that method go: "Psyche!! I don't actually support that haha!".
If sorting by all these 4 different dimensions is important, then have 4 different interfaces.
Note that there are various 'maxims' about how one should program (DRY is one; Liskov is another). Various scenarios end up with conflicts: Going all out on one 'principle' results in serious violations of the other. Programming is a simple rote application of these maxims until you've applied them all. It's much, much more complicated than that. Good API and good code design (in the sense that it is easy to understand, easy to modify, robust in the face of change requests, easy to test, and bugs, if they exist in it at all, are easy to spot / easy to figure out what's wrong based on a failed unit test) is a lot more complicated that!
You may want to read up on method references / lambdas. You can store code in a variable (I'm oversimplifying) here:
Comparator<Point> orderByX = Comparator.comparingDouble(Point::getX);
The above gets you a comparator object that can compare any 2 points, and does so by comparing the value of 'x' as returned by each point. You can then for example sort a list of Point instances: list.sort(orderByX)
.
You can 'capture' that idea of 'I just need you to turn any T into a double representing its sort order and I will take care of the rest' as, for example, a java.util.function.ToDoubleFunction<T>
. Point::getX
is one implementation of it:
ToDoubleFunction<T> func = Point::getX;
(assuming that's public class Point { public double getX() { ... }}
of course).
That doesn't invoke getX on Point (you can't; Point is a class, not an instance), that captures the idea of calling getX on some instance of Point. You can call func.apply(somePointInstance)
as often as you want, with any instance of Point you can find, to get doubles out.
Have a look at the Comparator class which contains a boatload of static methods that turn such method refs or lambdas into Comparators, which is what you're actually looking for (don't reinvent the sorting wheel, java has it already, and it's called java.util.Comparator
).