问题
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 eachAbstractCommand
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 inAbstractCommand
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?
回答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
回答2:
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.
回答3:
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.)
回答4:
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.
回答5:
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:
Create an Interface with some rules. That you want to apply in all your subclasses
Now Let
AbstractCommand
implement that Interface and it should also containstate
variable, by doing this you can maintain a set of rule at lower levelIn 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.
回答6:
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
回答7:
public abstract class AbstractCommand {
private static State state;
static {
state = Controller.getState();
}
protected AbstractCommand(){
}
public State getState(){
return state;
}
}
来源:https://stackoverflow.com/questions/14383276/initialize-member-of-abstract-class-without-subclasses-having-write-access