The Artima Developer Community
Sponsored Link

Flexible Java by Bill Venners
To Bean or Not to Bean
An E-Mail Debate

Advertisement

Advertisement

How do you know when to make a class into a JavaBean? Should all classes be JavaBeans? This page attempts to shed some light on vital questions such as these.

Here you will find the text of an e-mail debate between Mark Balbes, Mark Johnson, and myself. The debate took place over the course of a few days in April, 1998.

If you have your own opinion on this topic, feel free to post to the relevant Flexible Java Forum topic.

Now, let's listen in...

Subject: JavaWorld: Object Initialization
Date: Tue, 14 Apr 1998 14:27:04 -0700
From: Mark Balbes
To: Bill Venners

I enjoyed your JavaWorld article on object construction and initialization. One rule that I found particularly interesting was that you should not give a no-arg constructor if there is no default value that makes sense. This flies in the face of the JavaBeans specification that says that an object must have a no-args constructor. I've been wrestling with the notion of whether all my classes should be beans-compliant. Your rules seem to argue otherwise.

Subject: Re: JavaWorld: Object Initialization
Date: Tue, 14 Apr 1998 18:00:12 -0700
From: Bill Venners
To: Mark Balbes, Mark Johnson

Mark,

I enjoyed your JavaWorld article on object construction and initialization. One rule that I found particularly interesting was that you should not give a no-arg constructor if there is no default value that makes sense. This flies in the face of the JavaBeans specification that says that an object must have a no-args constructor. I've been wrestling with the notion of whether all my classes should be beans-compliant. Your rules seem to argue otherwise.

Why would you want to make all your classes beans-compliant? What are the pros and cons as you see them?

I would think that you'd only make into beans classes that are going to be published (either internally or externally) as libraries, which would be used in a Bean Builder tool.

I'm also sending this to Mark Johnson, the JavaBeans columnist for JavaWorld. What do you think mj?

bv

Subject: Re: JavaWorld: Object Initialization
Date: Tue, 14 Apr 1998 21:30:47 -0400
From: Mark Balbes

Bill

My company is just now embarking on Java programming. We've got a bunch of C programmers who will have to be trained in Java. For now, I'm the only one whose had any experience. (And I'm a physicist by training, not a software engineer.)

We're using Visual Cafe for our development environment. The advantages to coding to the JavaBeans standard are (in no particular order):

  • It gives us a coding standard against which we can test our classes. (It has to work in the V. Cafe RAD environment)
  • Our novice Java programmer's can use the V. Cafe RAD environment to do their programming which will help them to quickly learn the Java language.
  • Since the JavaBeans specification is simply an enforced coding convention, I don't see a reason not to do it, unless there is a good design reason in a specific case.

I haven't yet come across a case that I couldn't come up with a default constructor. It may have been somewhat arbitrary, but the developer using the bean will see that it's not what they want and will use their RAD tool to set the right property. It may take two lines of code rather than one to do the same thing, but for a novice Java programmer it's certainly the easier route. The more experience programmer who isn't using the RAD environment can still call a constructor that takes an argument.

I see our development breaking down into hierarchical levels of beans. The lowest level beans are used to build higher level beans which are then used to build the application. Perhaps my view is colored by my experience with AVS Express which is a completely visual building environment, much like Java Beans. By building components out of lower level components , you always have a (hopefully) easy-to-understand graphical interpretation of what you are looking at.

Mark

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 09:27:17 -0700
From: Bill Venners

Mark,

My company is just now embarking on Java programming. We've got a bunch of C programmers who will have to be trained in Java. For now, I'm the only one whose had any experience. (And I'm a physicist by training, not a software engineer.)

We're using Visual Cafe for our development environment. The advantages to coding to the JavaBeans standard are (in no particular order):

  • It gives us a coding standard against which we can test our classes. (It has to work in the V. Cafe RAD environment)

OK.

  • Our novice Java programmer's can use the V. Cafe RAD environment to do their programming which will help them to quickly learn the Java language.

Yes, working with beans in a RAD IDE is more gentle than straight coding.

  • Since the JavaBeans specification is simply an enforced coding convention, I don't see a reason not to do it, unless there is a good design reason in a specific case.

I haven't yet come across a case that I couldn't come up with a default constructor. It may have been somewhat arbitrary, but the developer using the bean will see that it's not what they want and will use their RAD tool to set the right property. It may take two lines of code rather than one to do the same thing, but for a novice Java programmer it's certainly the easier route. The more experience programmer who isn't using the RAD environment can still call a constructor that takes an argument.

This makes sense too.

I see our development breaking down into hierarchical levels of beans. The lowest level beans are used to build higher level beans which are then used to build the application. Perhaps my view is colored by my experience with AVS Express which is a completely visual building environment, much like Java Beans. By building components out of lower level components , you always have a (hopefully) easy-to-understand graphical interpretation of what you are looking at.

So the low level beans form a low-level library, the middle level another library, and so on. That's kind of how I think of beans. The more advanced members of the team are creating libraries of beans for all to use. The less savvy Java programmers will have an easier time working with the libraries because they can manipulate the beans in the RAD tool (as can the advance programmers).

I'm curious how it will go when you get to the top levels of your hierarchy. I would like to talk in my next book (Flexible Java) about when and when not to beanify a class, so your experience could help inform my advice. Please let me know how it goes.

bv

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 13:52:28 -0600
From: Mark Johnson

This is a topic for an future Beans column. It will be called, of course, "To Bean Or Not To Bean" *. Mark, any input you can provide would be valuable.

Personally, I can't think of many cases where you wouldn't want to "beanify" your classes. The JBS (Java Bean Specification) is so lightweight that I'd think most projects would want to specify Beans-compatibility as a starting point for a coding standard, extending it for the local requirements. The only real requirements for a bean are:

1. 0-arg constructor
2. Serializable

That's it. What this means is, any time you want a class to be not serializable, or not have a 0-arg constructor, you've got a class that shouldn't be a Bean. Everything else would be.

Actually, there seems to be quite a bit of confusion on this topic. I'd certainly be interested in hearing about negative effects of beanification, since I can't think of any. Let's have a look at the issues:

1. Accessor nomenclature ("design patterns" -- sheesh)

The set/get naming convention for state accessors is as good a convention as any. Hiding state change and access with accessor methods is an excellent idea. (If I had it my way, I'd do away with public fields entirely, but that would mean changing Java semantics.)

2. BeanInfo

No need to provide this if the class won't be used in a builder. On the other hand, the Introspector creates a BeanInfo by default for any class that conforms to bean coding standards.

3. Event listener signatures

There's no real work here: all this means is, use JDK1.1 event handling in the way it was intended. The major exception I see here is that you may want to have nonstandard event listener signatures in addition to the standard ones (if you have something special going on.)

4. Customizer

Again, no need to provide if the class won't be used in a builder. Nonetheless, builders create default customizers by aggregating properties' property editors. It's probably a good idea to have a default property editor for each "builder-editable" type, so that this works properly.

5. Bound and constrained events

Again, this is simply proper use of the JDK 1.1+ event structure.

6. Serialization

Addressing serialization concerns up-front is a lot cheaper than adding it later. If you don't want to serialize, make that decision at design-time. Adding "implements Serializable" to a class costs nothing, and makes a class a potential bean (on down the road, if not right now.)

7. 0-arg constructors

Avoiding superfluous 0-arg (in C++, "default") constructors is a common O-O guideline, one that I personally think is overrated. In fact, there are cases in C++ where this rule actually doesn't make sense. One example is when you're using something like STL to manage a collection of objects. If you want to create a nonempty collection of objects, which constructor do you use? A default-constructed object can be considered equivalent to a null object, which is what you get if you construct

        Vector v = new Vector(10);
in Java: ten null object references. In C++, you'd get 10 default-constructed objects. This is related to the other C++ issue: there's no such thing as a null object (or null object reference) in C++, only NULL pointers. Why not use pointers? Pointers have their place in C++, but they also have weaknesses that instances (or references to instances) can address. (How do you reference count a pointer? And don't say, "smart pointers". Smart pointers aren't pointers, they're objects.)

In Java, since null references exist and pointers don't, these arguments are irrelevant. The discussion of the need for 0-arg constructors is language-bound. I'm not currently clear on why Beans need 0-arg constructors, but I think it has something to do with instantiation. Good research topic for me.

The JBS says that not every class should be a bean, because only some objects have a visual representation that makes sense. Personally, I think that making that statement overemphasizes the "visual" aspect of beans. (The JBS is old.) Server-side beans usually have no user interface at all, and may need none to be configured. java.beans.Customizer is *NOT* a user interface! It's a deferral of customization to another object, which is USUALLY a GUI, but might well be something else.

The real power of beans isn't that they're visual. It's in the dynamic decisions you can make at runtime about what to do with objects based on the features (event sets, properties, etc.) they expose. It's a framework for object metadata.

And now, a reading from the Book of Java:

The Beanitudes (be-AN-i-tudes)

Blessed are those who use accessors,
For their internal state shall remain private.
Blessed are those who follow naming conventions,
For their attributes shall be appropriately exposed.
Blessed are the event listeners,
For they shall be notified.
Blessed are the Serializable,
For they shall be persistent.
Blessed are the providers of BeanInfo,
For they shall be general in their introspection.
Blessed are the Customizers,
For they shall be configured.

--mj

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 14:03:13 -0700
From: Bill Venners

mj,

Sheesh Mark (Johnson), you should write a book.

The other Mark will now be burdened by endless e-mails about when to Bean and when not to Bean.

Let me make a few comments.

This is a topic for an future Beans column. It will be called, of course, "To Bean Or Not To Bean". Mark, any input you can provide would be valuable.

Personally, I can't think of many cases where you wouldn't want to "beanify" your classes. The JBS (Java Bean Specification) is so lightweight that I'd think most projects would want to specify Beans-compatibility as a starting point for a coding standard, extending it for the local requirements. The only real requirements for a bean are:

1. 0-arg constructor
2. Serializable

That's it. What this means is, any time you want a class to be not serializable, or not have a 0-arg constructor, you've got a class that shouldn't be a Bean. Everything else would be.

I'm going to make a case that one might think a bit differently about designing a class such that its instances are JavaBean instances as opposed to plain old Java objects. (mj, are the classes Beans or the instances? I think a class, not an object, can be a Bean. Right?)

Actually, there seems to be quite a bit of confusion on this topic. I'd certainly be interested in hearing about negative effects of beanification, since I can't think of any. Let's have a look at the issues:

1. accessor nomenclature ("design patterns" -- sheesh)

The set/get naming convention for state accessors is as good a convention as any. Hiding state change and access with accessor methods is an excellent idea. (If I had it my way, I'd do away with public fields entirely, but that would mean changing Java semantics.)

I agree. Should use this anyway. But I don't see this as just Bean naming convention, but a Java naming convention. Set/Get nomenclature is mentioned in the Java Language Spec absent any reference to Beans.

2. BeanInfo

No need to provide this if the class won't be used in a builder. On the other hand, the Introspector creates a BeanInfo by default for any class that conforms to bean coding standards.

True. Don't need it. Wouldn't want to force programmers to write a BeanInfo for every class.

3. Event listener signatures

There's no real work here: all this means is, use JDK1.1 event handling in the way it was intended. The major exception I see here is that you may want to have nonstandard event listener signatures in additiojn to the standard ones (if you have something special going on.)

Agreed. If you are designing a class whose objects will be generating events, you should follow the JDK1.1 event model and naming conventions.

4. Customizer

Again, no need to provide if the class won't be used in a builder. Nonetheless, builders create default customizers by aggregating properties' property editors. It's probably a good idea to have a default property editor for each "builder-editable" type, so that this works properly.

Cool.

5. Bound and constrained events

Again, this is simply proper use of the JDK 1.1+ event structure.

Yup.

6. Serialization

Addressing serialization concerns up-front is a lot cheaper than adding it later. If you don't want to serialize, make that decision at design-time. Adding "implements Serializable" to a class costs nothing, and makes a class a potential bean (on down the road, if not right now.)

I tend to agree with you on this point too.

7. 0-arg constructors

Avoiding superfluous 0-arg (in C++, "default") constructors is a common O-O guideline, one that I personally think is overrated.

Now here is where I must beg to differ. Unfortunately, my opinion on this matter would take a long time to describe. Fortunately, I already described it and published it at JavaWorld. Unfortunately, that was the article that got this whole thing started in the first place. mj, if you haven't read it (I have, of course, dutifully read all of your articles. At least I have often wanted to dutifully read all of your articles.), the URL is:

http://www.javaworld.com/jw-03-1998/jw-03-techniques.html

My main point against adding superfluous no-arg constructors is that they make a class harder to understand and use if they force that class to be at times "invalid." The question in my mind then becomes, What's easier for Mark B's team to understand: A class that isn't a bean that doesn't have any invalid states, or a class that has invalid states but is a bean?

In fact, there are cases in C++ where this rule actually doesn't make sense. One example is when you're using something like STL to manage a collection of objects. If you want to create a nonempty collection of objects, which constructor do you use? A default-constructed object can be considered equivalent to a null object, which is what you get if you construct

         Vector v = new Vector(10);
in Java: ten null object references. In C++, you'd get 10 default-constructed objects. This is related to the other C++ issue: there's no such thing as a null object (or null object reference) in C++, only NULL pointers. Why not use pointers? Pointers have their place in C++, but they also have weaknesses that instances (or references to instances) can address. (How do you reference count a pointer? And don't say, "smart pointers". Smart pointers aren't pointers, they're objects.)

In Java, since null references exist and pointers don't, these arguments are irrelevant. The discussion of the need for 0-arg constructors is language-bound. I'm not currently clear on why Beans need 0-arg constructors, but I think it has something to do with instantiation. Good research topic for me.

Yes, I too wonder why beans require no-arg constructors in the first place. If you find out mj, let me know. And by the way, the default objects you mentioned in your C++ example likely have that thorny "invalid" problem. They exist, but they can't be used until the program does something else to them to make them useful, such as call an "initialize" method.

The JBS says that not every class should be a bean, because only some objects have a visual representation that makes sense. Personally, I think that making that statement overemphasizes the "visual" aspect of beans. (The JBS is old.) Server-side beans usually have no user interface at all, and may need none to be configured. java.beans.Customizer is *NOT* a user interface! It's a deferral of customization to another object, which is USUALLY a GUI, but might well be something else.

The real power of beans isn't that they're visual. It's in the dynamic decisions you can make at runtime about what to do with objects based on the features (event sets, properties, etc.) they expose. It's a framework for object metadata.

Yes, I agree that beans aren't about being visual, but I would say they are about being able to be strung together in a visual IDE. Isn't that really the whole point of them? To try and make software components. To try and make chunks of software act more like discrete hardware chips.

See, I think what the coding standard for classes should be overlaps lot with JavaBeans, and will end up turning a lot of (probably most) classes into beans, but doesn't force a class into a bean that doesn't fall naturally and gracefully into beanhood.

And finally, I mentioned I would make a case that one might think a bit differently about designing a class such that its instances are JavaBean instances as opposed to plain old Java objects.

When I think of designing a class that is going to be a bean, I think about its external interface in terms of properties, events, and methods. The properties are attributes of the class that I want to expose to the bean builder. When I think of designing a class that isn't necessarily going to be a bean (but may be one by its very nature), I think about its external interface more in terms of services (which I will expose as methods). I would be inclined to design a class such that it has fewer set/get methods when I am thinking in terms of services, to keep my inner state a bit more hidden to clients. That's the difference in mindset that I have about designing a class that is definitely going to be a bean and used in a builder as opposed to a class that may happen to be a bean.

I published a bit about this topic as well, in my article "Impressions of SD" (about the Software Developer Conference)

http://www.javaworld.com/jw-03-1998/jw-03-sd98.venners.html

If you want to check that out, read just the sections starting with "Get/Set: Thumbs up."

And now, a reading from the Book of Java:

The Beanitudes (be-AN-i-tudes)

Blessed are those who use accessors,
For their internal state shall remain private.
Blessed are those who follow naming conventions,
For their attributes shall be appropriately exposed.
Blessed are the event listeners,
For they shall be notified.
Blessed are the Serializable,
For they shall be persistent.
Blessed are the providers of BeanInfo,
For they shall be general in their introspection.
Blessed are the Customizers,
For they shall be configured.

And now, a reading from the Book of My Own Opinions.

The Bill's-Attitude

Enter through the narrow gate. For wide is the gate and broad is the road that leads to bad design, and many may enter through it. But small is the gate and narrow the road that leads to good design, and only a few find it.

bv

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 17:02:19 -0600
From: Mark Johnson

That's it. What this means is, any time you want a class to be not serializable, or not have a 0-arg constructor, you've got a class that shouldn't be a Bean. Everything else would be.

I'm going to make a case that one might think a bit differently about designing a class such that its instances are JavaBean instances as opposed to plain old Java objects. (mj, are the classes Beans or the instances? I think a class, not an object, can be a Bean. Right?)

Well, when someone says "a bean does so-and-so", they're either talking about the class ("a bean has a FrobEventListener interface") or about an instance ("a serialized bean lives in JAR"). A bean *is* a plain old java object, with the additional requirements that the object have a zero-arg constructor *and* implement java.io.Serializable. Any object that has both those traits is by definition a bean. Now, its being a bean may not buy you anything, since what makes beans interesting are their properties, event sets, etc. The degenerate case of a bean is

class ImABean implements java.io.Serializable { }

It has a default no-arg constructor, it's serializable, it has one read-only property (class, since getClass() exists), and just a few methods (whatever it inherits from java.lang.Object; clone(), toString(), and so on.)

2. BeanInfo No need to provide this if the class won't be used in a builder. On the other hand, the Introspector creates a BeanInfo by default for any class that conforms to bean coding standards.

True. Don't need it. Wouldn't want to force programmers to write a BeanInfo for every class.

Right. java.beans.Introspector.getBeanInfo() whips up a bean info from reflection data if the object doesn't provide one.

Unfortunately, that was the article that got this whole thing started in the first place. mj, if you haven't read it (I have, of course, dutifully read all of your articles. At least I have often wanted to dutifully read all of your articles.),

Pathetic, isn't it? I think we're in precisely the same spot here. I'll check out your article.

Yes, I too wonder why beans require no-arg constructors in the first place. If you find out mj, let me know.

I will, but it will be in my column.

And by the way, the default objects you mentioned in your C++ example likely have that thorny "invalid" problem. They exist, but they can't be used until the program does something else to them to make them useful, such as call an "initialize" method.

In C++ there's really no way around it. I'm not sure even templates would work, since you can't make argument lists into single parameters, and therefore you couldn't create a collection class that requires different constructor signatures. You can't do parameterized types that way! (You might be able to do something evil with the preprocessor, I suppose.) There's probably a pattern to do this cleverly, but that's one of the things that I think makes C++ an inferior language to many others. Excuse me, but you shouldn't have to go writing class after class (or inheriting from a common base class) in order to do something as simple as reference counting.

java.beans.Customizer is *NOT* a user interface! It's a deferral of customization to another object, which is USUALLY a GUI, but might well be something else.

The real power of beans isn't that they're visual. It's in the dynamic decisions you can make at runtime about what to do with objects based on the features (event sets, properties, etc.) they expose. It's a framework for object metadata.

Yes, I agree that beans aren't about being visual, but I would say they are about being able to be strung together in a visual IDE. Isn't that really the whole point of them? To try and make software components. To try and make chunks of software act more like discrete hardware chips.

No, that's my point. Visual building is the sexiest and most attractive feature to developers because it's easier than coding. But, as I was saying, visual building is only one benefit, albeit a major one, of being a component. Let's use the IC example. It's a major benefit that ICs are convenient little boxes that we can plug together in our CAD systems instead of having to rebuild all that stuff from scratch every time. But another benefit is that people can clone an IC's "API" (Cyrix) and provide drop-in competition and perhaps performance improvements. That has to do with specifications and interface, not how the thing handles once you've got one in hand.

Why do ICs conform to common packaging standards? Why does one 14-pin dip (or 384-pin ZIF) look exactly like another? The environment the chips live in can be simplified because you can assume that all chips have certain characteristics. That's why circuit boards are covered with little rectangles instead of looking like a bowl of Lucky Charms. Likewise, your software framework (information bus or whatever) can be simplified if you require participants to have certain features. You must be Serializable, instantiable, etc. to play.

Also, the standardization of what the signatures in a class file mean allows systems _other than_ GUI builders to be smart in using classes. Let's say you have an applet that scans a local directory, loads all the beans it finds, lays them out, and displays them. You might implement those beans' customizers, as both design-time elements and as data input dialogs. In your applet, it becomes obvious what to do. Click on Cancun and you get the worksheet for Cancun vacations (cancun.ser). Click on Mars and you get the worksheet for Mars commercial flight (still not a tourist destination). So, to add Paris, you (as a developer) create an instance of DestinationBean, set the City property to Paris (this property only appears at design time), drop an image on it, etc., then serialize the bean and drop it into your input directory. Hit reload, and the application _handles adding the new content to itself_. This is a use of "beany" behavior at runtime. It's an architectural use of the component, as opposed to a design-time benefit.

When I think of designing a class that is going to be a bean, I think about its external interface in terms of properties, events, and methods. The properties are attributes of the class that I want to expose to the bean builder. When I think of designing a class that isn't necessarily going to be a bean (but may be one by its very nature), I think about its external interface more in terms of services (which I will expose as methods). I would be inclined to design a class such that it has fewer set/get methods when I am thinking in terms of services, to keep my inner state a bit more hidden to clients. That's the difference in mindset that I have about designing a class that is definitely going to be a bean and used in a builder as opposed to a class that may happen to be a bean.

Yeah, this is an excellent point. However, I'm still not quite convinced there's any difference. I'm suggesting you not think in terms of services, and not think in terms of exposing "to a bean builder", but simply exposing your services and properties to whomever. Whomever may be a builder, or it may be some architectural component of your system that's responsible for figuring out how *dynamically* to connect components to achieve some goal.

Also, "properties" too often degenerates into thinking in term of "fields". It's habitual thinking for a designer to do something like this:

        private int r, g, b;
        int getR() { ... }
        void setR(int R) { ... }
        int getB() { }
        void setB(int B) { }
and so on, ad nausaeum, instead saying what you *mean* (instead of what you *did*):
        private int r, g, b;

        Color getColor()
        void setColor(Color color);

Just because there's a data member there is no reason to insist on writing an accessor for it. I guess my point is that it's important to do good semantic analysis on your objects, beans or otherwise. What is a method but a "service"? Why should beans' inner state be any more visible than any other services'? In fact, with a Bean, maybe it would be a good idea if the only way to change internal state is (1) to use the customizer, or (2) exercise one of the methods of the object's "useful" API. (or (3) deserialize, I guess.)

Although I sound convinced about this (that there's no difference from a design standpoint between a bean and a "plain ole" class), I'm not. This is right up the alley of your design column, and I'm looking forward to hearing your response. I'd like to be clearer on this topic myself (esp. before I try writing a column about it!)

The Bill's-Attitude

Enter through the narrow gate. For wide is the gate and broad is the road that leads to bad design, and many may enter through it. But small is the gate and narrow the road that leads to good design, and only a few find it.

A-men. aaaaah-hunh. YES, Brother!

--mj "Der Bohnemeister"

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 19:59:14 -0400
From: Mark Balbes

My main point against adding superfluous no-arg constructors is that they make a class harder to understand and use if they force that class to be at times "invalid." The question in my mind then becomes, What's easier for Mark B's team to understand: A class that isn't a bean that doesn't have any invalid states, or a class that has invalid states but is a bean?

Perhaps it's my limited experience with OO programming, but I don't see why, in most if not all cases, you can't create a no-arg constructor that puts the bean in a valid state. It may not be the state that the user wants, but they then have the option of changing the state.

If I was designing a class and decided that there was absolutely no way to make a no-arg constructor that also put the object in a valid state, I would opt to not include that constructor.

Incidentally, I believe the reason for requiring the no-arg constructor is so that someone in an interactive development environment can point to an object, say "I want one of those", and have the object appear before them complete, live, and ready to manipulate.

Mark

Subject: Re: JavaWorld: Object Initialization
Date: Wed, 15 Apr 1998 18:31:16 -0700
From: Bill Venners

Mark,

My main point against adding superfluous no-arg constructors is that they make a class harder to understand and use if they force that class to be at times "invalid." The question in my mind then becomes, What's easier for Mark B's team to understand: A class that isn't a bean that doesn't have any invalid states, or a class that has invalid states but is a bean?

Perhaps it's my limited experience with OO programming, but I don't see why, in most ifnot all cases, you can't create a no-arg constructor that puts the bean in a valid state. It may not be the state that the user wants, but they then have the option of changing the state.

For one example, take a look at the java.io.FileOutputStream class. This class needs to have a file name or file handle or File reference at creation time to be "valid." It would be difficult to come up with a default filename that a no-arg constructor of FileOutputStream would open.

On the other hand, they could have designed FileOutputStream such that it has a no-arg constructor that sets a flag that no filename has been chosen. Such a class would need to declare an open() method (or methods) that lets the client give it a filename after it has been created.

Either design is functional, and the second one would enable the FileOutputStream (so long as it is also Serializable) to be a bean. My claim is that the first one is easier to understand and use than the second one.

But I think that the difference in ease-of-use of these two designs is very small and that design guidelines such as whether or not to make every class a bean should in the end be decided upon by the group that has to work with each other's code.

If I was designing a class and decided that there was absolutely no way to make a no-arg constructor that also put the object in a valid state, I would opt to not include that constructor.

Incidentally, I believe the reason for requiring the no-arg constructor is so that someone in an interactive development environment can point to an object, say "I want one of those", and have the object appear before them complete, live, and ready to manipulate.

Yes, this makes sense. If JavaBeans didn't require a no-arg constructor, the person would have to specify some initial values for some "arg-full" constructor (and which arg-full constructor would the IDE choose?) before the person could get an object.

bv

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 09:35:15 -0600
From: Mark Johnson

My main point against adding superfluous no-arg constructors is that they make a class harder to understand and use if they force that class to be at times "invalid." The question in my mind then becomes, What's easier for Mark B's team to understand: A class that isn't a bean that doesn't have any invalid states, or a class that has invalid states but is a bean?

Perhaps it's my limited experience with OO programming, but I don't see why, in most ifnot all cases, you can't create a no-arg constructor that puts the bean in a valid state. It may not be the state that the user wants, but they then have the option of changing the state.

I think it has to do with the semantics of null; that is, an object or object reference that indicates "no information".

There's a difference between

        Point p1;               // Null point
and
        Point p2 = new Point(0, 0);     // Point at origin

The "null-ness" of p1 is semantically different from the "zeroness" of p2, because you can say

        if (p1 == null)         // Do I even have a point to process?
to find out whether or not the reference holds any information.

For a Point, it may be useful to default-construct at (0,0), since many operations start at or refer to origin. But what about something like an image?

        Image img1;
        Image img2 = new Image(0, 0);
        Image img3 = new Image();               // equivalent to img2

For a particular application, an empty image may be equivalent to a null image (img1 means the same thing as img2 or img3), yet you always have two cases to test in your code

        if (img1 == null)               // is it null?
                return;
        if (img2.height == 0 && img2.width == 0)                // is it empty?
                return;

There are two cases that mean an image with no information: a null reference, and a non-null reference to an image that's "empty". Now, there may be a perfectly good reason to have an image of size (0,0), but in many cases, it's just needless complexity.

In C++, there's no way to do a null reference. You have to use pointers, or (if you want to use references) extend your API to include a function like "bool isNull()". Pretty annoying.

If I was designing a class and decided that there was absolutely no way to make a no-arg constructor that also put the object in a valid state, I would opt to not include that constructor.

Which is precisely Bill's argument. When a no-arg constructor threatens to complicate your semantics (and thereby your programming), it makes sense to leave it out unless making the object a bean buys you more than avoiding the additional complexity. It's a trade-off.

Incidentally, I believe the reason for requiring the no-arg constructor is so that someone in an interactive development environment can point to an object, say "I want one of those", and have the object appear before them complete, live, and ready to manipulate.

Yes, that's the obvious answer. Stupid me.

Keep in mind that you should never say

        FooBean b = new FooBean();
but rather
        FooBean b = java.beans.Beans.instantiate(loader, "FooBean")

This searches the classpath looking for a serialized bean "FooBean.ser" first, then loads a default-constructed bean if the serialized one's not found. It also does some special initialization for applets.

The requirement that the object be Serializable is primarily so that you can cut and paste beans to and from the clipboard as byte streams, and so that you can customize and save beans as preconfigured objects.

--mj

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 14:22:28 -0500
From: Mark Balbes

For a particular application, an empty image may be equivalent to a null image (img1 means the same thing as img2 or img3), yet you always have two cases to test in your code

         if (img1 == null)               // is it null?
                 return;
         if (img2.height == 0 && img2.width == 0)                // is it empty?
                 return;
There are two cases that mean an image with no information: a null reference, and a non-null reference to an image that's "empty". Now, there may be a perfectly good reason to have an image of size (0,0), but in many cases, it's just needless complexity.

Again, I think we are thinking along different lines. I certainly wouldn't want to create the situation that you describe above where the user has to check for both a null reference and an empty image. I think that that situation is more common in a C environment where you may have a null pointer or a pointer that points to memory that hasn't been initialized yet.

The point is that the default-constructed object should be valid and not empty, but it doesn't necessarily have to have information that is particularly useful. For your example of an image object, perhaps the company logo is the default image. That is, the logo of the company who made the bean, not the ones using the bean. It can be conveniently carried with the object or created dynamically by the object. An IDE user can then pull this bean down from a selection menu and get a graphical representation of this object, complete with the default image. The IDE developer then changes the image to be the one he wants. A more experienced user who isn't using the RAD environment can call a constructor that takes an image as an argument if they know what they want already. The end user of the product never sees the default image, and is completely oblivious as to how the final image is loaded.

The trick, then, is to make the image object without a method to remove the image, just a method to replace it. If the developer using the image object is done with the image, he then knows that he has to null out his reference. An exception could be thrown if the bean IDE developer tries to replace the image with a null reference.

Its easy to come up with ways to create a no-arg constructor for a specific case. I'm sure we can come up with one where it doesn't make sense. However, I think the norm should be to have one and I would imagine that the cases where you don't have one are few and far between.

Incidentally, I believe the reason for requiring the no-arg constructor is so that someone in an interactive development environment can point to an object, say "I want one of those", and have the object appear before them complete, live, and ready to manipulate.

Yes, that's the obvious answer. Stupid me. Keep in mind that you should never say

         FooBean b = new FooBean();
but rather
         FooBean b = java.beans.Beans.instantiate(loader, "FooBean")
This searches the classpath looking for a serialized bean "FooBean.ser" first, then loads a default-constructed bean if the serialized one's not found. It also does some special initialization for applets.

What happens if you have two different instances of the same bean that have their properties set differently?

The requirement that the object be Serializable is primarily so that you can cut and paste beans to and from the clipboard as byte streams, and so that you can customize and save beans as preconfigured objects.

V. Cafe has chosen not to serialize beans, but to load in the default bean and then change the properties using code. This is allowed by the JavaBeans spec. This probably isn't elegant, but sure makes the code easier to understand. If I'm coding by hand and see

 FooBean b = java.beans.Beans.instantiate(loader, "FooBean")
I have no idea what state the bean is in. However, using code makes it obvious:
 FooBean b = new FooBean();
 b.setPropertyX();
 b.setPropertyY();

Mark

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 13:50:24 -0600
From: Mark Johnson

Keep in mind that you should never say

        FooBean b = new FooBean();
but rather
        FooBean b = java.beans.Beans.instantiate(loader, "FooBean")
This searches the classpath looking for a serialized bean "FooBean.ser" first, then loads a default-constructed bean if the serialized one's not found. It also does some special initialization for applets.
What happens if you have two different instances of the same bean that have theirproperties set differently?

If that's the case, then they'll be in different files

// This loads FooBean1.ser:
FooBean b1 = java.beans.Beans.instantiate(loader, "FooBean1");

// This loads FooBean2.ser:
FooBean b2 = java.beans.Beans.instantiate(loader, "FooBean2");

// This loads FooBean.ser, or creates a new FooBean if it
// can't find the file:
FooBean b0 = java.beans.Beans.instantiate(loader, "FooBean");

The requirement that the object be Serializable is primarily so that you can cut and paste beans to and from the clipboard as byte streams, and so that you can customize and save beans as preconfigured objects.

V. Cafe has chosen not to serialize beans, but to load in the default bean and then change the properties using code. This is allowed by the JavaBeans spec. This probably isn't elegant, but sure makes the code easier to understand. If I'm coding by hand and see

FooBean b = java.beans.Beans.instantiate(loader, "FooBean")
I have no idea what state the bean is in. However, using code makes it obvious:
FooBean b = new FooBean();
b.setPropertyX();
b.setPropertyY();

Yeah, the code generation approach is clearer if you're reading code. I'm still not sure about the advisability of

FooBean b = new FooBean();
since java.beans.Beans.instantiate() could extend its functionality to handle new cases in later JDK releases. (For example, it handles applets specially, if they're not serialized.) In this case, the builder tool would be behind the technology. (Of course, few builder tools are JDK version independent...)

--Mark Johnson

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 13:20:52 -0700
From: Bill Venners

Marks,

I felt you both made very good points, which were neither null nor empty. I imagine we can all at least agree on one point:

If you are designing a bean, its no-arg constructor should if at all possible produce an object that is in a "valid" state. This default state may not in all cases, however, be particularly useful.

I think Mark B's example of a vendor company icon as a default image and a method that will replace but not remove the current image was right on the mark. His explanation fits in well with my own philosophy about objects having a valid state from the beginning of their lifetimes to the end.

I think our differences boil down to the subjective judgment as to the relative merits of these two designs for an object (such as mj's image wrapper object) that doesn't have a natural default state:

1. Have a no-arg constructor that fills the object with a default, though not necessarily "useful," state.

2. Don't have a no-arg constructor and force the client programmer to pass parameters to an "arg-full" constructor from which a "useful" initial state can be calculated.

If you already know you want the class to be a bean, I imagine we all agree that approach 1 is the best. If, as in Mark B's case for example, the team has decided to make all classes bean-compliant, approach 1 is the way to go.

If you don't know you want the class to be a bean, then my opinion is that approach 2 is better, on the grounds that it is easier to understand and use. On this point, we may differ. It's a very subjective call, but one that I think should really be decided by the team. If I were to take a contract with a team that prefers approach one even if they don't care beans about beans, I would use approach 1 at that job because that's what the team wants to see when they look at the code.

My old opinion of when to beanify and when not to beanify still seems to stand. If someone is going to use a class in a bean builder, that class had better be a bean. Otherwise, you needn't force it into a bean, though it may be bean-ready by its very nature. I do, however, think you should use the bean/Java naming conventions and JDK1.1 event model scheme regardless of whether your class has a no-arg constructor or implements Serializable.

bv

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 14:55:32 -0600
From: Mark Johnson

My old opinion of when to beanify and when not to beanify still seems to stand. If someone is going to use a class in a bean builder, that class had better be a bean. Otherwise, you needn't force it into a bean, though it may be bean-ready by its very nature.

Your whole letter summarized well the points of the discussion. I think our difference (if there even is one), is that I'm not sure I believe in "non-bean" classes; that is, I think it's possible for applications to be built completely visually, and that there's no such thing as "this is better as a class than as a component."

--mj

Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 16:06:52 -0700
From: Bill Venners

mj,

Your whole letter summarized well the points of the discussion. I think our difference (if there even is one), is that I'm not sure I believe in "non-bean" classes; that is, I think it's possible for applications to be built completely visually, and that there's no such thing as "this is better as a class than as a component."

I think that's possible too, though I don't have the kind of experience you have to back it up. I think we do agree. My point is that there are also projects which *won't* be done visually, and for those projects I feel a beanify everything mandate is less compelling.

An example is the java.io package. If every class in that package had had to be a bean, it would have forced a slightly different design mindset I think, one that may not be worth it IF those classes were never going to be dropped into a IDE.

bv

The Debaters
Mark Balbes, Ph.D.
Senior Software Engineer
Computerized Medical Systems, Inc.
Mark.Balbes@cms-stl.com
Mark Johnson
JavaWorld's JavaBeans Columnist
mark.johnson@javaworld.com
Bill Venners
President, Artima Software
bv@artima.com

And The Debate Continues...
Remember, if you have some thoughts of your own about the appropriate times and places for beanhood, feel free to post your ideas to the Flexible Java Forum.

* Note that I liked mj's title idea, "To Bean or Not to Bean," so much that I stole it for the title of this web page. - bv


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2009 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us