[MUD-Dev] Object Models

Travis Casey efindel at earthlink.net
Tue Nov 28 00:47:24 New Zealand Daylight Time 2000

On Monday, November 27, 2000, KevinL wrote:
>>>> Travis Casey wrote
>> John Buehler <johnbue at msn.com> wrote:
>> > Travis Casey writes:

>> [snip explanation of "tight" vs. "loose" contracts]
>> >> Thus, changing what all a low-level component will do (e.g., to add a
>> >> "temperature" variable and make the "sharpen me" behavior take it into
>> >> account) doesn't mean that you'll necessarily have to change all the
>> >> code that uses that component.
>> > Loose contracts give you room to add behaviors, but it also limits the
>> > things that the client of a contract can rely on.  If you have a component
>> > that has temperature and a component that doesn't, yet the 'sharpen'
>> > contract makes no statement about the need for temperature information, you
>> > may get some unusual behaviors that you don't want.
>> I think this is getting to a different point than I was talking about.
>> Let me see if I can be clearer:
>> A contract specifies what must be supplied and what must be returned
>> -- but there's nothing in the idea of a contract that requires that
>> everything that is supplied be explicitly passed as a parameter.  This
>> leaves room for later revision of contracts without necessarily
>> changing the code interfaces.

> Ah, but I've never seen a contractual system that allows for stating 
> requirements on the "client" or user in that way - it's non-enforceable, you 
> can't stop an object that doesn't live up to the contract from attempting to 
> use the component (in any language I'm aware of).

I think maybe you've misinterpreted what I was writing.  The only
requirement the contract makes is that the "client" pass in valid data
-- that is, two objects and a value that make sense in the context.
That doesn't mean that the method will have no error-handling code --
only that if the client doesn't live up to its end of the contract,
the method doesn't guarantee results.

It's no different than, say, a "divide" method saying that the divisor
must be non-zero.  If it isn't, the divide method should return an
error rather than crash -- but you certainly can't expect it to return
a valid answer.

> John's suggesting something much tighter, I suspect - which is where he and I 
> part company.  I am a believer in a looser coding style, possibly closer to 
> the XP stuff, in cases where code is expected to change frequently and you 
> don't want to have to go back and touch everything to change a component.

> In muds, especially in "under development" muds (as opposed to all those 
> stable static muds that have finished being worked on *grin*), code _does_ 
> change, and in ways like my example - adding new skills that rely on new 
> attributes, adding new stats to objects, so on and so forth.  My approach to 
> this has been simply to write code that attempts to use the new attributes/
> methods/whatever but does not bomb out if they're not available, and more 
> importantly allow for a default "flow-through" mechanism in the form of 
> inheritance, so attributes can be added to _everything_ (or a reasonable 
> subset thereof) very quickly.

As I say above, nothing in contracts requires there to be no
error-handling code.  For that matter, you can specify error-handling
in the contract -- for example, say that "if the object passed for a
sharpening tool does not have a valid 'sharpening-assist' value, a
value of zero will be used instead".  Or, "if the object passed as the
'sharpener' does not have a valid 'sharpening' skill, the sharpen
routine will follow the skill tree up and look for a skill from which
'sharpening' can default.  If no default is found, a skill value of
zero will be used."

The idea of contracts is not that they ensure that no one will ever
make a mistake -- rather, the idea is that they provide clear
documentation of what a routine expects as input, what it will return,
and what it does.  When you get down to it, it's nothing more than a
fancy, more spelled-out form of preconditions and postconditions.

> I never saw a clear answer to this, but I believe if you have a blade 
> component and you add an isSharp() method, you're going to have to revisit 
> every object that uses that component and explicitly deal with that addition - 
> _or_ you're going to have to find every place that might check if an object's 
> sharp and refer to object->blade->isSharp().  The first concerns me because of 
> the level of upkeep for what I'd consider common operations, the second 
> concerns me because it breaks the "what if we've got two blades" assumption of 
> components somewhat - you're breaking down the object-ness of objects by 
> making assumptions for them about their structure.

This is a problem with changing the simulation, though, not a problem with
components or contracts.  The same thing is true if you have a blade
object and add an isSharp() method, or have lots of blades that have
been created by cutting-and-pasting code -- if you decide to add the
possibility that a blade isn't sharp, you have to add something to
check whether or not a blade is sharp everywhere that it's appropriate
regardless of what coding paradigm you're using.

What you're saying seems to me to be the same thing that I said in my
first message on this thread -- that it's not changing what an
existing function does that tends to cause problems, but rather when a
new function is added that things that have already been coded should
have used.

In the first situation, you can generally find most of the places that
need to be changed with a simple search -- do a grep for the name of
the function/method or such as that.  In some cases, you may even be
able to automate rewriting the function to use the new interface.

In the second situation, though, the only real way to do it is to
hand-review the code -- you can't do an automatic search for something
that *should* have been there, but isn't.  (Well, I suppose it's
possible in theory, but if you know how, you should be writing your
doctoral thesis with it right now.  :-)  That's the painful part.

> The more I think about this, the more I'm convinced that components have their 
> place, but probably with loose contracts - which'll make John cringe ;).  
> Tight contracts are _one_ approach to getting good code - but they are, to me, 
> an approach that follows the previous attitudes of "make it more explicit, 
> make it harder rules, and it'll be more likely to be right", which I don't 
> believe.  Loose contracts and loosely-typed scripting, a la python, combined 
> with heavy/fast testing cycles a la extreme programming, may be a more 
> suitable approach for muddish coding, maybe ;)

Personally, I don't believe that there's any "ideal" approach --
different things work for different people.  For a book which has a
good sampling of a lot of ideas for better programming, I'd recommend
_The Pragmatic Programmer_ by Hunt and Thomas.  They have 70 different
tips for better programming; whatever your programming style, you'll
probably find some that you can incorporate.

       |\      _,,,---,,_    Travis S. Casey  <efindel at earthlink.net>
 ZZzz  /,`.-'`'    -.  ;-;;,_   No one agrees with me.  Not even me.
      |,4-  ) )-,_..;\ (  `'-'
     '---''(_/--'  `-'\_)      

MUD-Dev mailing list
MUD-Dev at kanga.nu

More information about the MUD-Dev mailing list