From: ulf on
Rick Elbers wrote:
> You touch somewhere a terminological issue which I would discuss,
> skipping the main line of the discussion with Lahman.

Nice of you to join in.

> >Since we talk about terminology, let me say I am not happy with
> >"responsibility" either: Saying an object has the "responsibility for
> >knowing/doing sth." does not include in its meaning whether the object
> >actually does know/do it. This may be good for expressing requirements
> >for systems still to be built, but does not fit as well to describe
> >existing systems/classes/frameworks...
> >Whereas when I say an object has the abstract data (property) X or
> >abstract behavior (property) X then I express that it *does* know/do X
> >(and say nothing about how it does this).
> >
>
> This is interesting. I am not native english speaker, but I think I
> understand that you implied that:
>
> Sayiing "an object has some responsibility to do something" has a
> much richer meaning then:
> Saying "an object actually does something"
>
> Am I right so far ?

Hmm. I think no :( What I _indended_ to express was not sth I would
have called "richer meaning". When I read again what I did express, I
would better understand if you had understood me the opposite - because
I said: "Saying an object has the "responsibility [...]" does **not**
include in its **meaning** [...]".

> In my experience in design talking about responsibilities drives the
> design, which is indeed much more then that it only establishes object
> properties or actual behaviors. It helps filling in colloborations and
> gaps between objects too.

Yes. For the design process IT IS GOOD to talk of "responsibility for
behavior" (I think I forgot to point this out). It is (relatively) good
for the same reason that I also felt unhappy with it in another way:
Saying a T-object "has responsibility for doing X" = "requirement to do
X" = "should/is-supposed-to do X" [according to my linguistic feeling]
leaves open - and thus begs - the question: "And is each T-object
actually meeting this responsibility?" This unsoken question in the
back of our head [in my layman psychology's view] directs the
designer's attention at the contexts in which the object should/might
"do X" and at the possible cases of objects that might occur in these
context.

Another situations than design is where I want to consider a (usually
given) system at a particular (high) level of abstraction/detail: a
system of objects in which each object *is* doing something (so that as
a whole they satisfy the system's requirements), and I don't want to
have be worries directed at whether all object that are there are
constutated so that they will meet the expectations.
Maybe this is not a good/fair example, but anyway: In domain analysis,
eg., I would prefer not to describe Mammals as vertebra that "have the
responsibility to milk-feed their babies..." but rather as vertebra
that "[do] milk-feed their babies, period!".
And a classical example from the computational domain: One aspect of
the notion of a Stack could, eg., be described: When a Stack object
receives a "top()" message then it does/will return the latest pushed
and not yet popped element - not: ... then it has the responsibility
for returning the latest pushed and not yet popped element.


> ...
>
> This is why my main belief is that the best thing you can do for
> software engineering is to keep the non-technical talk about software
> alive up to the last nifty detail. Therefore the "semi-exact"
> terminology of "responsibility" provokes the exact right smell for me.
> It beats obsessive behavior to talk in less strict terms imho.

I am NOT disagreeing here. For software engineers, "responsibility" is
the term they should use (for as long as possible).

Ulf Schünemann

From: ulf on
H. S. Lahman wrote:
> ...
>
> You are correct, I oversimplified. One can also provide specialization
> by constraining a general specification. ...

Good: I was able to make you understand one of the points that
concerned me here. Another proof for the power of talking in examples.
(Actually I was looking for giving a good example for what I meant
already much early - where good example here is one which is not merely
an example of different algorithms implementing the same function. It
was your example of the search algorithms that sparked my inspiration.)


> To that extent, in our earlier discussion about whether subclasses
> modify/override superclass specifications, you were correct that they
> do. My hang-up was about modify/override. I think of those as different
> activities than constraining something. That is, in my view
> modify/override implies that one changes the nature of something while
> constraining it does not.

Yes, this can be confusing: I actually prefer Bertrand Meyer's term
"redefinition" over the established term "override" because it says
only: In the subclass a definition for something is given that was
defined already in a superclass. OOPLs demand only that the new
definition does not change the type of the defined. Because in this
generality this causes problems, Liskov & Wing estabilsh the principle
(LSP) that the new definition (the redefinition aka. overriding) is
just a constraining of the old one.


> >>>>... But in both cases the semantics of the
> >>>>responsibility is exactly the same from the perspective of an external
> >>>>client for each implementation pair.
> >>>
> >>>...
> >>
> > ... I needed ... the clarification that you were
> > not talking about the semantics of the responsibility as it was defined
> > in some higher level class, but the actual semantics which the
> > responsibility has in a particular concrete object implementing that
> > class.
>
> I only agree with that clarification in the specific context of a
> subclass constraining a more general superclass responsibility. And
> then I see the specification as fundamentally the same; it is just
> qualified in the subclass.

OK. Let me try again to rephrase and complete your original statement:

For all classes that have "inherited" a responsibility (to sort or to
calculate tax) from a superclass and define a valid implementation of
it,

- the semantics of this *responsibility* is *exactly* the same (since
in this case there is only *one* definition of this responsibility,
namely in the superclass)

- the externally visible semantics of the different *implementations*
in the different classes is the same as, or a
*concretization/constraining* of, the semantics of the responsibility
(because otherwise they would not be *valid* impementations)

I agree with that.


> That sort of specialization is actually a pretty unusual situation and
> most specializations are created by defining properties that are not
> shared by sibling subclasses. In those situations there is only one
> definition of the added properties.

Granted. But if "that sort of specialization" = constraining override
exists at the level of responsibility then some sort of "overriding"
reponsibilities is a possible operation in OOA/D and then I was right
to claim that the LSP (developed in the context of OOP) has sth to say
for OOA/D: In case you "override" a responsibility, then it should be
in a constraining way in order to satisfy the LSP <- This was proved by
Liskov & Wing purely at the specification level, independent from
possible implementations of original and constrained responsibility.


> <aside>
> Note that your List example probably would not even show up in an OOA/D
> model because classes like List usually just implement 1:* relationships
> at OOP time.

Yes. I was aware of that. While I'm confident there are also examples
in various problem domains, the hard thing is to find one that is easy
to explain, that is not better modeled as a case of parameterization,
and that does no raise to many other questions unrelated to the point.

> Most situations I have observed where specialization by constraint is
> necessary have been related to nonfunctional requirements where
> computing space implementations become important. Your List example is
> one of those where the relevant nonfunctional requirement is reuse of
> collection classes.

Aha... So would say that SortedListWithPreservedOrderOfEquals
is not conceptually a specialization of
SortedListWithoutGuaranteedOrderOfEquals
but just a specialization for the sake of reuse???

> ... there is a very thin line between
> constraining a specification and exposing an implementation. ...

Yes. It is problematic.
Of course it is better if you can design your class hierarchy without
constraining overrides of responsibilities (and without overrides of
implementations). But it can always happen that an aspect of a
responsibility/property was ignored, leaving the property
underspecified. For example it is often ignored what "sort" does if two
elements in the list are equal wrt. to the sorting order (this may be
an oversight, or it is simply not relevant in the current context).
Hence SortedListWithoutGuaranteedOrderOfEquals
is usually called just SortedList. But it can always happen later that
the ignored aspect becomes relevant. And then it must be possible to
extend the class hierarchy by a new subclass of the class with the
underspecified property, and make a constraining override of that
property/responsibility.


> > Abstract data is reducible to behavior. So if we're not talking about
> > the implementation of objects, they are all about behavior. Hence, yes,
> > LSP is about behavior and abstract data.
>
> I can't agree with the first sentence in a computing environment.
> Abstract day /may/ reduce to behavior (i.e., computation of a value) but ...

No objection here for "reduce" in the sense of "implement, realize,
represent".

I see I wasn't precise enough. What I meant was:
The abstract view of data (ie. the view without consideration of
representation of data) reduces [abstract] data to [abstract] behavior.
So in the abstract view of objects (the one that is used in OOA/D, the
one that ignores the implementation in the PL) we have abstract
behavior + abstract data reducible to abstract behavior => behavior
only
From: H. S. Lahman on
Responding to Ulf...

> H. S. Lahman wrote:
>
>>...
>>
>>You are correct, I oversimplified. One can also provide specialization
>>by constraining a general specification. ...
>
>
> Good: I was able to make you understand one of the points that
> concerned me here.

Alas, my understanding has not changed. All we have agreed to here is
that we had different semantics for 'override'.

>>Most situations I have observed where specialization by constraint is
>>necessary have been related to nonfunctional requirements where
>>computing space implementations become important. Your List example is
>>one of those where the relevant nonfunctional requirement is reuse of
>>collection classes.
>
>
> Aha... So would say that SortedListWithPreservedOrderOfEquals
> is not conceptually a specialization of
> SortedListWithoutGuaranteedOrderOfEquals
> but just a specialization for the sake of reuse???

I am saying they would be sibling subclasses of SortedList. My point
was that this situation would usually arise in the design of a utility
class library at OOP time. Such libraries are necessarily reusable.
But that sort of general reuse means that they must still be capable of
dealing with those situations where preserving the original order was
important, so one would have to provide subclasses that isolated that
constraint.

>>>Abstract data is reducible to behavior. So if we're not talking about
>>>the implementation of objects, they are all about behavior. Hence, yes,
>>>LSP is about behavior and abstract data.
>>
>>I can't agree with the first sentence in a computing environment.
>>Abstract day /may/ reduce to behavior (i.e., computation of a value) but ...
>
>
> No objection here for "reduce" in the sense of "implement, realize,
> represent".
>
> I see I wasn't precise enough. What I meant was:
> The abstract view of data (ie. the view without consideration of
> representation of data) reduces [abstract] data to [abstract] behavior.
> So in the abstract view of objects (the one that is used in OOA/D, the
> one that ignores the implementation in the PL) we have abstract
> behavior + abstract data reducible to abstract behavior => behavior
> only.

I still disagree with that because the ADT itself still represents the
knowledge as a scalar value, albeit an abstract one. Values are not
behavior.

As I indicated, it is also a very slippery slope in an OOA/D context
because (among other things) knowledge and behavior are treated quite
differently (e.g., knowledge is accessed synchronously while behavior is
accessed asynchronously). Knowledge and behavior are quite distinct in
an OOA/D context and they both have equal stature in OO abstraction.

>>In
>>fact, that is one of the compromises OOPL type systems make -- they
>>/always/ map knowledge attributes directly to data stores. One cannot
>>even declare knowledge that is always computed as an attribute in an
>>OOPL; such knowledge can only be defined with a getter function.
>
>
> In newer OOPLs like Delphi and C# you have a new kind of member called
> "property". From outside the object a "property" looks like an instance
> variable (aka. attribute), but a property can appear in an "interface"
> (while the latter can occur only in a "class") and a property is
> implemented by code to calculate the property's value (and optionally
> also code for [abstractly] changing a property to a new value). So if
> you map the modeling notion of "attribute" not to OOPL's instance
> variables but to "properties", then you have what you want.

What they are doing is getting around the getter/setter conundrum of
early OOPLs. The early OOPLs went overboard and mapped attributes
directly to memory storage types. That effectively exposed the
<hardware> implementation in the definition of the attribute. As a
result, whenever the hardware implementation changed, one had to change
the client implementations as well (or at least recompile them). So
developers quickly adopted getters and setters to isolate the hardware
implementation. That also allowed knowledge attributes to be defined
that were not memory data stores.

Unfortunately that presented a conundrum because one then had to declare
the knowledge attribute itself as private to force use of the
getters/setters, which implied it was not a public responsibility of the
object. The more modern OOPLs have overcome this by allowing syntax
where the attribute is declared and accessed publicly in all cases. In
those cases where the implementation needs to be modified or the
attribute is not a data store, the language provides syntax so that the
developer can provide "hidden" getters/setters that the compiler
substitutes behind the scenes. IOW, the modern OOPLs have finally
gotten things right and allowed access of public knowledge attribute
values such that their implementations can be encapsulated.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl(a)pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info(a)pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



From: ulf on
H. S. Lahman wrote:
> ...
>
> <stuff we mostly agree elided>
>
> > Do you agree with Liskov&Wing's suggestion?
>
> Only in a very limited sense in an OO context. As I indicated in the
> other message, LSP applies to knowledge responsibilities as well as
> behavior responsibilities. So the definition of knowledge attributes
> must be consistent. One can't define Rectangle with attributes for
> majorSideLength and minorSideLength and then derive a Square subclass
> that only defines sideLength.

Yes. Of course one cannot derive a class from Rectangle and not inherit
the major/minorSideLength properties/responsibilities-for-knowing.
But mathematically minded people will say: Rectangle has a
specialization
X which differs from Rectangle by the addition of the invariant
(constraint [property])
majorSideLength == minorSideLength.
But this means that "majorSideLength" and "minorSideLength" are now
just two names for the same length (the same property?), and
consequently the more appropriate name for it is now simply
"sideLength".

I see three positions towards adding an invariant in a subclass in
order relate two inherited responsibilites:
a. this is not allowed
b. the [preservation of the] invariant is a new responsibility of the
object
-> each public method of the object has to respect it on top of
respecting the behavioral responsibility it is implementing
c. the invariant is implicitly added to the pre- and postconditions in
the specifications of all behavioral responsibilities of X-objects -
this means in particular that all responsiblities inherited from
Rectangle are "overriden" in a constraining fashion. [This would then
be our second encounter with constraining overrides of
responsibilities]

My position is oscillating between b. and c.

> Beyond that, in addition to my response to (3), OO methods are very pure
> because of methodological constraints on the way they are constructed.
> Since they are intrinsic, self-contained, and logically indivisible, one
> can express their specification purely in terms of a transformation of
> input values to output values. It is that contractual definition that
> must be honored for all implementations to ensure substitutability.

Yes, *each* method/behavioral-property can be specified self-contained
and in terms of its own input-to-output transformation [at least for
the issue at hand].
But a class can say more about its instances than specify all their
self-contained
knowledge/behavior properties. It can also *relate* several of these
self-contained properties eg. by an invariant. Such an relating
invariant cannot be reduced to the self-constained specification of any
of the object's knowledge/behavior properties.

How do invariants fit into your presentation of OOA/D founded on
self-constained responsibilities?

> So if method A modifies attribute Z, then attribute Z is just an
> input/output value in the specification of method A's responsibility.
> The fact that Z happens to be a knowledge property of the same object is
> serendipity. The old value could just as well have in a stimulus
> event's data packet and the new value in an output event's data packet.
> That is, the semantics of Z don't matter to the specification of A;
> only the transformation of the value matters.

Yes. The semantics of each knowledge/behavior property of an object is
independent of the semantics of its other knowledge/behavior
properties.

> Thus the values themselves cannot affect substitutability adversely
> because they are already part of the specification of the responsibility
> transformation that provides the criterion for substitutability.

I thought over and over again why this is wrong, but I couldn't find
it. I think I know now why I cannot translate Liskov&Wing's
observations to specifications in terms of responsibilities as you
described them. Hence it seems you were right and I was wrong in this
point.

The crucial difference between Liskov&Wing's model-based formal
specifications and specifications in terms of responsibilities is the
treatment of data/knowing:

In model-based specifications, data/state-variables are passive. A
constraint on one variable or relating several variables is not
realized in a self-constained manner in the variable(s), but has to be
preserved by all operations manipulating the variable(s). Hence adding
an invariant adds sth to the specificiation of all methods (inherited
and in subclasses). This is why constraining (majorSideLength ==
minorSideLength) + mutators (SetMajorAndMinorLengths) is such a
dangerous combination [cf. the subthread on Ellipses/Circles with
constraint FocusA == FocusB and SetFoci].

In responsibility-based specifications, invariants can be added to the
[few] knowledge properties related by the invariant, instead of adding
them to all responsibilities for behavior mutating these knowledge
properties. Hence in class X, operation SetMajorAndMinorLengths(a,b)
may still have the unchanged inherited semantics of making
majorSideLength == a & minorSideLength == b. The responsibility to
ensure the invariant majorSideLength == minorSideLength lies in the
implementation of the responsibilties for knowing a majorSideLength and
a minorSideLength.

Is this the difference which you tried to explain to me all along?

This is wonderful: All the problems have dissipated. Square can be a
subclass of Rectangle (and Circle can be a subclass of Ellipse).

Well, not really. majorSideLength == a & minorSideLength == b is still
inconsistent with majorSideLength == minorSideLength, no matter who is
responsible. No implementation will be able to satisfy both. Square as
subclass of Rectangle would be legal, but would still have no
instances...


> >>... I was
> >>responding to your suggestion that substitutability could somehow be
> >>affected by the sequence of (history of) messages an object receives:
> >>...
> >
> > I want to reaffirm this. See B above for a rephrasing. I don't know how
> > otherwise to help you understand loss LSP-substitutability caused by
> > non-preservation of history properties.

Ok, I take this back. In the context of responsibility-based
specifications, the logical error does not occur when the client gets a
result conflicting with the assumed history property (because a state
variable was changed by a subclass behavior in a way not possible by
superc
From: H. S. Lahman on
Responding to Ulf...

>>>Do you agree with Liskov&Wing's suggestion?
>>
>>Only in a very limited sense in an OO context. As I indicated in the
>>other message, LSP applies to knowledge responsibilities as well as
>>behavior responsibilities. So the definition of knowledge attributes
>>must be consistent. One can't define Rectangle with attributes for
>>majorSideLength and minorSideLength and then derive a Square subclass
>>that only defines sideLength.
>
>
> Yes. Of course one cannot derive a class from Rectangle and not inherit
> the major/minorSideLength properties/responsibilities-for-knowing.
> But mathematically minded people will say: Rectangle has a
> specialization
> X which differs from Rectangle by the addition of the invariant
> (constraint [property])
> majorSideLength == minorSideLength.
> But this means that "majorSideLength" and "minorSideLength" are now
> just two names for the same length (the same property?), and
> consequently the more appropriate name for it is now simply
> "sideLength".
>
> I see three positions towards adding an invariant in a subclass in
> order relate two inherited responsibilites:
> a. this is not allowed
> b. the [preservation of the] invariant is a new responsibility of the
> object
> -> each public method of the object has to respect it on top of
> respecting the behavioral responsibility it is implementing
> c. the invariant is implicitly added to the pre- and postconditions in
> the specifications of all behavioral responsibilities of X-objects -
> this means in particular that all responsiblities inherited from
> Rectangle are "overriden" in a constraining fashion. [This would then
> be our second encounter with constraining overrides of
> responsibilities]
>
> My position is oscillating between b. and c.

I would argue that in an OO context (c) is always the answer. The
client must always satisfy the precondition for invoking the
responsibility. However, since attribute state variable values are
inputs to behavior responsibilities, the behavior precondition must
include any invariants of knowledge dependencies, such as
majorSideLength = minorSideLength. That is, the client is responsible
for ensuring conditions on the input variables prevail before invoking
the behavior responsibility. Thus the responsibility's internal
invariant on knowledge attributes becomes a precondition from the
perspective of the client.

[Apropos of our other subthread, this is another example of why
knowledge and behavior responsibilities are quite different in OOA/D.
Dependencies among state variable values can exist and the client must
ensure that they prevail before invoking a behavior responsibility.
OTOH, behavior responsibilities are constructed to ensure that they
never have dependencies, so such constraints become irrelevant.]

>>Beyond that, in addition to my response to (3), OO methods are very pure
>>because of methodological constraints on the way they are constructed.
>>Since they are intrinsic, self-contained, and logically indivisible, one
>>can express their specification purely in terms of a transformation of
>>input values to output values. It is that contractual definition that
>>must be honored for all implementations to ensure substitutability.
>
>
> Yes, *each* method/behavioral-property can be specified self-contained
> and in terms of its own input-to-output transformation [at least for
> the issue at hand].
> But a class can say more about its instances than specify all their
> self-contained
> knowledge/behavior properties. It can also *relate* several of these
> self-contained properties eg. by an invariant. Such an relating
> invariant cannot be reduced to the self-constained specification of any
> of the object's knowledge/behavior properties.
>
> How do invariants fit into your presentation of OOA/D founded on
> self-constained responsibilities?

Per the above, knowledge invariants are part of the precondition for a
behavior responsibility and they only apply to input state variable
values. In abstracting behavior responsibilities to be intrinsic and
self-contained the developer <methodologically> ensures that invariants
between behaviors do not exist.

>>So if method A modifies attribute Z, then attribute Z is just an
>>input/output value in the specification of method A's responsibility.
>>The fact that Z happens to be a knowledge property of the same object is
>>serendipity. The old value could just as well have in a stimulus
>>event's data packet and the new value in an output event's data packet.
>> That is, the semantics of Z don't matter to the specification of A;
>>only the transformation of the value matters.
>
>
> Yes. The semantics of each knowledge/behavior property of an object is
> independent of the semantics of its other knowledge/behavior
> properties.

A quibble, but technically this is not quite true. Consider Mass =
Volume * Density. If all three are defined as attributes, one has a
problem because Volume and Density might be updated with output values
from different behavior responsibilities. However, in OOA/D these
problem space invariants are treated specially by explicitly designating
the dependent and independent variables in the static description. That
signals the designer that referential integrity issues exist and must be
explicitly dealt with in dynamic description.

In practice the developer usually takes the easy way out and ensures
they are all updated within the behavior responsibility scope by
daisy-chaining <synchronous> knowledge setters. Alternatively the
designer can define an invariant for each behavior responsibility that
causes them to update the independent variable as well. Yet another
(much more risky) approach is to organize the overall flow of messages
such that referential integrity is not an issue (e.g., Mass is updated
after both Density and Volume have been updated but before Mass is
accessed by any other object).

However, I would view this sort of invariant as a problem space
invariant rather than an intrinsic dependency among behavior
responsibilities. That's because the designer can resolve referential
integrity issues outside the scope of individual responsibilities.

>>Thus the values themselves cannot affect substitutability adversely
>>because they are already part of the specific