Re: Converting from one subclass to another
Re: Converting from one subclass to another
- Subject: Re: Converting from one subclass to another
- From: David Elliott <email@hidden>
- Date: Thu, 14 Feb 2008 01:12:33 -0500
Hi David,
On Feb 13, 2008, at 5:06 PM, David Avendasora wrote:
Alright, maybe I'm doing something completely wrong (yeah, yeah,
Chuck), but this isn't an apples and oranges thing.
They are both nutritional values, for example: "Calories: 120"
You may have unwittingly just solved your own problem by realizing
this. But then again you may not have.
The difference between my two subclasses is simply how the value of
the valueQuantity() attribute is derived. In one subclass the user
specifies the amount, and in the other it is calculated from the
components that make up the product that the nutrition information
is tied to. This made me think: different implementations of the
same core object means two subclasses.
Then maybe subclassing is the right thing to do. I'm not going to
make the blanket statement that it's necessarily wrong in this case.
All I can do is try to refine your thoughts on the matter. The best
question to ask yourself is whether or not it is appropriate for the
user to choose before creating your subclass. If so, then you are
fine. It sounds like it may be the case that you could easily ask the
user whether he wanted to add a new fixed nutritional value or one
calculated from the ingredients. If he chooses to add a calculated
one, you may not even display the edit page for it. If he chooses to
add a fixed one then you go ahead and display the form.
I think the problem you were originally seeing is that EOF won't
necessarily prevent you from creating an object of the abstract
NutritionalValue class. So make sure you code is set up to create the
appropriate sub class.
If you cannot decide _before_ creating the object that will go into
the nutritionalValues relationship of your NutritionalBlock then
subclassing is not appropriate. If you can decide it, then
subclassing may very well be appropriate. If you actually create the
object with the right type to begin with then you won't have the weird
problem you were originally having which is that the object only takes
on its real class after it is refaulted and
I think the best thing I could do is throw the whole inheritance
structure out and simply put an "if" clause on the valueQuantity()
method that returns the calculated value if nutritionValueType()
returns "Calculated", or simply change nutritionValueType() to an
isCalculated() boolean instead.
Just doesn't seem as cool though. But I guess "working" is even
cooler.
Like I said, it all depends. Subclassing is an advanced EOF feature
that can be quite useful but you have to understand the limitations.
There's very little literature on it and most people suggest to just
avoid it entirely which is not always the right thing to do but may be
if you cannot wrap your head around it.
-Dave
Dave
On Feb 13, 2008, at 3:57 PM, David Elliott wrote:
Hi David,
I can tell you right now that you're going down a very dangerous
path here.
Consider even simple code like this:
MyObject *foo; // from somewhare
foo.setNutritionValueType(...);
// foo is still the old instance.
When you subclass like this you need to know _before_ you create
the object which subclass it is to have. To do anything else is to
completely defeat the very purpose of subclassing in the first
place. If you are using D2W there are little methods you can use
to provide the user his choice before actually creating the object.
If you are not using D2W then you just ask the user in advance.
One UI pattern I've used is something like this:
Add new: [_] Apple [_] Orange [+]
Where [+] is a regular button to add the new object to the list and
Apple/Orange is a set of radio buttons. You could use a pop-up or
whatever else instead. Or you could create an add button for each
object.
What you cannot do is create a Fruit and then expect to be able to
make it an Apple or an Orange at your whim. It doesn't work that
way. Subclassing _might_ be the appropriate thing to do in your
case but it's a very advanced feature of EOF that you have to plan
for very well.
One other thing. If the attributes are the same regardless of the
type (and by this example, they seem to be) then subclassing is the
wrong thing to do entirely. If you want different behavior for
different nutrition types based on the same set of attributes then
I suggest you break your logic into a separate class.
For example:
class Fruit {
...
static abstract class Calculator {
abstract void doSomething(Fruit fruit);
};
static class AppleCalculator extends Calculator {
...
};
static class OrangeCalculator extends Calculator {
...
};
static NSDictionary _calculatorForType = new NSDictionary(new
String[]
{ "APPLE"
, "ORANGE
}, new Calculator[]
{ new AppleCalculator()
, new OrangeCalculator()
});
...
};
You might even implement a cover method like so:
static Calculator calculatorForType(FruitType ft)
{
return
(Calculator)_calculatorForType.valueForKey(ft.fruitTypeCode());
}
That will get you polymorphism (thus allowing you to avoid nasty
switch or if/elif) without requiring the objects themselves have
their own distinct types.
Note that the above isn't compiled, it's just a general example I
typed straight into this mail. Also note I've been doing more C++
than Java coding lately so I probably have semicolons where I don't
need them.
There's other ways to do it too. For example, you can give each
object its own instance of Calculator in which case it will be
easier to make it a non-static inner class and acually store the
pointer to it as a _calculator i-var or something of the sort.
It all really depends on how much state you need or do not need to
put in the separate Calculator class.
-Dave
On Feb 13, 2008, at 3:26 PM, David Avendasora wrote:
Well, I've got it working, somewhat. I'm creating a new instance
of the other subclass, copy all the attributes and relationships
over, then delete the old instance.
The problem I'm having is that I'm somehow getting two instances
of the target subclass. Maybe someone can look at this code and
tell me what I'm doing wrong.
public void setNutritionValueType(NutritionValueType aValue) {
if (nutritionValueType() == null || aValue == null) {
super.setNutritionValueType(aValue);
} else if (aValue != nutritionValueType()) {
CalculatedNutritionValue nv = new CalculatedNutritionValue();
editingContext().insertObject(nv);
nv.setIsSuspect(isSuspect());
nv.addObjectToBothSidesOfRelationshipWithKey(nutritionBlock(),
"nutritionBlock");
nv.addObjectToBothSidesOfRelationshipWithKey(nutritionElement(),
"nutritionElement");
nv.addObjectToBothSidesOfRelationshipWithKey(aValue,
"nutritionValueType");
nv.setSortOrder(sortOrder());
nv.addObjectToBothSidesOfRelationshipWithKey(unitOfMeasure(),
"unitOfMeasure");
nv.setValueQuantity(valueQuantity());
editingContext().deleteObject(this);
}
}
It seems very simple. I just don't get where I'm getting a second
instance. If I save the EditingContext, I do get two new records
in the DB.
Any guesses? Anyone?
Dave
On Feb 13, 2008, at 3:01 PM, Ken Anderson wrote:
David,
I've been down this path many times, and my current
recommendation is to just have some kind of type on the object
that identifies it as one or the other. I have a situation where
I store the class name of a 'calculator' class in the EO, and
then dynamically call static methods on that class to do the
heavy lifting.
Ken
On Feb 13, 2008, at 11:15 AM, Mike Schrag wrote:
I could just create a new object of the new class and delete
the existing one
yes
What is the best way to get WO to pickup the subclass change
immediately?
Or is this just a bad way of doing things?
yes
Inheritance hierarchy is to be treated like a PK -- It's an
unchanging attribute of an EO. In fact, it's actually part of
the EOGlobalID.
What you're describing is very similar to the Employee vs
Manager modeling problem. The general consensus is that when an
Employee becomes a Manager, it's not changing types, it's
changing Roles, so it's actually a Person=>Role, where Role is
Manager or Employee and you are just changing the relationship,
not the intrinsic type.
You should consider type to be immutable for your own safety and
the safety of those around you. The correct way is to delete
your old class and make a new one, or maintain a relationship to
(something like) a NutrionValueCalculator that changes (if there
is a lot of other state in this object other than just this
value).
ms
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
@tgwbd.org
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden