What I really need is to be able to declare regular variables in an interface and implement that interface in two classes that I would not have to have to re-declare these in each class (ie class.data.variables instead of class.variables). Is there any way that I could achieve the same goal differently?
To give more detail. Essentially, I have created a small drawing program that drops JLabels on a JPanel that is on a JScrollPane. Because I have a specific design for these JLabels (ie they are not just for drawing they represent airline objects for this application), I have a class that extends JLabel and adds my application specific variables to it. Ultimately, I read and write an XML file with these variables so they can load and save their designs. Since I can not use this extended class for my XML definitions because it screams about the parent class even though I told it to have NONE as the accessor (I read there is a bug), I have to create an identical class and copy values back and forth for saving and loading. Not too much of a problem except when I add a variable to the JLabel extended class and forget to add it to the XML mimic class and subsequent copy routines.
So, it would be great if I could make one class (say CellDataRecord.java) that held the extra data declarations and have that class be used in both places (the JLabel extension and the XML data) without having to have something like XML.data.CellDataRecordXXX.
You can do that with inheritance or using an interface, where the variable is set as a constant in the parent class. Since you are extending a JLabel, you should implement the interface on both classes:
public interface MyInterface {
int someint = 9;
}
public class MyClass1 extends JLabel implements MyInterface {
//this class has access to `someint`
}
public class MyClass2 extends JLabel implements MyInterface {
// also has access to `someint`
}
Edit
Since you want to be able to change the same variable from different classes, you have to ensure you aren't changing copies and are changing the same variable, so you should use a volatile keyword on the variable to indicate to java that all threads should check the value before it updates it.
Now you'll need to have a separate class so that instances can be made from other classes to get the value. You have to use the static keyword to ensure that one copy is kept for all class instances.
public class MyVariableWrapper {
public static volatile int some_var = 9;
public void updateSomeVar(int newvar) {
some_var = newvar;
}
public int getSomeVar() { return some_var; }
}
Now the other two classes just do this:
public class MyClass1 extends JLabel {
MyVariableWrapper myVariableWrapper;
MyClass1() {
super();
myVariableWrapper = new MyVariableWrapper();
// now I have access to `some_var`
}
}
public class MyClass2 extends JLabel {
MyVariableWrapper myVariableWrapper;
MyClass2() {
super();
myVariableWrapper = new MyVariableWrapper();
// now I have access to the same `some_var` as MyClass1
}
// this is a wrapper method for your convenience
// since you don't like the excess code when accessing the variable
public int getSomeVar() {
return myVariableWrapper.some_var;
// or myVariableWrapper.getSomeVar();
}
public void setSomeVar(int newvar) {
myVariableWrapper.some_var = newvar;
// or myVariableWrapper.setSomeVar(newvar);
}
}
Now you can do this:
MyClass2 myClass2 = new MyClass2();
System.out.println(""+myClass2.getSomeVar());
I'm not sure I 100% grasp your problem but from the first few lines of your description, instead of implementing an interface, you could define an abstract class and have your classes extend it. That way, you'll be able to define attributes in the abstract class and these will be common to all subclasses.
Related
I design my game application and face some troubles in OOP design.
I want to know some patterns which can help me, because java have not any multiple extends option. I will describe my problem below, and also explain why multiple interface doesn't help me at all. Lets go.
What we want is "class is set of features". By feature I mean construction like:
field a;
field b;
field c;
method m1(){
// use, and change fields a,b,c;
}
method m2(){
// use, and change fields a,b,c;
}
//etc
So, basically the feature is a set of methods and corresponding fields. So, it's very close to the java interface.
When I talk that class implemets "feature1" I mean that this class contains ALL "feature needed" fields, and have realisation of all feature related methods.
When class implements two features the tricky part begins. There is a change, that two different features contains similar fields (names of this fields are equal). Let the case of different types for such fields will be out of scope. What I want - is "feature naming tolerance" - so that if methodA() from feature A change the field "common_field", the methodB from feature B, that also use "common_field" as field will see this changes.
So, I want to create a set of features (basically interfaces) and their implementations. After this I want to create classes which will extends multiple features, without any copy-paste and other crap.
But I can't write this code in Java:
public static interface Feature1 {
public void method1();
}
public static interface Feature2 {
public void method2();
}
public static class Feature1Impl implements Feature1 {
int feature1Field;
int commonField;
#Override
public void method1() {
feature1Field += commonField;
commonField++;
}
}
public static class Feature2Impl implements Feature2 {
int feature2Field;
int commonField;
#Override
public void method2() {
commonField++;
}
}
public static class MyFeaturedClass extends Feature1Impl, Feature2Impl implements Feature1, Features2 {
}
So, as you can see the problem are really complex.
Below I'll describe why some standart approaches doesn't work here.
1) Use something like this:
public static class MyFeaturesClass implements Feature1,Feature2{
Feature1 feature1;
Feature2 feature2;
#Override
public void method2() {
feature2.method2();
}
#Override
public void method1() {
feature1.method1();
}
}
Ok, this is really nice approach - but it does not provide "feature field name tolerance" - so the call of method2 will not change the field "commonField" in object corresponding the feature1.
2) Use another design. For what sake you need such approach?
Ok. In my game there is a "unit" concept. A unit is MOVABLE and ALIVE object.
Movable objects has position, and move() method. Alive objects has hp and takeDamage() and die() methods.
There is only MOVABLE objects in my game, but this objects isn't alive.
Also, there is ALIVE objects in my game, but this objects isn't movable (buildings for example).
And when I realize the movable and alive as classes, that implements interfaces, I really don't know from what I should extends my Unit class. In both cases I will use copy-paste for this.
The example above is really simple, actually I need a lot of different features for different game mechanics. And I will have a lot of different objects with different properties.
What I actually tried is:
Map<Field,Object> fields;
So any object in my game has such Map, and to any object can be applied any method. The realization of method is just take needed fields from this map, do its job and change some of them. The problem of this approach is performance. First of all - I don't want to use Double and Interger classes for double and int fields, and second - I want to have a direct accsess to the fields of my objects (not through the map object).
Any suggestions?
PS. What I want as a result:
class A implements Feature1, Feature2, Feature3, Feature4, Feature5 {
// all features has corresponding FeatureNImpl implementations;
// features 1-2-3 has "shared" fields, feature 3-4 has, features 5-1 has.
// really fast implementation with "shared field tolerance" needed.
}
One possibility is to add another layer of interfaces. XXXProviderInterface could be defined for all possible common fields, that define a getter and setter for them.
A feature implementation class would require the needed providers in the constructor. All access to common fields are done through these references.
A concrete game object class implementation would implement the needed provider interfaces and feature interfaces. Through aggregation, it would add the feature implementations (with passing this as provider), and delegate the feature calls to them.
E.g.
public interface Feature1 {
void methodF1();
}
public interface Feature2 {
void methodF2();
}
public interface FieldAProvider {
int getA();
void setA(int a);
}
public class Feature1Impl implements Feature1 {
private FieldAProvider _a;
Feature1Impl(FieldAProvider a) {
_a = a;
}
void methodF1() {
_a.setA(_a.getA() * 2);
}
}
// Similar for Feature2Impl
public class GameObject implements Feature1, Feature2, FieldAProvider
{
int _fieldA;
Feature1 _f1;
Feature2 _f2;
GameObject() {
_f1 = new Feature1Impl(this);
_f2 = new Feature2Impl(this);
}
int getA() {
return _fieldA;
}
void setA(int a) {
_fieldA = a;
}
void methodF1() {
_f1.methodF1();
}
void methodF2() {
_f2.methodF2();
}
}
However, I don't think this is an optimal solution
To explain what I mean by this question I will use code examples below. Imagine you have this function.
private void fadeButton(JButton b, int timeToFade) {
//Fade code goes here
}
How would you implement this as a function which could be run like
JButton b = new JButton("Press Me");
b.fadeButton(20000);
Where fadeButton now looks like
private void fadeButton(int timeToFade) {
//Fade code goes here
}
Because the function is declared on the button itself.
Typically you create a derived class:
public JFadableButton extends JButton
This will contain the method private void fadeButton(int timeToFade).
Short answer is: you don't.
Longer answer:
You can't do that in Java directly (adding methods to a class outside of the source code of that class). That might be different in other languages, like Kotlin offers "something" like that.
In java, you have to make detours, for example by turning to the decorator pattern.
And just for the record: I didn't mention the simple "you can extend that class" because I read your question as "how do I add methods to JButton directly". But of course, creating your own class that extends JButton allows you to add methods; but of course, they only exist on objects of your derived class.
You could extend JButton with a new class, thus inheriting JButton's methods and adding the ability to add your own code:
public class FadingButton extends JButton {
//Constructors go here
private void fadeButton(int timeToFade) {
//Fade code goes here
}
}
You could also decorate the JButton with another class:
public class JButtonDecorator {
private JButton btn;
//Constructor here
private void fadeButton(int timeToFade) {
//Fade code goes here, hiding the held button
}
//getter and setter method for button
}
Or, if you want lots of different ways to affect your UI, you can make a utility class, similar to above:
//You could use a factory pattern to make this a singleton instead of having static methods
public abstract class UIUtils {
private UIUtils{} //Don't instantiate this class
public static void fadeComponent(JComponent toFade) {
//Fade code goes here
}
//Other static utility methods
}
Edit: Making use of these patterns. The extended class is self-explanatory and an example of simple inheritance, so it's just a matter of JButton btn = new FadingButton(); for example. Here are the others:
To use the decorator, instantiate it at the same scope as the button you're using now. For example:
JButton myButton = new JButton();
//Customize button and add to UI
JButtonDecorator jbDec = new JButtonDecorator(myButton);
jbDec.fadeButton(20000);
Although the button is a field of the decorator, it will otherwise behave normally in your UI. The decorator just wraps the class with useful methods such as the fadeButton method.
To use the utility class, there are two ways. One is two make an abstract class with static methods (as above), some consider it bad form but it's good for simple programs:
UIUtils.fadeComponent(myButton); //It's just that simple!
//The UIUtils class itself is never instantiated.
//All the methods are static, so no instances are needed.
Or if you want a more advanced method, make your utility class a singleton. This changes the utility class to this:
public class UIUtils {
UIUtils singleton;
private UIUtils{} //Don't instantiate this class publicly
public static UIUtils getInstance() {
if(singleton==null) //This is the first time the method is called
singleton = new UIUtils();
return singleton; //Return the one instance of UIUtils
}
public void fadeComponent(JComponent toFade) {
//Fade code goes here
}
//Other utility methods
}
Then you would declare your UIUtils object at class level to use across your UI:
UIUtils uiUtil = UIUtils.getInstance();
And somewhere in your code:
uiUtil.fadeComponent(myButton);
This pattern is more efficient with memory and is more object-oriented, but I don't personally find it very suitable for utility classes.
You can create a new class which extends JButton, and then add any method that could help you achieve what you want. But that's an exemple, there is many ways to achieve this.
Ps, don't set this method as private if you want to use it somewhere else than inside your class.
This is the simplest way I think think of. You have already got it. But just use this method:
private void fadeButton(int timeToFade) {
//Fade code goes here
}
This is assuming you already have the code for the fade, do you? I think this one is the one you should use. You don't need the button to be a parameter. When you want to call the method to fade the button, just put it in the ActionListener. So after you have the ActionListener for the button, do the following: btnName.fadeButton(timeToFade);
Here is how to code the ActionListener:
btnHome.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
}
public class AreaDeRectangulo extends JFrame {
public static final int WIDTH = 640;
public static final int HEIGHT = 480;
public JLabel AltoL, AnchoL, AreaL, PerimetroL;
public JTextField AltoTF, AnchoTF, AreaTF, PerimetroTF;
public JButton Calcular, Salir;
public BotonDeCalcular cbHandler;
public BotonDeSalir ebHandler;
}
public class BotonDeCalcular implements ActionListener {
public void actionPerformed(ActionEvent e) {
double alto, ancho, area;
ancho = Double.parseDouble(AnchoTF.getText());
alto = Double.parseDouble(AltoTF.getText());
area = ancho * alto;
AreaTF.setText(" " + area);
}
}
public static class BotonDeSalir implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
public static void main(String[] args) {
AreaDeRectangulo rectObj = new AreaDeRectangulo();
}
}
}
Why the classes "BotondeCalcular" and "BotondeSalir" have to be created inside the main class? are they like a mini-classes?
PD: it gives me an error if I try to separate the classes
They are called Nested Classes (some call them Inner Classes or other names)
As for why the author chose to do so, the java docs provide some suggestions.
Compelling reasons for using nested classes include the following:
It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such "helper classes" makes their package more streamlined.
It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A's members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.
These are design decisions that developers make. There is no hard reason why a class must be defined inside another class.
This is because they are inner classes and use the features available to inner classes. Let's look at BotonDeCalcular:
ancho = Double.parseDouble(AnchoTF.getText());
This uses a field called AnchoTF, declared in the outer class. In essence, that line is equivalent to the more verbose:
ancho = Double.parseDouble(AreaDeRectangulo.this.AnchoTF.getText());
If you want to move those classes to separate files, you'll need to hold an instance of AreaDeRectangulo that you pass in by constructor.
In the spirit of well designed OO, a certain class I am extending has marked one of its fields protected. This class has also generously provided a public setter, yet no getter.
I am extending this class with a base class that is in turn extended by several children. How can I restrict access to the protected variable from my children while still being able to manipulate it privately and set it publicly?
See example below:
public abstract class ThirdPartyClass {
protected Map propertyMap;
public void setPropertyMap(Map propertyMap){
this.propertyMap= propertyMap;
}
// Other methods that use propertyMap.
}
public abstract class MyBaseClass extends ThirdPartyClass{
// Accessor methods for entries in propertyMap.
public getFoo(){
propertyMap.get("Foo");
}
public getBar(){
propertyMap.get("Bar");
}
// etc...
}
public class OneOfManyChildren extends MyBaseClass {
// Should only access propertyMap via methods in MyBaseClass.
}
I have already found that I can revoke access by making the field private final in MyBaseClass. However that also hinders using the setter provided by the super class.
I am able to circumvent that limitation with the "cleverness" below yet it also results in maintaining two copies of the same map as well as an O(n) operation to copy over every element.
public abstract class MyBaseClass extends ThirdPartyClass{
private final Map propertyMap = new HashMap(); // Revokes access for children.
/** Sets parent & grandparent maps. */
#Override
public final void setPropertyMap(Map propertyMap){
super.setPropertyMap(propertyMap);
this.propertyMap.clear();
this.propertyMap.putAll(propertyMap);
}
}
Are there any better ways of accomplishing this?
Note: This is only one example of the real question: How to restrict access to protected fields without maintaining multiple copies?
Note: I also know that if the field were made private in the first place with a protected accessor, this would be a non-issue. Sadly I have no control over that.
Note: IS-A relatonship (inheritance) required.
Note: This could easily apply to any Collection, DTO, or complex object.
Metaphor for those misunderstanding the question:
This is akin to a grandparent having a cookie jar that they leave accessible to all family members and anyone else in their house (protected). A parent, with young children, enters the house and, for reasons of their own, wishes to prevent their children from digging into the cookie jar ad nauseam. Instead, the child should ask the parent for a chocolate chip cookie and see it magically appear; likewise for a sugar cookie or Oreo. They need never know that the cookies are all stored in the same jar or if there even is a jar (black box). This could be easily accomplished if the jar belonged to the parent, if the grandparent could be convinced to put away the cookies, or if the grandparents themselves did not need access. Short of creating and maintaining two identical jars, how can access be restricted for children yet unimpeded for the parent & grandparent?
This might not be possible for you, but if you could derive an interface from ThirdPartyClass and make ThirdPartyClass implement it ?
Then have MyBaseClass act as a decorator by implementing the interface by delegating to a private member ThirdPartyClassImpl.
I.e.
public interface ThirdParty ...
public class ThirdPartyClass implements ThirdParty
public class MyBaseClass implements ThirdParty {
private ThirdParty decorated = new ThirdPartyClass();
public class SubclassOne extends MyBaseClass....
etc
Ok, cheating mode on:
How about you overwrite de public setter and change the map implementation to a inner class of MyBaseClass. This implementation could throw a exception on all methods of map you dont want your children to access and your MyBaseClass could expose the methods they should use by using an internal method your map implementation...
Still has to solve how the ThirdPartyMethod will access those properties, but you could force your code to call a finalizationMethod on your MyBaseClass before use it... I'm just divagating here
EDIT
Like This:
public abstract class MyBaseClass extends ThirdPartyClass{
private class InnerMapImpl implements Map{
... Throw exception for all Map methods you dont want children to use
private Object internalGet(K key){
return delegate.get(key);
}
}
public void setPropertyMap(Map propertyMap){
this.propertyMap= new InnerMapImpl(propertyMap);
}
public Object getFoo(){
return ((InnerMapImpl) propertyMap).internalGet("Foo");
}
}
Sadly, there's nothing you can do. If this field is protected, it is either a conscious design decision (a bad one IMO), or a mistake. Either way, there's nothing you can do now about it, as you cannot reduce the accessibility of a field.
I have already found that I can revoke access by making the field private final in MyBaseClass.
This isn't exactly true. What you are doing is called variable hiding. Since you are using the same variable name in your subclass, references to the propertyMap variable now point to your private variable in MyBaseClass. However, you can get around this variable hiding very easily, as shown in the code below:
public class A
{
protected String value = "A";
public String getValue ()
{
return value;
}
}
public class B extends A
{
private String value = "B";
}
public class C extends B
{
public C ()
{
// super.value = "C"; --> This isn't allowed, as B.value is private; however the next line works
((A)this).value = "C";
}
}
public class TestClass
{
public static void main (String[] args)
{
A a = new A ();
B b = new B ();
C c = new C ();
System.out.println (new A ().getValue ()); // Prints "A"
System.out.println (new B ().getValue ()); // Prints "A"
System.out.println (new C ().getValue ()); // Prints "C"
}
}
So, there's no way you can "revoke" access to the protected class member in the super class ThirdPartyClass. There aren't a lot of options left to you:
If your child class do not need to know about the class hierarchy above MyBaseClass (i.e. they won't refer to ThirdPartyClass at all), and if you don't need them to be subclasses of ThirdPartyClass then you could make MyBaseClass a class which does not extend from ThirdPartyClass. Instead, MyBaseClass would hold an instance of ThirdPartyClass, and delegate all calls to this object. This way you can control which part of ThirdPartyClass's API you really expose to your subclasses.
public class MyBaseClass
{
private ThirdPartyClass myclass = new ThirdPartyClass ();
public void setPropertyMap (Map<?,?> propertyMap)
{
myclass.setPropertyMap (propertyMap);
}
}
If you need a direct access to the propertyMap member of ThirdPartyClass from MyBaseClass, then you could define a private inner class and use it to access the member:
public class MyBaseClass
{
private MyClass myclass = new MyClass ();
public void setPropertyMap (Map<?,?> propertyMap)
{
myclass.setPropertyMap (propertyMap);
}
private static class MyClass extends ThirdPartyClass
{
private Map<?,?> getPropertyMap ()
{
return propertyMap;
}
}
}
If the first solution doesn't apply to your case, then you should document exactly what subclasses of MyBaseClass can do, and what they shouldn't do, and hope they respect the contract described in your documentation.
I am able to circumvent that limitation with the "cleverness" below yet it also results in maintaining two copies of the same map as well as an O(n) operation to copy over every element.
Laf already pointed out, that this solution can easily be circumvented by casting the child classes into the third party class. But if this is ok for you and you just want to hide the protected parent map from your child classes without maintaining two copies of the map, you could try this:
public abstract class MyBaseClass extends ThirdPartyClass{
private Map privateMap;
public Object getFoo(){
return privateMap.get("Foo");
}
public Object getBar(){
return privateMap.get("Bar");
}
#Override
public final void setPropertyMap(Map propertyMap) {
super.setPropertyMap(this.privateMap =propertyMap);
}
}
Note also, that it doesn't really matter, if the parents map is protected or not. If one really wants to access this field through a child class, one could always use reflection to access the field:
public class OneOfManyChildren extends MyBaseClass {
public void clearThePrivateMap() {
Map propertyMap;
try {
Field field =ThirdPartyClass.class.getDeclaredField("privateMap");
field.setAccessible(true);
propertyMap = (Map) field.get(this);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
return;
}
propertyMap.clear();
}
}
So it actually comes down to the question, why you want the field not to be accessible by the child classes:
1) Is it just for convenience, so it is immediately clear how your api should be used? - then it is perhaps fine to simply hide the field from the sub classes.
2) Is it because of security reasons? Then you should definitely search for another solution and use a special SecurityManager that also prohibits accessing private fields through reflection...
That said there is perhaps another design you could try: Instead of extending the third party class, keep a final inner instance of this class and provide public access to the inner class like this:
public abstract class MyBaseClass {
private Map privateMap;
private final ThirdPartyClass thirdPartyClass = new ThirdPartyClass(){
public void setPropertyMap(Map propertyMap) {
super.setPropertyMap(MyBaseClass.this.privateMap = propertyMap);
};
};
public Object getFoo(){
return privateMap.get("Foo");
}
public Object getBar(){
return privateMap.get("Bar");
}
public void setPropertyMap(Map propertyMap) {
thirdPartyClass.setPropertyMap(propertyMap);
}
public final ThirdPartyClass asThirdPartyClass(){
return this.thirdPartyClass;
}
}
Then, whenever you need to access the third party library with an instance of the third party class, you do something like this:
OneOfManyChildren child;
thirdpartyLibrary.methodThatRequiresThirdPartyClass(child.asThirdPartyClass());
What about creating another protected variable called propertyMap ? That should over shadow if for your child classes. You can also implement it such that calling any method on it will cause an exception.
However, as accessor methods are defined in the base class, they will not see your second shadowed version and still set it appropriately.
How can I restrict access to the protected variable from my children while still being able to manipulate it privately and set it publicly?
So you want the public to have more rights than you do? You can't do that since they could always just call the public method... it's public.
Visibility on variables is just like visibility on methods, you are not going to be able to reduce that visibility. Remember that protected variables are visible outside the direct subclass. It can be accessed from the parent by other members of the package See this Answer for Details
The ideal solution would be to mess with the parent level class. You have mentioned that making the object private is a non-starter, but if you have access to the class but just cannot downscope (perhaps due to existing dependencies), you can jiggle your class structure by abstracting out a common interface with the methods, and having both the ThirdPartyClass and your BaseClass use this interface. Or you can have your grandparent class have two maps, inner and outer, which point to the same map but the grandparent always uses the inner. This will allow the parent to override the outer without breaking the grandparent.
However, given that you call it a 3rd party class, I will assume you have no access at all to the base class.
If you are willing to break some functionality on the master interface, you can get around this with runtime exceptions (mentioned above). Basically, you can override the public variable to throw errors when they do something you do not like. This answer is mentioned above, but I would do it at the variable (Map) level instead of your interface level.
If you want to allow READ ONLY access top the map:
protected Map innerPropertyMap = propertyMap;
propertyMap = Collections.unmodifiableMap(innerPropertyMap)
You can obviously replace propertyMap with a custom implementation of map instead. However, this only really works if you want to disable for all callers on the map, disabling for only some callers would be a pain. (I am sure there is a way to do if(caller is parent) then return; else error; but it would be very very very messy). This means the parents use of the class will fail.
Remember, even if you want to hide it from children, if they add themselves to the same package, they can get around ANY restrictions you put with the following:
ThirdPartyClass grandparent = this;
// Even if it was hidden, by the inheritance properties you can now access this
// Assuming Same Package
grandparent.propertyMap.get("Parent-Blocked Chocolate Cookie")
Thus you have two options:
Modify the Parent Object. If you can modify this object (even if you can't make the field private), you have a few structural solutions you can pursue.
Change property to fail in certain use-cases. This will include access by the grandparent and the child, as the child can always get around the parent restrictions
Again, its easiest to think about it like a method: If someone can call it on a grandparent, they can call it on a grandchild.
Use a wrapper. A anti decorator pattern, that instead of adding new methods removes them by not providing a method to call it.
For context, I'm trying to make a game something along the lines of Pokemon. You obtain, train and fight monsters.
Each species of monster is a class inheriting from an abstract base class (so they can have unique behaviour), and hopefully there will be a very large number of different species throughout the game. Ex:
abstract class Monster {
int hp;
void attack();
//Etc.
}
public class FireBreathingDragon extends Monster {
static String species_name = "Fire Breathing Dragon";
//Blah
}
So when the player is exploring, they will encounter monsters local to an area at random. The game then needs to create a monster at random from a list of species that live in that area. Now to make this code reusable between areas (and make it easy to create monsters dynamically in other places in the code) I don't want to hardcode the possibilities into the area. Instead I think I'd like something along the lines of a factory that creates a monster of a given species on demand, something like:
public class MonsterFactory {
Monster createMonster(
String species_name,
//Possibly other paramters
);
}
The problem is then implementing createMonster in a "nice" or "elegant" way when you have (potentially) tens or hundreds of different Monster classes. Of course you could use a very very long if-else if-else or switch statement, but that's horrible to write and extend. Is there a nice way to do this? It would also be good if it was relatively easy to extend when adding more monsters.
Or is there some totally different design I should be using instead?
Disclaimer: My java is a little rusty, syntax may not be perfect, sorry about that.
You could register all your Monster implementation classes in a List.
List<Class<? extends Monster>> monsterTypes = new LinkedList<>();
monsterTypes.add(FireBreathingDragon.class);
// more
This doesn't have to be hardcoded. You can externalize it to some XML, Json, or other file format.
The factory instance or class can then choose a monster type from the list at a random index. You can then use reflection to instantiate the type.
The simplest solution is to have a data driven monster class. This means you only have one class (or a small number) and this class can be used for a wide variety of monsters with different attributes and abilities.
You could have a CSV file which contains each species and all the attributes and abilities fr that species. This way you could add a species by adding a line in a spreadsheet.
This solution uses Class Factories without any form of reflection. Why is this important in the context of the question ("the most elegant way")? From a very interesting exchange with another contributor: I quote Sun/Oracle's Reflection API Tutorial "Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it." To justify this, Sun/Oracle's authors resort to extremely technical reasons internal to Java. I agree with them, but my main reason is long-term code maintenance and tooling. And what's the main alternative to reflection? Annotation-based automatic code generation. I can't do something like that in this short space, but I can produce what should be, more or less, the resulting code:
public interface Factory<T> {
T make();
}
public static class BasicMonster {
}
public static class Monster1 extends BasicMonster {
public static final Factory<Monster1> getFactory() {
return new Factory<Monster1>() {
public Monster1 make() { return new Monster1() ; }
};
}
}
public static class Monster2 extends BasicMonster {
public static final Factory<Monster2> getFactory() {
return new Factory<Monster2>() {
public Monster2 make() { return new Monster2() ; }
};
}
}
List<Factory<? extends BasicMonster>> monsterFactories= new ArrayList<Factory<? extends BasicMonster>>();
{
monsterFactories.add(Monster1.getFactory());
monsterFactories.add(Monster2.getFactory());
}
...
BasicMonster newMonster= monsterFactories.get(aRandomOne).make() ;
Form static class used to indicate classes not intended as inner.
Even if list monsterFactories were initialized through reflection, the presence of factory objects in the code permits a higher level of static analysis than reflective constructor invocation.
You could put all the classes in a specific package, then scan that directory for class files, load them, and track the ones that extend Monster. You could even define some custom annotations to help manage this, e.g. #IgnoreMonster to temporarily disable some without having to change the location of the file. This is similar to the way e.g. Hibernate scans source to find entity mappings.
Here is an example. All the Monster classes are placed in package dload.monsters. First, here's the base class I'm using for this example:
package dload.monsters;
public abstract class Monster {
public abstract String getName ();
}
Then, a MonsterFactory which scans for all classes in the dload.monsters package (sorry its a little sloppy, and I skimped out on exception handling):
package dload.monsters;
import java.io.*;
import java.net.*;
import java.util.*;
public class MonsterFactory {
private static final List<Class<? extends Monster>> monsterClasses = new ArrayList<Class<? extends Monster>>();
private static final Random random = new Random();
#SuppressWarnings("unchecked") // <- for monsterClasses.add((Class<? extends Monster>)cls);
public static void loadMonsters () throws Exception {
// in this example, Monster is in the same package as the monsters. if
// that is not the case, replace "." with path relative to Monster.
File folder = new File(Monster.class.getResource(".").toURI());
for (File f : folder.listFiles()) {
if (f.getName().endsWith(".class")) {
String name = f.getName().split("\\.")[0];
// replace "dload.monsters." below with package monsters are in
Class<?> cls = ClassLoader.getSystemClassLoader().loadClass("dload.monsters." + name);
// if Monster is not in same package as monsters, you can remove
// cls.equals(Monster.class) check. this check makes sure the loaded
// class extends Monster, but is not the Monster class itself (since
// its also in that package).
if (Monster.class.isAssignableFrom(cls) && !cls.equals(Monster.class)) {
System.out.println("Found " + cls.getSimpleName());
monsterClasses.add((Class<? extends Monster>)cls);
}
}
}
// at this point all Class's for monsters are in monsterClasses list.
}
public static Monster randomMonster () throws Exception {
// choose a class at random
int n = random.nextInt(monsterClasses.size());
Class<? extends Monster> cls = monsterClasses.get(n);
// instantiate it
return cls.newInstance();
}
}
Then, when you want to use it:
public static void main (String[] args) throws Exception {
// load monsters; only need to do this once at startup
MonsterFactory.loadMonsters();
// create 10 random monsters
for (int n = 0; n < 10; ++ n) {
Monster m = MonsterFactory.randomMonster();
System.out.println("name is " + m.getName());
}
}
Note that at any time you can check the monster's Class for relevant annotations.
Another option, if the classes are already loaded (which they won't be if they've never been used or explicitly loaded) is to use Instrumentation.getAllLoadedClasses() to get a list of all classes currently loaded, then scan through all classes looking for ones that are assignable to a Monster.
Note: I do feel like there is a cleaner way to do the actual scan, and I haven't tested this in a JAR. Suggestions welcome.
All that being said, if a Monster's behavior could be entirely defined by data, I also support and recommend the data driven approach described above.
You should take a look at the Cartesian Product Algorithm. It will generate every combination of products and you can then choose one at random.
Essentially the algorithm will take arrays of attributes and create unique combinations of the different attributes and add them to an array. You can then randomly select a key from the array when you create the enemy. That way every enemy has a random chance to have any number of attributes.
have an interface or base class that provides a monster.
I thought I'd include this wiki-bit, "The factory method pattern is an object-oriented creational design pattern to implement the concept of factories and deals with the problem of creating objects (products) without specifying the exact class of object that will be created."
This lets you use superclass methods or interfaces exclusively without ever needing to know the specific subtype of the interface. This is important because you cannot call new base_monster();
abstract class base_monster {
abstract base_monster factory();
}
/// make sure every monster has a name...
//
abstract class Monster extends base_monster {
String name;
static int object_counter = 0;
Monster factory() {
name = Integer(object_counter).toString();
object_counter();
return this;
}
/// this class has a useful setter
void object_counter( int c ) { object_counter++; out.println( object_counter ); }
}
class Griffon extends Monster {
Monster factory() { return new Griffon(); }
}
class Harpy extends Monster {
Harpy() { name = "Grizelda WhuttleThut III"; }
Harpy factory() { return new Harpy(); }
}
class BlackHarpy extends Harpy {
BlackHarpy factory() { super.factory(); return new BlackHarpy(); }
}
// we assume that each class has a default constructor. But,
// if the array is filled with monsters of different subclasses we
// would have to use reflection or nasty instanceof switches to be
// able to call a (specific) defined constructor.
ArrayList<Monster> monsters = new ArrayList<Monster>();
monsters.add( new BlackHarpy() );
for( int I = 0; I < ave_monsters_appearing; I++ )
monsters.add( new Harpy() );
//
// an array of ten harpies and a boss Harpy.
///
// how can this array of monsters be copied into the other array?
// (we want object copies, not reference copies)
///
ArrayList<Monster> local_monsters = new ArrayList<Monster>();
/// solution: use the factory method
for( Monster m : monsters )
local_monsters.add( m.factory() );
.
.
Hope this solves the problem of not having a static method.