Okay so I have a class which has an attribute which needs to be an enum state. The problem is, depending on the context, the values that this state can have can change. I have two enums class to deal with that and they both implement an Interface. The class actually has the interface as attribute as I thought it will solve the problem, but looks like this does not work...what is the cleanest way to deal with the situation ? (See the class diagram, it explains better than me)
#Problem with enums (OOP problem)
141 messages · Page 1 of 1 (latest)
⌛ This post has been reserved for your question.
Hey @mossy hornet! Please use
/closeor theClose Postbutton above when you're finished. Please remember to follow the help guidelines. This post will be automatically closed after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
Depending on how the program is launched, the Client can either have access to the FirstEnum states or the SecondEnum states. Note that in the future, it's possible to add a ThirdEnum or more.
What do you mean by "but looks like this does not work..."?
The client can't contain an operation (A, B, ...)
It contains the interface but it doesn't mean anything :/
I can use state.execute() because state isn't A or B, it's FirstEnum or SecondEnum. :/
Yeah, you're abstracting the enum behind your interface, so only the interface would be accessible.
I suppose you could just check if your Ienum is an instance of FirstEnum or SecondEnum and then cast to the appropriate one, whenever you need to perform operations specific to the enum type.
Although there might be an entirely more appropriate data structure for your use case. Not sure what your use case is though.
Is there a reason you need to know which enum was used?
If there's more behavior you want outside of execute, couldn't you add that to your interface and override it within each enum type?
The context is I'm coding a CoreWar game, an old game in which two programs written in assembly are fighting each other in the memory.
The client is the Instruction class, which contains (obviously) one instruction. Each of them has an opcode.
The neat part is that there is different standards with different opcodes
The ICWS88 standard contains 11 opcodes
The ICWS94 standard contains 18 opcodes iirc and some are executed differently because of the modifier addition (a thing that affect the operation execution)
The enums are containing the different opcodes
The way I've mostly commonly seen for handling behavior specific to each enum constant, would be to have your each of your execute method implementations perform a switch on (this) and specify the behavior for each case.
Although what you mentioned in #general is also interesting.
The fact that we can add methods to enum constants can avoid really long switch case statement and make the program easier to extend later imo
So I use it sometimes, not that often
But yeah, I will try a cast, maybe this will work... 🤔
I hope...
And I hope I will be able to do it a clean way
The only reason I'm hesitant to recommend enum constant specific methods it is simply because I've read a lot of code and have never seen it used before.
Even when useful, syntax is problematic if it is unfamiliar to everyone who reads it. I would always prefer to be more verbose, if using an obscure syntax means the behavior is much less obvious.
Although in this case, I think the syntax is easy to understand even to those who are unfamiliar with it, so... It's probably completely fine.
The syntax is...
public enum FooEnum {
A {
@Override
public void bar() {
//code
}
},
B {
@Override
public void bar() {
//code
}
},
C {
@Override
public void bar() {
//code
}
};
public void bar();
}
Eesh... When written out like that, it's actually even more verbose than a simple switch statement.
But sometimes switch will be spread throughout the code
In that case, every method are gathered and the method are shorter
But the file by himself sure can be a big one
You'd do the switch in FooEnum#bar in that case.
I don't see how it's any different from the perspective of an outside class.
I will use the switch if there's few operations and if I'm sure that I will not add more operations later
public enum FooEnum {
A,B,C;
public void bar();
switch(this) {
case A:
break;
case B:
break;
case C:
}
}
^ My class is shorter than yours, and just as obvious in terms of behavior.
Also, is this possible for me to use the Strategy Pattern ?
If Ienum contains all the methods and then I rewrite them in FirstEnum and SecondEnum, throwing an error in case of bad use?
Matter of taste I think, switch in case of 3 is okay, but in case of 20 operations with 5 lines per operations it starts to be long
And if you have multiple operations you need multiple switch
You'd also need multiple method overrides per enum constant. Pretty sure they'd scale at roughly the same rate.
But yeah, you're probably right about it being a matter of taste.
If Ienum contains all the methods and then I rewrite them in FirstEnum and SecondEnum, throwing an error in case of bad use?
Sounds fine to me.
It's big and not satisfying to look at, but easier to find what I want in the code. 🤔
I will try that then, thanks! :D
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
Up to you. Nobody's ever going to complain if you make more than one thread per day.
Okay here is why I didn't close this thread, I got a problem with the Strategy Pattern.
I thought this was the best idea, but...an interface that contains enum fields does not exist ._.
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
JavaBot isn't that efficient in the end...
Can an enum inherit from another enum?
In the end the cast was the best idea 🥹
I don't know how to do that properly either
I mean, how am I supposed to say "this interface is in this enum type", this does not work like that either
@rocky egret It's impossible to cast in an enum type, and for the Strategy Pattern it's impossible as an interface is not an enum. :/
Si I'm back with the same problem...
Have you considered dropping the idea of enums and just making some good old fashion constants?
I just double checked and you can definitely cast an interface to an enum if the enum implements that interface, so I'm not sure what you mean by this.
The IDE does not want me to do (Enum) Interface. :/
The client needs to have one operation among the existing ones, so an enum is almost mandatory
For your consideration:
Create an interface Opcode with a single method execute
An abstract class Standard subclassed by classes Standard88 and Standard94
Each standard implementation contains constants defining each of their Opcodes.
Because Opcode is a functional interface, you can inline the execution for each Opcode.
Also it would be class here not enum.
The idea behind this model is to allow every Operation, and the only code the constant have is (if standard is FirstClass then doForA() here and if it's SecondClass then doForA() here)
Oh, I need to consider that too, it sounds great!
Works fine for me. 
How would you define the constant in the standard implementation? I mean, what would be the content of each class?
I didn't know about that...why is this looking like an enum? 🤔
It can be useful...I don't know I'm a bit lost...^^'>
Enums are essentially just a shortcut to making constants.
Because they're a shortcut, they've got their own limitations though, so they're not a one-size-fits-all solution for every situation that demands constants.
And how is this working to call that ?
Like...
returnedValue = JMP(param); ?
I'm not sure what you mean.
In my current code, every opcode has an execute() method that takes parameter and return a value. I never encountered that kind of code before so I don't really know how it works
The thing is, the client is an Instruction. And instruction contains an opcode and two operands.
So I can't put a standard instead of an opcode. It should be an opcode and nothing else
You can use any params/return values that you'd like. I just used strings as an example.
What do you think about this kind of thing?
Basically a try to make a Strategy pattern
Tell me then! :D
Your suggestions are really helpful. :0
import java.util.function.BiFunction;
public enum OpCode {
JMP((s1, s2) -> "s1 + s2 = " + s1 + s2, Standard.ICWS88, Standard.ICW94), // Both standards
ETC((s1, s2) -> "this is the return value", Standard.ICWS88); // Just one
private BiFunction<String, String, String> execution;
private Standard[] standards;
OpCode(BiFunction<String, String, String> execution, Standard... standards) {
this.execution = execution;
this.standards = standards;
for (Standard standard : standards) {
standard.register(this);
}
}
public Standard[] getStandards() {
return this.standards;
}
public String execute(String s1, String s2) {
return this.execution.apply(s1, s2);
}
}
import java.util.HashSet;
import java.util.Set;
public enum Standard {
ICWS88, ICW94;
private final Set<OpCode> opCodes = new HashSet<>();
public void register(OpCode opCode) {
this.opCodes.add(opCode);
}
public Set<OpCode> getOpCodes() {
return this.opCodes;
}
}
I used a bifunction because I noticed your execute takes in two params and provides a return value. You can make the types whatever you want.
I-
Don't understand anything... 🥹
Probably too hard for what I am doing...
Especially considering the fact that I have only 12 hours to finish it. 🥹
You've got an enum for your two standards.
Each contains all of the valid OpCodes for that standard.
You've also got an enum containing all available OpCodes.
Each OpCode contains references to the standards it's supported by.
Each OpCode also has a function determining its execute behavior.
So you can do something like this:
OpCode.JMP.execute(s1,s2);
Or you can do something like this:
for (OpCode opCode : Standard.ICS88.getOpCodes()) {
System.out.println("ICS88 supports OpCode: " + opCode.name());
}
Honestly, I can't think of a better way to represent this relationship.
Don't get too caught up on the bifunction. It's not as scary as it looks.
JMP((s1, s2) -> "s1 + s2 = " + s1 + s2, Standard.ICWS88, Standard.ICW94)
I don't know how the both standards are differenciated, because sometimes in 88 and 94 opcodes can work differently
Change the BiFunction to a TriFunction and pass in your Standard as a param, then, if you need to, you can provide different behavior depending on the standard.
I can update the examples if you'd like.
🙂🔫
Sigh
Is this simpler than using a Strategy Pattern?
Good to know that Bifunction and Trifunction exist tho but...it's frightening considering the time that left, and I will need hours after too
public enum OpCode {
JMP((s1, s2, standard) -> {
if (standard == Standard.ICW94) {
return s1;
} else {
return s2;
}
}, Standard.ICWS88, Standard.ICW94), // Both standards
ETC((s1, s2, standard) -> "this is the return value", Standard.ICWS88); // Just one
private TriFunction<String, String, Standard, String> execution;
private Standard[] standards;
OpCode(TriFunction<String, String, Standard, String> execution, Standard... standards) {
this.execution = execution;
this.standards = standards;
for (Standard standard : standards) {
standard.register(this);
}
}
public Standard[] getStandards() {
return this.standards;
}
public String execute(String s1, String s2, Standard standard) {
return this.execution.apply(s1, s2, standard);
}
}
Technically a TriFunction doesn't exist, you'll have to make it yourself, but it's super easy. Just copy paste this:
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
Edit: I removed the stuff you don't need. It's less scary now.
🥹
I'm happy about the fact that I learnt something new and useful. 🥹
I'm sad about the fact that if I try that I will probably struggle a lot and don't finish in time so...
I will try a Strategy Pattern, and if this does not work I will try the trifunction
I have high confidence in you. 
I don't know what to answer between "thanks" and "you shouldn't". 😭 😂
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
He reacts to the word "t.hanks"...
to be pedantic, this is not future-proof to adding new elements
"thanks but you shouldn't" tends to get the point across pretty well 👍
Haha, you're right!
And you, what do you think? Do you think a Strategy Pattern can be used or should I really use the trifunction?
no clue what a strategy pattern is, sorry. is it referring to the enum elements with overridden methods?
I am talking about this kind of thing... :0
do you have a fixed number of operations?
For each standard yes, but it's possible to add new standards
does each standard have the same operations, and do the operations behave identically in each standard
No :/
I mean, mostly, but for example the 94 added modifier, which affect the execution of the operations
no to the latter or no to both?
No for both
if each standard has different operations then each standard would need a seperate operation handler
Here is my idea, I think this can work! :D
Depending on the standard the return value will be different.
There's a method 88 and 94 with different algorithm behind.
Non-valid opcode (for example 94 opcode in 88) will throw an error. Basic usage for strategy.
seems like a lot of repeated code
It looks repeated but it isn't.
Every Opcode execute a different operation here.
well no, defining the methods is still repeated code
you could have them be lambdas, or with a different setup, even method references
🤔
It's 7 AM here and it's working... ;-;
Thanks for your help. ;-;
I still have work to do but I think the hardest part is behind now...
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived.
If your question was not answered yet, feel free to re-open this post or create a new one.
Kinda off subject now, but what’s not future proof about it?
If you add a new element without providing a case for it in foo() you’ll get a compiler error specifically instructing you to add a case for it.
This is the most common way I’ve seen people provide different behavior depending on the enum. Seems solid to me. Although I do prefer inlined lambdas passed into the constructor like my most recent example.
Ayyy, good work. Did you end up sticking with your strategy pattern?
ah coming back to it i had a fault in my thought process
wait hold on
well there was a fault but the point might still stand, give me a sec to test
i was thinking of the "guaranteed return" thing with enum switches which require a default branch if there's unconditional return later
which would catch newly-defined members without requiring a case for the member
Yeah, I intentionally didn’t provide a ‘default’ in my example in order to avoid that. Doing so would definitely be dangerous.
you can have missing members in the switch because the switch isn't required to provide a return value or anything of that sort
it's unsafe in the same way - unimplemented new member - but for a different reason than i was thinking
#bot-cmds message
oh come on javabot
I suppose you could easily make a case that it isn’t future proof if it’s as fragile as no longer being future proof the moment you have a return outside of the switch or provide a default within the switch, because someone new to a codebase might not understand the consequences of doing so.
And that’s a good point about it not being future proof at all if there’s no return value, which is actually the case in my example. Nice catch.
The Strategy Pattern works fine! :D
Thanks for your help again! :D
I think I will close the post, now. :0
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.
If you are finished with your post, please close it.
If you are not, please ignore this message.
Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts.