Boost logo

Boost :

From: Thorsten Ottosen (nesotto_at_[hidden])
Date: 2004-11-18 20:03:13


"David B. Held" <dheld_at_[hidden]> wrote in message
news:cnje4t$j7j$1_at_sea.gmane.org...
| Thorsten Ottosen wrote:
|
| > "David B. Held" <dheld_at_[hidden]> wrote in message
| > news:cngf20$cmb$1_at_sea.gmane.org...
| > [...]
| > | If not, are you suggesting that we construct
| > | such types partially, and then fill in the rest later, so that we can
| > | avoid functions with many arguments?
| >
| > that's pretty close.
|
| That's a pretty silly policy. What Noah means by "one shot" is that
| some classes have little things called "invariants", and deferring
| initialization till after the c'tor may well break those.

I doubt that is what he meant.

If you have aguments that are optional, then one should be able to set those
later. Not providing them
cannot possible break an invariant.

A major reason for using classes instead of tuples of data is that now one can
start controlling invariants.

| > [...]
| > function leafNode2( $name1, $name2, $link1, $link2,
| > $target1 = selfTarget, $target2 = selfTaget,
| > $title1 = "", $title2 = "", $root = root );
| >
| > I'm not proud of it, but I just havn't had the time to refactor it yet. It
| > should be obvious that all alguments ending with 1 and 2 should be grouped
| > somehow, maybe as
| >
| > class Link
| > {
| > $name,
| > $url;
| > $target = selfTarget;
| > $title = "";
| > ...
| > }
|
| Maybe so. But suppose you didn't need to pass two links? Then is it
| so obvious that those arguments need to be encapsulated into a single
| *EXTRA* class?

It depends, I guess. I think in this case it would be a good candidate.

| > [...]
| > Doing such a refactoring takes time, but it will surely make the
abstraction
| > level of my application much higher.
|
| That does not imply a benefit to me. If it did, then I would argue
| that a pointer-to-a-pointer is better than a pointer, because it has
| a higher level of abstraction (and gives you more things to do with
| the intermediate pointer).

that is not the type of abstraction we're talking about

| By that reasoning,

by your reasoning :-)

| a pointer-to-an-X+1 is
| "better" than a pointer-to-an-X, and therefore, we should all use
| pointers that have an infinite level of nesting, to write truly divine
| code. Of course, the flaw in this argument is the notion of
| "overgeneralization."
|
| > Just for fun, I would like to see what functions you have which you claim
| > should not be refactored. Maybe you're right that doing a refactoring
| > would simply ont be good.
|
| Consider *gasp* data entry applications, where you have records with
| many fields, which should all be initialized at once. Here's one for
| some mail tracking software:
|
| bool TInputForm::Add(AnsiString ID, double Weight, AnsiString Zip,
| byte Zone, AnsiString Extra, bool MakeVisible);
|
| Now, you might say: "But you should put all those arguments in some
| kind of package class before passing it to the Add() function."

why not only those that tend to go together; it might not be all.

| However, that is exactly the *point* of the Add() function! These
| pieces of data are collected elsewhere, and it is the responsibility
| of the Add() function to integrate them into a single package object.
| Yes, it is possible to refactor it into classes that only take a few
| fields at a time, but I can guarantee that such a refactoring will
| not be an improvement.

how can you guarantee that? What is the abstraction that add() package
the items together in? Why shouldn't one be able to pass the data around in a
that "package" form?

>From what I can infer then it's not impossible that one could change the
function into

class Package
{
   AnsiString ID;
   ...
   public:
      set_id( AnsiString const& r );
};

bool TInputForm::Add( const Package& p, bool MakeVisible );

| I could give examples from lots of other
| business apps that need to deal with records, some that have far
| more fields than this.
|
| void __fastcall TInputForm::OnInsert(int ManifestID,
| MDTP::TMailClass Class, AnsiString ID, double Weight,
| AnsiString Zip, byte Zone, double Postage, AnsiString Extra,
| AnsiString Username, TDateTime Entered);
|
| Here's an event handler that gets called *before* the Add() function
| above, and thus gets the raw data, and not the packaged object.
| It wouldn't even make sense to refactor this function. There are
| no sensible defaults, since the data is always supposed to be passed
| from the data entry source. There are 10 arguments, for none of which
| I make apology.

I'm not apologizing for all the bad code I have written...but I am trying to
do better the next time.

| It does not now nor did it ever occur to me to attempt
| to reduce the number of arguments for this function. Doing so would
| not improve the quality of the code, because there is absolutely no
| reason to generalize this function. It has a very specific and
| proscribed purpose, and there is no anticipation of needing to expand
| its repertoire.
|
| Now, in this case, we are looking at functions that only get called
| from a small number of places (or even just one). Thus, named
| parameters don't really help any. But the fact is, long argument
| lists are not intrinsically evil.

are they intrinsically good?

| I could give numerous other
| examples, but I hope I've made my point.

Thinking more about it I can see that the database layer I have in an
application is just
a lot of functions with many arguments. So I can find many example too.

If I had encapsulated this in classes, what would the effect be? Well, I had
to do this:

1. pack the values in an object
2. send that object along to a function
3. unpack the data from the object inside the function

And I guess that is what you think is plain stupid.

My point has been, however, that once you start making abstractions you

1. simplify interfaces
2. makes it possible to define invariants for your data and hence localize
error-checks
3. adds type-safety to the application

(but I guess we don't have to go through the benefits of OO here.)

So if I were to do it all over, I would probably map my database tables to
classes instead of passing
tuples of strings and intergers around.

-Thorsten


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk