Sponsored Link •
|
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):
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,
OK.
Yes, working with beans in a RAD IDE is more gentle than straight coding.
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"
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 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.
No need to provide this if the class won't be used in a builder.
On the other hand, the Introspector creates a 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. 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, --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.
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?) 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.
True. Don't need it. Wouldn't want to force programmers to write a
Agreed. If you are designing a class whose objects will be generating events, you should follow the JDK1.1 event model and naming conventions.
Cool.
Yup.
I tend to agree with you on this point too.
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?
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.
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 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
Well, when someone says "a bean does so-and-so", they're either
talking about the class ("a bean has a class ImABean implements java.io.Serializable { }
It has a default no-arg constructor, it's serializable, it has one
read-only property (class, since True. Don't need it. Wouldn't want to force programmers to write a BeanInfo for every class.
Right.
Pathetic, isn't it? I think we're in precisely the same spot here. I'll check out your article.
I will, but it will be in my column.
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.
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
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
(
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!)
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
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,
For one example, take a look at the
On the other hand, they could have designed
Either design is functional, and the second one would enable the
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.
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
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 pointand 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 "
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.
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
"
The requirement that the
object be --mj
Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 14:22:28 -0500 From: Mark Balbes
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.
What happens if you have two different instances of the same bean that have their properties set differently?
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 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");
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 bv
Subject: Re: JavaWorld: Object Initialization
Date: Thu, 16 Apr 1998 14:55:32 -0600 From: Mark Johnson
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,
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 bv The Debaters
And The Debate Continues...
* 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
|