The 3 methods are
A) superclassing
B) superclassing modified
B) delegation.
In thinking over superclassing vs delegation, I realized
superclassing, if used in a modified form isn't so bad.
However straight superclassing presents real design problems with
inconsistent states between ModuleConfig & ApplicationConfig objects.
I still prefer delegation a bit more, but will help out regardless of
the consensus.
Here were my thoughts
Use cases, using C++ type syntax for brevity
Need to support:
1) ApplicationConfig as arguments to methods
void ClassY.methodA(ApplicationConfig)
2) ApplicationConfig as return values
ApplicationConfig ClassX.methodB()
3) Operations on ApplicationConfig protected fields
protected ApplicationConfig.formBeans.field1
4) Operations on ApplicationConfig public methods
public ApplicationConfig.formBeans.methodZ()
DESIGN GOALS:
A) Use ModuleConfig internally in Struts exclusively.
B) Create ApplicationConfig objects for external consumption only,
avoid storing/persisting ApplicationConfig
C) operations on the methods and properties manipulate
the same underlying objects
1) ApplicationConfig as arguments to methods
void ClassY.methodA(ApplicationConfig)
"extract Superclass method"
A Method signature of
void ClassY.methodA(ModuleConfig)
Works for both ModuleConfig objects and Application Config
objects.
This is a strength of superclassing.
"Delegation "
A Method signature of
void ClassY.methodA(ApplicationConfig)
and
void ClassY.methodA(ModuleConfig)
would need to be supplied.
What does that mean ?
void ClassY.methodB(ApplicationConfig ac) {
ClassY.methodB(ac.methodConfig);
}
Superclassing is more elegant. Delegation required the
definition of a 2nd method. For each and every method that
takes a ModuleConfig.
2) ApplicationConfig as return values
ApplicationConfig ClassX.methodA()
"extract Superclass method" with mods
A Method signature of
ApplicationConfig ClassX.methodA()
There are two choices to support this use case
1) "superclassing pure" The other alternative is
to construct a separate
object entirely that i.e. declare
class ApplicationConfig extends ModuleConfig {
}
While possible however this violates the design goal C:
“operations on the methods and properties manipulate
the same underlying objects”
Does it matter if a ApplicationConfig.plugIn object points to a
different copy of an ArrayList than the internal ModuleConfig ?
It very well could. At the very minimum it opens up the
possibility of ending up with ModuleConfig and ApplicationConfig
for a given module containing different plugIn items if a .add()
is performed.
It also changes the behavior from Struts 1.1B2.
2) The second choice "superclassing modified"
A ApplicationConfig object can be created from a
ModuleConfig object:
class ApplicationConfig {
ApplicationConfig(ModuleConfig mc) {
super(mc.prefix);
this.field1 = mc.field1
this.field2 = mc.field2
}
}
Currently the Constructor for ApplicationConfig and thus
the new ModuleConfig would look like :
class ModuleConfig {
static field1 = new Map();
ModuleConfig(String prefix) {
this.prefix = prefix
}
}
During the super() call the fields are initialized
such as field1 = new Map(), then they would be reinitialized
again with this.field1 = mc.field1
This can be optimized a by rewriting a 2nd constructor
for ModuleConfig
class ModuleConfig {
ModuleConfig(String prefix) {
this();
this.prefix = prefix;
this.field1 = new Map();
this.field2 = new Map();
}
ModuleConfig(String prefix,boolean dummy) {
this.prefix = prefix;
}
}
"Delegation "
A Method signature of
ApplicationConfig ClassX.methodA()
A ApplicationConfig object can be created from a
ModuleConfig object:
ApplicationConfig {
ApplicationConfig(ModuleConfig mc) {
this.mc = mc;
this.field1 = mc.field1
this.field2 = mc.field2
}
}
With Superclassing 2 constructors needed to be invoked, an
ApplicationConfig and a ModuleConfig to return a
ApplicationConfig object. Also an additional
constructors needs to be added to ModuleConfig especially for
ApplicationConfig to prevent unnecessary creation of objects
that are later thrown away. This constructor will need to be
deprecated and removed at a later time.
For Delegation only one object is created.
Here I would give an advantage to delegation over
"superclass modified" method.
3) Operations on ApplicationConfig protected fields
protected ApplicationConfig.formBeans.field1
Looking at the above constructors, from section 2 we see that
"superclass pure" presents similar hazards of not modifying
the same objects. For both cases "superclassing modified" and
delegation are equivalent for manipulate fields since
they are assigned in the constructor.
4) Operations on ApplicationConfig pubic methods
public ApplicationConfig.methodsZ() ()
“extract superclass method”
Similarly to the section 1.
Nothing more needs to be performed
“delegation”
Similar to section 1.
For each Method in ApplicationConfig
ApplicationConfig {
methodA(x) {
return mc.methodA(x);
}
}
Here superclassing has the advantage.
SUMMARY
1) superclassing required no new methods to be added
for the case of
ClassX.method(ApplicationConfig)
is changed to
ClassX.method(ModuleConfig)
delegation adds an addition method
ClassX.method(ApplicationConfig) {
return ClassX.method(ModuleConfig);
}
2) ApplicationConfig as return values
ApplicationConfig ClassX.methodA()
"superclassing pure" presents hazards.
"superclassing modified" requires an additional constructor
for ModuleConfig.
3) Operations on ApplicationConfig protected fields
protected ApplicationConfig.formBeans.field1
As in 2 "superclassing pure" presents hazards.
"superclassing modified" is equalivent to "delegation"
4) Operations on ApplicationConfig pubic methods
public ApplicationConfig.methodsZ() ()
"superclass pure" presents hazards.
"superclass modified" no extra methods.
"delegation requires extra methods.
---------------------------- A---------------------------------
class BadClass {
BadClass(x,y) {code}
methodA(z) {code}
}
becomes ...
class GoodClass {
GoodClass(x,y) {code}
methodA(z) {code}
}
class BadClass {
BadClass() { super(x,y) }
}
--------------------------- B ------------------------------------
class BadClass {
BadClass(x,y) {code}
methodA(z) {code}
}
becomes...
class BadClass {
GoodClass goodClass
BadClass(x,y) {goodClass = new GoodClass(x,y)}
BadClass(GoodClass goodClass) {this.goodClass = goodClass}
methodA(z) {return goodClass.methodA(z)}
}
class GoodClass {
GoodClass(x,y) {code}
methodA(z) {code}
}
--
To unsubscribe, e-mail: <mailto:struts-dev-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:struts-dev-help@;jakarta.apache.org>