Re: A hopefully interesting design question ...
- From: x97mdr <jeffreycameron@xxxxxxxxx>
- Date: Tue, 3 Feb 2009 06:21:55 -0800 (PST)
Me again,
Hopefully one last time ...
I reviewed the State pattern and it appears as though it assumes that
each concrete state object has the same set of operations on it.
Since my objects each have different properties (with a small subset
of common properties) they do not seem to have this prerequisite so
I'm a little confused. I think I can work around this but I just want
to make sure that I am following the right path here.
The context object provides access to the items in the various states
so will I have to have in my context object a method that allows
access to each of the possible properties of the subclasses:
public class Context
{
void RequestImputable(); // from EditedAnswerBox
void RequestInvalid(); // from EditedAnswerBox
void RequestImputationAction(); // from DonorAnswerBox
}
where the Context does some checking in each of the Requestxxx methods
to see what state it is in and determine if the request makes sense or
not. For instance, it would trigger an exception if the Context's
state was EditedAnswerBox but the user called RequestImputationAction.
Also, what interface would the Context's private state be in my
instance? Would it be an interface defining all possible properties
(perhaps implemented as an interface of all of the other possible
interfaces?)
Thanks in advance, again its very much appreciated
On Jan 27, 5:27 pm, "H. S. Lahman" <h.lah...@xxxxxxxxxxx> wrote:
Responding to x97mdr...
Every Answer Box has a set of properties that always apply, for
instance, if the Answer Box is imputable (i.e. it can be imputed) so
the basic Answer Box is (all examples in C#):
public AnswerBox
{
public bool Imputable { get; set; }
}
Now after an Answer Box has been edited, it has a new set of
properties BUT it still needs access to the basic properties of an
AnswerBox:
public EditedAnswerBox
{
public bool Imputable { get; set; }
public bool Invalid{ get; set; }
public bool Inconsistent{ get; set; }
}
IF an Answer Box is edited and either its Invalid or Inconsistent
flags are true then it can be considered either a Fail or a Donor and
may take on other properties and still requires access tot he Edited
Answer Box properties:
public DonorAnswerBox
{
public bool Imputable { get; set; }
public bool Invalid{ get; set; }
public bool Inconsistent{ get; set; }
public ImputationAction Action { get; set; }
}
So there is my dilemma: Do I model this with:
1. Inheritance
public DonorAnswerBox : EditedAnswerBox
{
public ImputationAction Action { get; set; }
}
Probably not for a couple of reasons. One is that the generalization
would look like:
[AnswerBox]
+ immutable
A
|
+-------+---------+
| |
[EditedAnswerBox] [NotInEditedAnswerBox]
+ invalid
+ inconsistent
A
|
+-----------------+
| |
[DonorAnswerBox] [NotInDonorAnswerBox]
+ action
This will trigger the Warning Klaxons for reviewers because the dual
notion to generalization is specialization. That is, members of OO
subclasses should have properties that specialize that them compared to
other members of the superset. In your case there is nothing to
specialize the members of [AnswerBox] that are not members of
[EditedAnswerBox]. So the only thing that defines them is a negative;
they are not members of [EditedAnswerBox].
Defining stuff in terms of negatives is usually a no-no and that is
especially true for OOA/D because it opens a Pandora's Box of
opportunities for foot-shooting when doing maintenance.
<aside>
Sadly several popular OOPLs screw this up by allowing one to instantiate
a member of a superclass without specifying a leaf subcalss. This
violates the basic proposition that the union of members of all
subclasses must be a complete set of the superclass members.
(Emphasizing the negative definition is why I drew the generalization
the way I did; it becomes obvious if one explicitly defines a subclass
for the remaining superclass members.) It leads to serious problems when
the tree is modified during maintenance by adding other classes that do
provide specialization. For example, one does not know if superclass
instances the were created prior to the maintenance should be in the new
subclass or not. That sort of thing can lead to very obscure defects.
</aside>
There is another reason not to use generalization. Your subclasses are
the result of operations on the superclasses. Thus an EditedAnswerBox
can only exist if somebody edits an AnswerBox (e.g., a member of
[NotInEditedAnswerBox] is operated on to become a member of
[EditedAnswerBox]. Ultimately the OO generalization is simply a Venn
Diagram from set theory. It simply classifies and a single object
resolves the properties of the entire tree. So the notion of an object
of a subclass being created from an existing superclass object
introduces some issues around identity and what an object actually is. So....
2. Modified Decorator
public DonorAnswerBox : IEditedAnswerBox // pretend I have an
interface like this even though I'm too lazy to type one out :)
{
private IEditedAnswerBox context;
public bool Imputable { get { return editedAnswerBox.Imputable; }
set { editedAnswerBox.Imputable = value} }
public bool Invalid { get { return editedAnswerBox.Invalid ; } set
{ editedAnswerBox.Invalid = value} }
public bool Inconsistent { get { return
editedAnswerBox.Inconsistent ; } set { editedAnswerBox.Inconsistent =
value} }
public ImputationAction Action { get; set; }
}
I still think this is too elaborate because there is no dynamic
substitution that depends on run time context. Everyone who interacts
with some flavor of an AnswerBox knows exactly what it is dealing with,
both to create it and to collaborate with it.
3. Some other pattern ??
p.s. I did read the other responses. I'm not sure that State really
covers what I want (though it might possibly). And I think my
modified Decorator pattern is something like what you are getting at,
am I correct?
Actually, at this point I would agree with using the State pattern. What
you have described is classically known as subclass migration.
[BasicBox]
A
|
+----------------+----------------------+
| | |
[AnswerBox] [EditedAnswerBox] [DonarAnswerBox]
+ immutable + immutable + immutable
+ invalid + invalid
+ inconsistent + inconsistent
+ action
The trick is that the same entity can migrate through being any one of
these. Thus the same BasicBox can start as an AnswerBox and then be
morphed into an EditedAnswerBox. The disjoint set rule for OO
generalization is satisfied since the AnswerBox with that identity
ceases to exist when the migration takes place.
Until the GoF book there were someinterestingand sometimes amusing
techniques for implementing this, especially in code generators.
However, the GoF state pattern offered a very elegant way of dealing
with the problem. The mechanism was still superficially generalization
but with a unique twist. The "states" don't attempt to provide
generalization or specialization; they simply define property sets that
an entity may have in different contexts. Thus [BasicBox] is more of an
organizational collection than a true OO superclass because each
subclass could be a standalone class. (That's why 'immutable' appears in
all the subclasses, but not in the superclass.)
[Typically there will be relationships between the "subclasses" to
define constraints on how the migration can take place. For example, in
your situation one can only become an EditedAnswerBox if one was an
AnswerBox. So an association would exist between [AnswerBox] and
[EditedAnswerBox] but not between [AnswerBox] and [DonorAnswerBox].]
The nice thing about the State pattern is that the rules for normalizing
classes don't apply because it is the same entity migrating between the
classes. IOW, one can't argue that it shouldn't have a property because
that property was already in another class. (The key to this legerdemain
is that the property is still tied to the entity identity when it
migrates.) So these "standalone" classes can have some identical
attributes. (One way to think of this is that values of attributes such
as 'immutable' are "inherited" horizontally as the object migrates from
one subclass to the next.)
The only remaining implementation issue is accessing the object. I am
assuming that there will be relationships from each subclass to
particular classes that care about the specific role the object is in
(e.g., An Editor object if the entity is in the AnswerBox
instantiation). Also note that at the OOP level there is nothing to
prevent you from providing different interfaces for each subclass, such as
public AnswerBox: IAnswerBox
{...}
public EditedAnswerBox: IAnswerBox, IEditedAnswrBox
{...}
public DonorAnswerBox: IAnswerBox, IEditedAnswerBox, IDonorAnswerBox
{...}
to restrict collaborations.
--
Life is the only flaw in an otherwise perfect nonexistence
-- Schopenhauer
H. S. Lahman
H.lah...@xxxxxxxxxxx
software blog:http://pathfinderpeople.blogs.com/hslahman/index.html
.
- Follow-Ups:
- Re: A hopefully interesting design question ...
- From: H. S. Lahman
- Re: A hopefully interesting design question ...
- Prev by Date: Re: Presentation Chooser
- Next by Date: Re: A hopefully interesting design question ...
- Previous by thread: Re: A hopefully interesting design question ...
- Next by thread: Re: A hopefully interesting design question ...
- Index(es):
Relevant Pages
|