How to remove dependency on Java enumeration values?

[Remember the gap: I know that the best solution would be to completely get rid of the listing, but this is not an option for today, as indicated in the comments, but it is planned for the future.]

We have two deployment modules: frontend and backend. The interface uses an enumeration and calls the EJB service on the backend with the enumeration as a parameter. But the listing changes often, so we don’t want the backend to know its meaning.

String constants

A possible solution would be to use String constants instead of enums, but this would lead to small changes in the interface. I am looking for a solution that causes as few changes in the interface as possible.

Wrapper class

Another solution is to use a wrapper class with the same interface as the enumeration. An enumeration becomes a wrapper class, and enumeration values ​​become constants inside this wrapper. I had to write some deserialization code to guarantee the identity of the object (as the enumerations are enumerated), but I don't know if this is the right solution. What if different classloaders are used? The wrapper class implements a Java interface that replaces enum with a backend. But will deserialized code run in the backend even like that?

Example for a wrapper class:

public class Locomotion implements Serializable { private static final long serialVersionUID = -6359307469030924650L; public static final List<Locomotion> list = new ArrayList<Locomotion>(); public static final Locomotion CAR = createValue(4654L); public static final Locomotion CYCLE = createValue(34235656L); public static final Locomotion FEET = createValue(87687L); public static final Locomotion createValue(long type) { Locomotion enumValue = new Locomotion(type); list.add(enumValue); return enumValue; } private final long ppId; private Locomotion(long type) { this.ppId = type; } private Object readResolve() throws ObjectStreamException { for (Locomotion enumValue : list) { if (this.equals(enumValue)) { return enumValue; } } throw new InvalidObjectException("Unknown enum value '" + ppId + "'"); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (ppId ^ (ppId >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Locomotion)) { return false; } Locomotion other = (Locomotion) obj; if (ppId != other.ppId) { return false; } return true; } } 

Did you already have the same problem? How did you solve it?

+4
source share
4 answers

Ok, let me see if I understand. you said, that

"The interface uses the enumeration and calls the EJB service to the backend with enum as a parameter. But the enumeration often changes, so we don’t want the backend to know its values."

When you say “values,” I assume that you are referring to the numeric value that you pass in the enumeration constructor, not the enumeration constants.

Therefore, this means that the interface and backend will have two different versions of the enum class, but the enumeration constants in them will be the same.

I assume that communication is via RMI (but this is not entirely clear in your post).

Enumeration serialization / deserialization now works differently than with other objects. According to the Java Serialization specification, when an enumeration is serialized, only its name is serialized. And when it is deserialized, it is created using the Enum.valueOf (name) method.

So, your original shell proposal will not work, because the server, due to the enums envisioned, will never know the actual value of the enumerations in the client.

On the bottom line, if you intend to transfer the enumeration to the server, there is no way to do what you pretend, because the values ​​in the interface will never reach the backend, if serialization is meant.

If RMI is implied, a good solution would be to use code portability, so you could put the problematic class in the repository, accessible for both the server and the client, and when the front-end developers change the class definition, you can publish the class in the repository and server can get it from there.

See this article on dynamically loading code using the code base property in RMI http://download.oracle.com/javase/6/docs/technotes/guides/rmi/codebase.html

Another possible solution is that you can stop using Java Enum and use a Java class with finite constants, as it was before in the previous days before enumerations, and in this way you can guarantee that its values ​​will be correctly serialized when they are are sent to the server.

Somewhat

 public class Fruit implements Serializable{ private static final long serialVersionUID = 1L; public final Fruit ORANGE = new Fruit("orange"); public final Fruit LEMON = new Fruit("lemon"); private String name; private Fruit(String name){ this.name = name; } } 

This way you can fully control what happens during deserialization, and your shell template can work that way.

This type of construction cannot completely replace an enumeration; for example, it cannot be used in switch statements. But if this is a problem, you can use this object as a parameter sent to the server and let the server rebuild the enumeration from it with its version of the enum class.

Thus, your listing may have two new methods: one for creating Java instances from the listing itself:

 public static Fruit toFruit(FruitEnum enum); public FruitEnum valueOf(Fruit fruit); 

And you can use them to convert versions and version settings for the server.

+2
source

This is an odd request, since I think the server should know about the values ​​of what is being collected in the database, but normally, I will play. Perhaps you could do it

 public enum Giant {Fee, Fi, Fo, Fum}; public void client() { Giant giant = Giant.Fee; server(giant); } public void server(Enum e) { String valueForDB = e.name(); //or perhaps String valueForDB = e.toString(); } 
+1
source

To transfer data between the interface and the backend, it is necessary to use the same class versions because of the possible serialization during the sorting of parameters. So again, they need to know exactly the same enumerations or any other classes that you are trying to use. Switching transitions to something else will not work either. You must set a well-known class identifier for both.

So, if the server has to do actions based on any processing / calculation of parameter values, use strings or any other immutable class that you decide on, and put your values ​​inside: a string of characters, an array of numbers, or something else.

So, if you put your database identifier inside a wrapper object, the server will be able to retrieve the objects from the database. But still - they both need the exact version of the wrapper class in their classes.

+1
source

Well, I can’t be too precise, because I don’t see your code, but in my experience something that changes must be external data, not enumerations.

What I almost always find is that if I exteriorize the information contained in the enumerations, then I must exteriorize several other parts, but after all this, I end up canceling a lot of code.

Every time you really use enumeration values, you almost certainly write duplicate code. I mean, if you have enums like "HEARTS", "DIAMONDS" ...

The only way they can be used in your code is something like a switch statement:

 switch(card.suit) case Suit.HEARTS: load_graphic(Suit.HEARTS); // or better yet: Suit.HEARTS.loadGraphic(); break; case Suit.SPADES: Suit.SPADES.loadGraphic(); ... 

Now this is obviously stupid, but I made a stupid restriction to say that you used the values ​​in the code. My statement is that if you are not using values ​​that you do not need, list them - do not use the values ​​in the code and see:

 card.suit.loadGraphic(); 

Wow, everything is gone. But suddenly, the whole purpose of using renaming has disappeared - instead, you will get rid of the entire preload a "Suit" factory class with 4 instances from a text file with lines like "Heart.png" and "Spade.png".

Almost every time I use enumerations, I end up factoring them as follows.

I'm not saying that there is no code that can benefit from the enumerations - but the better I get the factoring code and externalizing the data, the less I can imagine that they are really needed.

-1
source

Source: https://habr.com/ru/post/1347198/


All Articles