How to restrict enums in Kotlin? - kotlin

I have an enum with many values; error codes for example, or some official list of coded values. In my application, I have several functions where only a subset of those values is admissible. How can I derive restricted enums that contain only a subset of the original enum?
For example, I have an externally provided dictionary of error codes that model as enum:
enum class ApiError(val: errorCode: Int) {
INCORRECT_CHARACTER(1),
MISSING_VALUE(2),
TOO_SMALL(3),
TOO_LARGE(4)
}
In one function call, only the TOO_SMALL and TOO_LARGE errors may result, in another only INCORRECT_CHARACTER or MISSING_VALUE. Instead of defining two new enums for these particular error return values, I would like both to somehow reference the complete enum with all error codes.
To be more precise: Assume I have a function fun handleError(error: ApiError); inside this function, I want to be able to write an exhaustive when pattern match that covers all enum cases. However, I also want to be able to pass an argument of a restricted enum type to that same function, where that restricted type can take on only a subset of the enum values, as in the example above.
What comes to mind (but does not work in Kotlin) would be to subclass the ApiError enum while restricting the admissible values in each subclass. Is there a Kotlin solution that does something similar?
The opposite question – to subclass an enum for extension – has been discussed here at length. As far as I understand, the objections there do not apply when restricting the potential enum values.
And just for curiosity: I suppose the above question is some concrete and utterly misspecified version of a some type theoretical problem. Can someone provide pointers to the proper theory and terminology?

What comes to mind (but does not work in Kotlin) would be to subclass the APIError enum while restricting the admissible values in each subclass. Is there a Kotlin solution that does something similar?
Yes, if you need to express a hierarchy, you could use sealed class/interface hierarchies with objects as leaves.
sealed class ApiError(val code: Int) {
object IncorrectCharacter : ApiError(1)
object MissingValue : ApiError(2)
}
sealed class SizeError(code: Int): ApiError(code) {
object TooSmall : SizeError(3)
object TooLarge : SizeError(4)
}
What you lose here compared to enums is the ability to list all possible values using ApiError.values(). But in this case it might not be an issue.
Also it might not be ideal to serialize (and even more so, deserialize), depending on which serialization library you're using.

Related

When is a class a data class?

I know what classes are about, but for better understanding I need a use case. Recently I discovered the construct of data classes. I get the idea behind normal classes, but I cannot imagine a real use case for data classes.
When should I use a data class and when I use a "normal" class? For all I know, all classes keep data.
Can you provide a good example that distinguishes data classes from non-data classes?
A data class is used to store data. It's lighter than a normal class, and can be compared to an array with key/value (dictionary, hash, etc.), but represented as an object with fixed attributes. In kotlin, according to the documentation, that adds those attributes to the class:
equals()/hashCode() pair
toString() of the form "User(name=John, age=42)"
componentN() functions corresponding to the properties in their order of declaration.
copy() function
Also it has a different behavior during class inheritence :
If there are explicit implementations of equals(), hashCode(), or toString() in the data class body or final implementations in a
superclass, then these functions are not generated, and the existing
implementations are used.
If a supertype has componentN() functions that are open and return compatible types, the corresponding functions are generated for the
data class and override those of the supertype. If the functions of
the supertype cannot be overridden due to incompatible signatures or
due to their being final, an error is reported.
Providing explicit implementations for the componentN() and copy() functions is not allowed.
So in kotlin, if you want to describe an object (a data) then you may use a dataclass, but if you're creating a complex application and your class needs to have special behavior in the constructor, with inheritence or abstraction, then you should use a normal class.
I do not know Kotlin, but in Python, a dataclass can be seen as a structured dict. When you want to use a dict to store an object which has always the same attributes, then you should not put it in a dict but use a Dataclass.
The advantage with a normal class is that you don't need to declare the __init__ method, as it is "automatic" (inherited).
Example :
This is a normal class
class Apple:
def __init__(size:int, color:str, sweet:bool=True):
self.size = size
self.color = color
self.sweet = sweet
Same class as a dataclass
from dataclasses import dataclass
#dataclass
class Apple:
size: int
color: str
sweet: bool = True
Then the advantage compared to a dict is that you are sure of what attribute it has. Also it can contains methods.
The advantage over to a normal class is that it is simpler to declare and make the code lighter. We can see that the attributes keywords (e.g size) are repeated 3 times in a normal class, but appear only once in a dataclass.
The advantage of normal class also is that you can personalize the __init__ method, (in a dataclass also, but then you lose it's main advantage I think) example:
# You need only 2 variable to initialize your class
class Apple:
def __init__(size:int, color:str):
self.size = size
self.color = color
# But you get much more info from those two.
self.sweet = True if color == 'red' else False
self.weight = self.__compute_weight()
self.price = self.weight * PRICE_PER_GRAM
def __compute_weight(self):
# ...
return (self.size**2)*10 # That's a random example
Abstractly, a data class is a pure, inert information record that doesn’t require any special handling when copied or passed around, and it represents nothing more than what is contained in its fields; it has no identity of its own. A typical example is a point in 3D space:
data class Point3D(
val x: Double,
val y: Double,
val z: Double
)
As long as the values are valid, an instance of a data class is entirely interchangeable with its fields, and it can be put apart or rematerialized at will. Often there is even little use for encapsulation: users of the data class can just access the instance’s fields directly. The Kotlin language provides a number of convenience features when data classes are declared as such in your code, which are described in the documentation. Those are useful when for example building more complex data structures employing data classes: you can for example have a hashmap assign values to particular points in space, and then be able to look up the value using a newly-constructed Point3D.
val map = HashMap<Point3D, String>()
map.set(Point3D(3, 4, 5), "point of interest")
println(map.get(Point3D(3, 4, 5))) // prints "point of interest"
For an example of a class that is not a data class, take FileReader. Underneath, this class probably keeps some kind of file handle in a private field, which you can assume to be an integer (as it actually is on at least some platforms). But you cannot expect to store this integer in a database, have another process read that same integer from the database, reconstruct a FileReader from it and expect it to work. Passing file handles between processes requires more ceremony than that, if it is even possible on a given platform. That property makes FileReader not a data class. Many examples of non-data classes will be of this kind: any class whose instances represent transient, local resources like a network connection, a position within a file or a running process, cannot be a data class. Likewise, any class where different instances should not be considered equal even if they contain the same information is not a data class either.
From the comments, it sounds like your question is really about why non-data classes exist in Kotlin and why you would ever choose not to make a data class. Here are some reasons.
Data classes are a lot more restrictive than a regular class:
They have to have a primary constructor, and every parameter of the primary constructor has to be a property.
They cannot have an empty primary constructor.
They cannot be open so they cannot be subclassed.
Here are other reasons:
Sometimes you don't want a class to have a copy function. If a class holds onto some heavy state that is expensive to copy, maybe it shouldn't advertise that it should be copied by presenting a copy function.
Sometimes you want to use an instance of a class in a Set or as Map keys without two different instances being considered as equivalent just because their properties have the same values.
The features of data classes are useful specifically for simple data holders, so the drawbacks are often something you want to avoid.

What are sealed classes in Kotlin?

I'm a beginner in Kotlin and recently read about Sealed Classes. But from the doc the only think I actually get is that they are exist.
The doc stated, that they are "representing restricted class hierarchies". Besides that I found a statement that they are enums with superpower. Both aspects are actually not clear.
So can you help me with the following questions:
What are sealed classes and what is the idiomatic way of using ones?
Does such a concept present in other languages like Python, Groovy or C#?
UPDATE:
I carefully checked this blog post and still can't wrap my head around that concept. As stated in the post
Benefit
The feature allows us to define class hierarchies that are
restricted in their types, i.e. subclasses. Since all subclasses need
to be defined inside the file of the sealed class, there’s no chance
of unknown subclasses which the compiler doesn’t know about.
Why the compiler doesn't know about other subclasses defined in other files? Even IDE knows that. Just press Ctrl+Alt+B in IDEA on, for instance, List<> definition and all implementations will be shown even in other source files. If a subclass can be defined in some third-party framework, which not used in the application, why should we care about that?
Say you have a domain (your pets) where you know there is a definite enumeration (count) of types. For example, you have two and only two pets (which you will model with a class called MyPet). Meowsi is your cat and Fido is your dog.
Compare the following two implementations of that contrived example:
sealed class MyPet
class Meowsi : MyPet()
class Fido : MyPet()
Because you have used sealed classes, when you need to perform an action depending on the type of pet, then the possibilities of MyPet are exhausted in two and you can ascertain that the MyPet instance will be exactly one of the two options:
fun feed(myPet: MyPet): String {
return when(myPet) {
is Meowsi -> "Giving cat food to Meowsi!"
is Fido -> "Giving dog biscuit to Fido!"
}
}
If you don't use sealed classes, the possibilities are not exhausted in two and you need to include an else statement:
open class MyPet
class Meowsi : MyPet()
class Fido : MyPet()
fun feed(myPet: MyPet): String {
return when(myPet) {
is Mewosi -> "Giving cat food to Meowsi!"
is Fido -> "Giving dog biscuit to Fido!"
else -> "Giving food to someone else!" //else statement required or compiler error here
}
}
In other words, without sealed classes there is not exhaustion (complete coverage) of possibility.
Note that you could achieve exhaustion of possiblity with Java enum however these are not fully-fledged classes. For example, enum cannot be subclasses of another class, only implement an interface (thanks EpicPandaForce).
What is the use case for complete exhaustion of possibilities? To give an analogy, imagine you are on a tight budget and your feed is very precious and you want to ensure you don't end up feeding extra pets that are not part of your household.
Without the sealed class, someone else in your home/application could define a new MyPet:
class TweetiePie : MyPet() //a bird
And this unwanted pet would be fed by your feed method as it is included in the else statement:
else -> "Giving food to someone else!" //feeds any other subclass of MyPet including TweetiePie!
Likewise, in your program exhaustion of possibility is desirable because it reduces the number of states your application can be in and reduces the possibility of bugs occurring where you have a possible state where behaviour is poorly defined.
Hence the need for sealed classes.
Mandatory else
Note that you only get the mandatory else statement if when is used as an expression. As per the docs:
If [when] is used as an expression, the value of the satisfied branch becomes the value of the overall expression [... and] the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions
This means you won't get the benefit of sealed classes for something like this):
fun feed(myPet: MyPet): Unit {
when(myPet) {
is Meowsi -> println("Giving cat food to Meowsi!") // not an expression so we can forget about Fido
}
}
To get exhaustion for this scenario, you would need to turn the statement into an expression with return type.
Some have suggested an extension function like this would help:
val <T> T.exhaustive: T
get() = this
Then you can do:
fun feed(myPet: MyPet): Unit {
when(myPet) {
is Meowsi -> println("Giving cat food to Meowsi!")
}.exhaustive // compiler error because we forgot about Fido
}
Others have suggested that an extension function pollutes the namespace and other workarounds (like compiler plugins) are required.
See here for more about this problem.
Sealed classes are easier to understand when you understand the kinds of problems they aim to solve. First I'll explain the problems, then I'll introduce the class hierarchies and the restricted class hierarchies step by step.
We'll take a simple example of an online delivery service where we use three possible states Preparing, Dispatched and Delivered to display the current status of an online order.
Problems
Tagged class
Here we use a single class for all the states. Enums are used as type markers. They are used for tagging the states Preparing, Dispatched and Delivered :
class DeliveryStatus(
val type: Type,
val trackingId: String? = null,
val receiversName: String? = null) {
enum class Type { PREPARING, DISPATCHED, DELIVERED }
}
The following function checks the state of the currently passed object with the help of enums and displays the respective status:
fun displayStatus(state: DeliveryStatus) = when (state.type) {
PREPARING -> print("Preparing for dispatch")
DISPATCHED -> print("Dispatched. Tracking ID: ${state.trackingId ?: "unavailable"}")
DELIVERED -> print("Delivered. Receiver's name: ${state.receiversName ?: "unavailable"}")
}
As you can see, we are able to display the different states properly. We also get to use exhaustive when expression, thanks to enums. But there are various problems with this pattern:
Multiple responsibilities
The class DeliveryStatus has multiple responsibilities of representing different states. So it can grow bigger, if we add more functions and properties for different states.
More properties than needed
An object has more properties than it actually needs in a particular state. For example, in the function above, we don't need any property for representing the Preparing state. The trackingId property is used only for the Dispatched state and the receiversName property is concerned only with the Delivered state. The same is true for functions. I haven't shown functions associated with states to keep the example small.
No guarantee of consistency
Since these unused properties can be set from unrelated states, it's hard to guarantee the consistency of a particular state. For example, one can set the receiversName property on the Preparing state. In that case, the Preparing will be an illegal state, because we can't have a receiver's name for the shipment that hasn't been delivered yet.
Need to handle null values
Since not all properties are used for all states, we have to keep the properties nullable. This means we also need to check for the nullability. In the displayStatus() function we check the nullability using the ?:(elvis) operator and show unavailable, if that property is null. This complicates our code and reduces readability. Also, due to the possibility of a nullable value, the guarantee for consistency is reduced further, because the null value of receiversName in Delivered is an illegal state.
Introducing Class Hierarchies
Unrestricted class hierarchy: abstract class
Instead of managing all the states in a single class, we separate the states in different classes. We create a class hierarchy from an abstract class so that we can use polymorphism in our displayStatus() function:
abstract class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val receiversName: String) : DeliveryStatus()
The trackingId is now only associated with the Dispatched state and receiversName is only associated with the Delivered state. This solves the problems of multiple responsibilities, unused properties, lack of state consistency and null values.
Our displayStatus() function now looks like the following:
fun displayStatus(state: DeliveryStatus) = when (state) {
is Preparing -> print("Preparing for dispatch")
is Dispatched -> print("Dispatched. Tracking ID: ${state.trackingId}")
is Delivered -> print("Delivered. Received by ${state.receiversName}")
else -> throw IllegalStateException("Unexpected state passed to the function.")
}
Since we got rid of null values, we can be sure that our properties will always have some values. So now we don't need to check for null values using the ?:(elvis) operator. This improves code readability.
So we solved all the problems mentioned in the tagged class section by introducing a class hierarchy. But the unrestricted class hierarchies have the following shortcomings:
Unrestricted Polymorphism
By unrestricted polymorphism I mean that our function displayStatus() can be passed a value of unlimited number of subclasses of the DeliveryStatus. This means we have to take care of the unexpected states in displayStatus(). For this, we throw an exception.
Need for the else branch
Due to unrestricted polymorphism, we need an else branch to decide what to do when an unexpected state is passed. If we use some default state instead of throwing an exception and then forget to take care of any newly added subclass, then that default state will be displayed instead of the state of the newly created subclass.
No exhaustive when expression
Since the subclasses of an abstract class can exist in different packages and compilation units, the compiler doesn't know all the possible subclasses of the abstract class. So it won't flag an error at compile time, if we forget to take care of any newly created subclasses in the when expression. In that case, only throwing an exception can help us. Unfortunately, we'll know about the newly created state only after the program crashes at runtime.
Sealed Classes to the Rescue
Restricted class hierarchy: sealed class
Using the sealed modifier on a class does two things:
It makes that class an abstract class. Since Kotlin 1.5, you can use a sealed interface too.
It makes it impossible to extend that class outside of that file. Since Kotlin 1.5 the same file restriction has been removed. Now the class can be extended in other files too but they need to be in the same compilation unit and in the same package as the sealed type.
sealed class DeliveryStatus
object Preparing : DeliveryStatus()
class Dispatched(val trackingId: String) : DeliveryStatus()
class Delivered(val receiversName: String) : DeliveryStatus()
Our displayStatus() function now looks cleaner:
fun displayStatus(state: DeliveryStatus) = when (state) {
is Preparing -> print("Preparing for Dispatch")
is Dispatched -> print("Dispatched. Tracking ID: ${state.trackingId}")
is Delivered -> print("Delivered. Received by ${state.receiversName}")
}
Sealed classes offer the following advantages:
Restricted Polymorphism
By passing an object of a sealed class to a function, you are also sealing that function, in a sense. For example, now our displayStatus() function is sealed to the limited forms of the state object, that is, it will either take Preparing, Dispatched or Delivered. Earlier it was able to take any subclass of DeliveryStatus. The sealed modifier has put a limit on polymorphism. As a result, we don't need to throw an exception from the displayStatus() function.
No need for the else branch
Due to restricted polymorphism, we don't need to worry about other possible subclasses of DeliveryStatus and throw an exception when our function receives an unexpected type. As a result, we don't need an else branch in the when expression.
Exhaustive when expression
Just like all the possible values of an enum class are contained inside the same class, all the possible subtypes of a sealed class are contained inside the same package and the same compilation unit. So, the compiler knows all the possible subclasses of this sealed class. This helps the compiler to make sure that we have covered(exhausted) all the possible subtypes in the when expression. And when we add a new subclass and forget to cover it in the when expression, it flags an error at compile time.
Note that in the latest Kotlin versions, your when is exhaustive for the when expressions as well the when statements.
Why in the same file?
The same file restriction has been removed since Kotlin 1.5. Now you can define the subclasses of the sealed class in different files but the files need to be in the same package and the same compilation unit. Before 1.5, the reason that all the subclasses of a sealed class needed to be in the same file was that it had to be compiled together with all of its subclasses for it to have a closed set of types. If the subclasses were allowed in other files, the build tools like Gradle would have to keep track of the relations of files and this would affect the performance of incremental compilation.
IDE feature: Add remaining branches
When you just type when (status) { } and press Alt + Enter, Enter, the IDE automatically generates all the possible branches for you like the following:
when (state) {
is Preparing -> TODO()
is Dispatched -> TODO()
is Delivered -> TODO()
}
In our small example there are just three branches but in a real project you could have hundreds of branches. So you save the effort of manually looking up which subclasses you have defined in different files and writing them in the when expression one by one in another file. Just use this IDE feature. Only the sealed modifier enables this.
That's it! Hope this helps you understand the essence of sealed classes.
If you've ever used an enum with an abstract method just so that you could do something like this:
public enum ResultTypes implements ResultServiceHolder {
RESULT_TYPE_ONE {
#Override
public ResultOneService getService() {
return serviceInitializer.getResultOneService();
}
},
RESULT_TYPE_TWO {
#Override
public ResultTwoService getService() {
return serviceInitializer.getResultTwoService();
}
},
RESULT_TYPE_THREE {
#Override
public ResultThreeService getService() {
return serviceInitializer.getResultThreeService();
}
};
When in reality what you wanted is this:
val service = when(resultType) {
RESULT_TYPE_ONE -> resultOneService,
RESULT_TYPE_TWO -> resultTwoService,
RESULT_TYPE_THREE -> resultThreeService
}
And you only made it an enum abstract method to receive compile time guarantee that you always handle this assignment in case a new enum type is added; then you'll love sealed classes because sealed classes used in assignments like that when statement receive a "when should be exhaustive" compilation error which forces you to handle all cases instead of accidentally only some of them.
So now you cannot end up with something like:
switch(...) {
case ...:
...
default:
throw new IllegalArgumentException("Unknown type: " + enum.name());
}
Also, enums cannot extend classes, only interfaces; while sealed classes can inherit fields from a base class. You can also create multiple instances of them (and you can technically use object if you need the subclass of the sealed class to be a singleton).

How to put class dynamically in <>

I know there are various capabilities in Java with reflection.
For example:
Class<?> clazz = Class.forName("java.util.Date");
Object ins = clazz.newInstance();
I wonder if I could pass class dynamicaly in some method declaration in <> tags (or there is other way to do it if it must be fixed). I would like to change that class declaration dynamicaly; because I would like to write generic method for all types of classes.
In there, I have this:
List<Country>
Can I write it something diffrent with reflection? For example can it be somehow be achieved to pass class as parameter (or how else should be this done):
List<ins>
? I would appreciate examples.
This cannot be done because generics are a compile time feature. Once code is compiled, the only place where generics are exists are at method signatures, and they are only used for compiling new code.
When working with reflection, you are basicly working with raw types, and need to code according to that, that means, you can cast the returned result of newInstance() to the list type your need, for example:
List<Country> ins = (List<Country>)clazz.newInstance();
This is a safe operation to do, because you know at that point its empty, and isn't passed to any outside code.
I don't think this is possible. Generics in Java are implemented in a way that prohibits runtime access.
Generics are there so that the compiler can verify correct typing, but are no longer present at runtime (this is called "type erasure"). Reflection deals with the runtime representation of types only. As far as I know the only case where reflection has to deal with generics is to find out "fixed" type parameters of sub-classes, e.g. when you have class Bar<T> and class Foo extends Bar<String>, you can find out that the T of Bar is fixed to String in Foo using reflection. However, this is information found in the class file, too. Except that, reflection can only see or create raw-types.

What is the difference between the concept of 'class' and 'type'?

i know this question has been already asked, but i didnt get it quite right, i would like to know, which is the base one, class or the type. I have few questions, please clear those for me,
Is type the base of a programing data type?
type is hard coded into the language itself. Class is something we can define ourselves?
What is untyped languages, please give some examples
type is not something that fall in to the oop concepts, I mean it is not restricted to oop world
Please clear this for me, thanks.
I didn't work with many languages. Maybe, my questions are correct in terms of : Java, C#, Objective-C
1/ I think type is actually data type in some way people talk about it.
2/ No. Both type and class we can define it. An object of Class A has type A. For example if we define String s = "123"; then s has a type String, belong to class String. But the vice versa is not correct.
For example:
class B {}
class A extends B {}
B b = new A();
then you can say b has type B and belong to both class A and B. But b doesn't have type A.
3/ untyped language is a language that allows you to change the type of the variable, like in javascript.
var s = "123"; // type string
s = 123; // then type integer
4/ I don't know much but I think it is not restricted to oop. It can be procedural programming as well
It may well depend on the language. I treat types and classes as the same thing in OO, only making a distinction between class (the definition of a family of objects) and instance (or object), specific concrete occurrences of a class.
I come originally from a C world where there was no real difference between language-defined types like int and types that you made yourself with typedef or struct.
Likewise, in C++, there's little difference (probably none) between std::string and any class you put together yourself, other than the fact that std::string will almost certainly be bug-free by now. The same isn't always necessary in our own code :-)
I've heard people suggest that types are classes without methods but I don't believe that distinction (again because of my C/C++ background).
There is a fundamental difference in some languages between integral (in the sense of integrated rather than integer) types and class types. Classes can be extended but int and float (examples for C++) cannot.
In OOP languages, a class specifies the definition of an object. In many cases, that object can serve as a type for things like parameter matching in a function.
So, for an example, when you define a function, you specify the type of data that should be passed to the function and the type of data that is returned:
int AddOne(int value) { return value+1; } uses int types for the return value and the parameter being passed in.
In languages that have both, the concepts of type and class/object can almost become interchangeable. However, there are many languages that do not have both. For instance, I believe that standard C has no support for custom-defined objects, but it certainly does still have types. On the otherhand, both PHP and Javascript are examples of languages where type is very loosely defined (basically, types are either single item, collection/array/object, or undefined [js only]), but they have full support for classes/objects.
Another key difference: you can have methods and custom-functions associated with a class/object, but not with a standard data-type.
Hopefully that clarified some. To answer your specific questions:
In some ways, type could be considered a base concept of programming, yes.
Yes, with the exception that classes can be treated as types in functions, as in the example above.
An untyped language is one that lets you use any type of variable interchangeably. Meaning that you can handle a string with the same code that handles an int, for instance. In practice most 'untyped' languages actually implement a concept called duck-typing, so named because they say that 'if it acts like a duck, it should be treated like a duck' and attempt to use any variable as the type that makes sense for the code encountered. Again, php and javascript are two languages which do this.
Very true, type is applicable outside of the OOP world.

Is there a commonly used OO Pattern for holding "constant variables"?

I am working on a little pinball-game project for a hobby and am looking for a pattern to encapsulate constant variables.
I have a model, within which there are values which will be constant over the life of that model e.g. maximum speed/maximum gravity etc. Throughout the GUI and other areas these values are required in order to correctly validate input. Currently they are included either as references to a public static final, or just plain hard-coded. I'd like to encapsulate these "constant variables" in an object which can be injected into the model, and retrieved by the view/controller.
To clarify, the value of the "constant variables" may not necessarily be defined at compile-time, they could come from reading in a file; user input etc. What is known at compile time is which ones are needed. A way which may be easier to explain it is that whatever this encapsulation is, the values it provides are immutable.
I'm looking for a way to achieve this which:
has compile time type-safety (i.e. not mapping a string to variable at runtime)
avoids anything static (including enums, which can't be extended)
I know I could define an interface which has the methods such as:
public int getMaximumSpeed();
public int getMaximumGravity();
... and inject an instance of that into the model, and make it accessible in some way. However, this results in a lot of boilerplate code, which is pretty tedious to write/test etc (I am doing this for funsies :-)).
I am looking for a better way to do this, preferably something which has the benefits of being part of a shared vocabulary, as with design patterns.
Is there a better way to do this?
P.S. I've thought some more about this, and the best trade-off I could find would be to have something like:
public class Variables {
enum Variable {
MaxSpeed(100),
MaxGravity(10)
Variable(Object variableValue) {
// assign value to field, provide getter etc.
}
}
public Object getVariable(Variable v) { // look up enum and get member }
} // end of MyVariables
I could then do something like:
Model m = new Model(new Variables());
Advantages: the lookup of a variable is protected by having to be a member of the enum in order to compile, variables can be added with little extra code
Disadvantages: enums cannot be extended, brittleness (a recompile is needed to add a variable), variable values would have to be cast from Object (to Integer in this example), which again isn't type safe, though generics may be an option for that... somehow
Are you looking for the Singleton or, a variant, the Monostate? If not, how does that pattern fail your needs?
Of course, here's the mandatory disclaimer that Anything Global Is Evil.
UPDATE: I did some looking, because I've been having similar debates/issues. I stumbled across a list of "alternatives" to classic global/scope solutions. Thought I'd share.
Thanks for all the time spent by you guys trying to decipher what is a pretty weird question.
I think, in terms of design patterns, the closest that comes to what I'm describing is the factory pattern, where I have a factory of pseudo-constants. Technically it's not creating an instance each call, but rather always providing the same instance (in the sense of a Guice provider). But I can create several factories, which each can provide different psuedo-constants, and inject each into a different model, so the model's UI can validate input a lot more flexibly.
If anyone's interested I've came to the conclusion that an interface providing a method for each psuedo-constant is the way to go:
public interface IVariableProvider {
public int maxGravity();
public int maxSpeed();
// and everything else...
}
public class VariableProvider {
private final int maxGravity, maxSpeed...;
public VariableProvider(int maxGravity, int maxSpeed) {
// assign final fields
}
}
Then I can do:
Model firstModel = new Model(new VariableProvider(2, 10));
Model secondModel = new Model(new VariableProvider(10, 100));
I think as long as the interface doesn't provide a prohibitively large number of variable getters, it wins over some parameterised lookup (which will either be vulnerable at run-time, or will prohibit extension/polymorphism).
P.S. I realise some have been questioning what my problem is with static final values. I made the statement (with tongue in cheek) to a colleague that anything static is an inherently not object-oriented. So in my hobby I used that as the basis for a thought exercise where I try to remove anything static from the project (next I'll be trying to remove all 'if' statements ;-D). If I was on a deadline and I was satisfied public static final values wouldn't hamstring testing, I would have used them pretty quickly.
If you're just using java/IOC, why not just dependency-inject the values?
e.g. Spring inject the values via a map, specify the object as a singleton -
<property name="values">
<map>
<entry> <key><value>a1</value></key><value>b1</value></entry>
<entry> <key><value>a2</value></key><value>b3</value></entry>
</map>
</property>
your class is a singleton that holds an immutable copy of the map set in spring -
private Map<String, String> m;
public String getValue(String s)
{
return m.containsKey(s)?m.get(s):null;
}
public void setValues(Map m)
{
this.m=Collections.unmodifiableMap(m):
}
From what I can tell, you probably don't need to implement a pattern here -- you just need access to a set of constants, and it seems to me that's handled pretty well through the use of a publicly accessible static interface to them. Unless I'm missing something. :)
If you simply want to "objectify" the constants though, for some reason, than the Singleton pattern would probably be called for, if any; I know you mentioned in a comment that you don't mind creating multiple instances of this wrapper object, but in response I'd ask, then why even introduce the sort of confusion that could arise from having multiple instances at all? What practical benefit are you looking for that'd be satisfied with having the data in object form?
Now, if the values aren't constants, then that's different -- in that case, you probably do want a Singleton or Monostate. But if they really are constants, just wrap a set of enums or static constants in a class and be done! Keep-it-simple is as good a "pattern" as any.