Initialize member of abstract class without subclasses having write access
Asked Answered
E

7

2

I have an abstract class:

public abstract class AbstractCommand {

    private static State state;
}

Intention

  • An object of class State is provided by some "controlling classes", providing data that is needed by each AbstractCommand subclass
  • Each subclass needs read access to it
  • The subclasses are not allowd to change the field

Current approach

The field state should be initialized by the "controlling classes" of the program so that subclasses (that define commands) can use it (read-only). The subclasses are defined internally and should be used as an interface for the user. This user should not have write access to state.

The problem

  • Adding a public setState() method in AbstractCommand would make it accessible to all subclasses and with that to the user
  • Making the field final would force the creating of the object to take place in the abstract class and the "controlling classes" would have to use this object, furthermore it would not be replacable

How do you handle something like this?

Another try

Because some answers suggested solutions using package visibility I wonder if this would do a good job:

Have a class in the same package that provides the required information by delegating a call from the "controlling classes" (from outside the package) to the abstract class.

Sounds a little fuzzy, too but what do you think?

Erupt answered 17/1, 2013 at 16:10 Comment(8)
So you want that subclasses of AbstractCommand can't set the state value, but an other class can do it ?Slovenly
Yes, something like that. Of course, a more appropriate solution with the same effect would do it as well.Erupt
Do you want the state variable to be "shared" among all of your commands? Seems to me you'd want to share state only among your extending classes (i.e. one for all instances of Command1, one for all instances of Command2, etc).Solo
Yes, all commands (which are the extending classes) need read access. It is always the same variable.Erupt
I see, but declaring the state as static in your abstract class will cause all of your extending classes to share the same state. So if an instance of Command1 will have the same state as an instance of Command2. Just making sure I understand what you want.Solo
Is there a reason you can't just add a getState method to these "controlling classes"? It seems to me like this is a bunch of extra work to achieve just that.Transcendentalistic
Yeah, getState() in the conrolling classes... But when to call that? What if that object would change and another one has to be set?Erupt
You should tag people you're replying to using @, for example, @user905686, this sticks a notification in my inbox so I see your comment and can answer. Anyway, don't store it in a variable in your commands, just call getState() when you need it and store it in a local variable inside your method. Then you don't have to worry if it changes since you'll just be getting it when you need it. If you need to update commands when the state changes, you can use the observer pattern (a.k.a. "listeners" in Java).Transcendentalistic
S
1

If I understand you correctly, you are looking for the protected keyword.

In java this keyword allows for subclass and package field access, but does not make the field public. This allows for the public read-only behavior you're looking for without sacrificing the public protection of the field. The only classes that can access a protected field directly will be anything in the same package or a direct subclass (which may be in a different package).

Source: http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

Stereophotography answered 17/1, 2013 at 16:16 Comment(4)
No, not really. That would make it accessible via the subclasses which the user uses... and the user should not have access to it.Erupt
So you have a private variable and, optionally, a private setter but a protected getter. The subclasses can read the value. Only the class itself (the parent) can change it.Unhandy
The private setter would not help much because the field has to be set from the outside. The problem is: not by everyone.Erupt
At this level of complexity, I'm not sure access modifiers give the level of control he's looking for.Stereophotography
D
1

You could put the AbstractCommand into the same package with the "controlling classes" and specific implementations to another package. Then you could provide a package-private setter and protected getter. This would allow the controlling classes set the value and implementations would only have access to the getter.

Howevery, this would mess your package structure. If you do not want this to happen - try to use a Factory. You culd build the following package structure:

 command
     impl
         CommandImpl1 //extends AbstractCommand
         CommandImpl2 //extends AbstractCommand
     AbstractCommand
     CommandFactory

The idea is that a Factory is used to create instances of an AbstractCommand. So you will pass the parameter to the Factory in any other package and it would select an implementation you need and return you a new object. In this case you could use the previous idea to grant proper access to the getters and setters. However here you would be able to set the field once and forever.

If you need to modify it many times, you could create an assessor. This is the CommandAccessor class in the same package as your AbstractCommand and it should provide the static methos like:

public static void setState(State newState, AbstractCommand command);

Nothing would prevent you from using it in the implementation classes, however you could just set an informal rule that it should no be used.

Diastema answered 17/1, 2013 at 16:22 Comment(5)
Yeah, thanks, that would be possible... But that would also mix up the package structure a little because logically they don´t really belong together...Erupt
This is a strange use-case you have described. Usually you want to give more access to subclasses than to unrelated class, not less.Diastema
@Erupt That's because what you are trying to do requires quite a bit of brain gymnastic. It's quite unusual (at least to me) to have a field in a super class that must be read-only from the subclasses, but read-write to other clients of the said class. I have no knowledge about your project, but it sound to me as if you are working from an innapropriate design.Burgas
The factory (or builder) pattern doesn´t sound bad. But I don´t want the factory to chose an implementation - I have different commands and the user should be able to create instances of any command he likes.Erupt
you could create different instanses with different methods. Or, you could create a enum with the comand type and provide it to the Factory. If you are implementing a parser enums normally do quite well in the parsing codeDiastema
G
1

I can only offer fuzzy solutions.

Some solutions first:

Either do

private static final State state = Controller.initState();

Or use inversion of controll, dependency injection, @Inject. That would allow unit tests too. There certainly are open source DI containers out there in the web (Spring, or is Pico container still around?). Or requesting beans from some DI container.

If both are too early, go for lazy evaluation (partly the initialisation of statics is already lazy). Sometimes one will see an inner class:

private static class Singleton {
    private static final State state = Controller.initState();
}

Possibly with a getInstance.

My choice:

Somehow no statics, but getters to singletons. A bean frame work with controllers.


Singletons instead of statics.

Statics (static functions) where abundantly used in the prior eclipse 3 rich client, like

IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
boolean autoPrint = store.getBoolean(AUTO_PRINT);

Now alternatively with dependency injection by the OSGi container and annotations:

@Inject @Preference(AUTO_PRINT)
boolean autoPrint;

From: Eclipse 4, Rich Clients by M. Teufel and J. Helming

Besides being shorter, there is less coupling between classes, and unit tests can more easily be written, as we can fill in autoPrint like we like, and do not need to meddle with the filling class.

If one hesitates adding the overhead of such a container, the easiest way is to have alternatives to several statics is having one global application context, where you can lookup java objects, POJO beans. Maybe backed by an XML file:

State state = ApplicationContext.lookup(State.class, "state");

<bean name="state" class="org.anic.State" value="sleepy" depends="firstThis"/>
<bean name="firstThis .../>

Mind, there no longer is a need to have a static state.

The Spring framework has such an XML approach.

The advantage being a centralized initialisation, where sequence and different factory / creation methods are thinkable.

(Sorry for the messy answer.)

Gaberlunzie answered 17/1, 2013 at 16:45 Comment(1)
Could you explain the singleton approach a little more detailed? What is the difference / the trick?Erupt
S
0

Pass it in as the constructor of your abstract class

public abstract class AbstractCommand {
    private static State state;
    protected AbstractCommand(State state){
        this.state = state;
    }        

    public State getState(){
        return state;
    }
}

In your extending classes...

 public class Command1 extends AbstractCommand{
       public Command1(){
             super([some state]);
       }
 }

The extending class can set the state once during initialization, but has read-only access thereafter.

Solo answered 17/1, 2013 at 16:26 Comment(1)
But then subclass would have to ask the "controlling classes" for the object (and you are not sure if this is already initialized) and also the field would be set again each time a command is constructed...Erupt
R
0

So I see you want the behavior as mentioned by Magus as "So you want that subclasses of AbstractCommand can't set the state value, but an other class can do it ?"

Here is my suggestion:

  1. Create an Interface with some rules. That you want to apply in all your subclasses

  2. Now Let AbstractCommand implement that Interface and it should also contain state variable, by doing this you can maintain a set of rule at lower level

  3. In second leg of Interface define in step 1 have your other class that you want not to have access to AbstractCommand class variable

By doing this you can maintain your package structure. Hope this helps.

Regine answered 17/1, 2013 at 16:31 Comment(2)
I´m not sure if I undestand you right, what exactly prevents the subclasses from setting the value? Where can I set the value? I don´t really get point 3.Erupt
refer latest comment I have added. I could not add it here because of post limit.Regine
R
0

Here is what I was trying:

Create Interface as:

public interface RuleInterface { //Define rules here void method1(); }

Now implement this in your AbstractCommand class

public abstract class AbstractCommand implements RuleInterface{ private static String state; }

Have other class, this class can modify state varibale

public class SubClassAbstractCommand extends AbstractCommand{ @Override public void method1() {
} }

Create one more leg for Interface as:

public class AnotherLeg implements RuleInterface{ @Override public void method1() { } }

Now AnotherLeg class can't access state variable but still you can enforce the rules via interface RuleInterface

Regine answered 17/1, 2013 at 17:7 Comment(1)
state is private, so how can SubClassAbstractCommand modify it? Then again, the subclass (command) would need access, buzt read-only...Erupt
F
0
public abstract class AbstractCommand {
    private static State state;
    static {
        state = Controller.getState();
    }
    protected AbstractCommand(){
    }        
    public State getState(){
        return state;
    }
}
Fewness answered 19/7, 2014 at 14:12 Comment(1)
Welcome to the site. Would you mind expanding it by adding some text to explain how it solves the problem & to differentiate it from the preceding answers?Skindive

© 2022 - 2024 — McMap. All rights reserved.