A puritanically correct GUI design.
I recently ran into a very old thread in which it was pointed out (correctly, when I think about it) that extending JFrame or JPanel to make a GUI component is technically an abuse of inheritance.
I am writing a GUI as part of a project. I want to write it in the most correct fashion possible, even if the actual benefit is nil. Call it a matter of principle.
Say we have a very simple GUI with one JFrame and a JPanel in that JFrame, each of which interacts with some functional code. Does this approach makes sense?:
1) Each of the two GUI components is embodied by a class which extends Object and has a member container (JFrame or JPanel). A static method is provided that instantiates the class and returns the container (factory model).
2) A launcher with a main() method is used to instantiate the JFrame-oriented class and render the JFrame visible. The JFrame-oriented class in turn instantiates the JPanel-oriented class and adds its JPanel to the JFrame-oriented class's own JFrame.
3) Action events are caught by inner classes of the respective GUI components and after low level processing (dialog boxes that say "Are you sure you want to do that, doofus?!" and so forth) the action handlers call methods in separate classes that have the actual guts of the program.
4) The functional classes are also instantiated by the launcher and passed as parameters when calling the static method of the JFrame-oriented class. That class in turn propogates them down to the JPanel-oriented class.
There are more details that could use hashing out but this sets forth the basic pattern I am looking at.
Is it good OOP?
Drake
[1689 byte] By [
Drake_Duna] at [2007-10-2 4:54:06]

Sorry, one more detail. Should the JFrame-oriented class be a singelton?Drake
If you are looking for pure OO design then you should be aware that static methods are symptoms of procedural design.
Then why should we stop inherit from JPanel to create custom panels? I disagree. Could you provide a link to discussion?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> Then why should we stop inherit from JPanel to create
> custom panels? I disagree. Could you provide a link
> to discussion?
Here are some materials on that topic:
http://forum.java.sun.com/thread.jspa?threadID=538522&messageID=2638893
http://www.cs.ualberta.ca/~hoover/201-Notes/Notes/section/ooquality.htm#i-sub
The first one (the message thread) also contains several external links to more on the subject.
If you think about it though it is pretty obvious. Is your application a JFrame? Noooo... it HAS a JFrame. Classic "is-a/has-a" analysis.
On the page of the second link there is a good rule of thumb I think. If you are an extending a class, then the subclass should be useable anywhere the parent class is.. otherwise you are not extending functionality, you are redefining the nature of the class.
From my own perspective the actual practical drawbacks of extending JFrame are zero or close enough to it as makes no difference, but I like to defend principle even when it is naked principle. Helps me keep things straight in my head.
Drake
> If you think about it though it is pretty obvious.
> Is your application a JFrame? Noooo... it HAS a
> a JFrame. Classic "is-a/has-a" analysis.
This is correct. But you were talking about making GUI component which is different thing.
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> > If you think about it though it is pretty obvious.
> > Is your application a JFrame? Noooo... it HAS a
> > a JFrame. Classic "is-a/has-a" analysis.
>
> This is correct. But you were talking about making
> GUI component which is different thing.
If you aren't changing the behavior of the JPanel, then there is no reason to extend it. In most GUI applications, we just add things to the JPanel instance. This is no more changing the behavior of the JPanel class than adding Strings to a List changes the List class.
> 1) Each of the two GUI components is embodied by a
> class which extends Object and has a member container
> (JFrame or JPanel). A static method is provided that
> instantiates the class and returns the container
> (factory model).
This makes sense for the JPanel but are you going to be passing the JFrame to anything? Just create a class that creates the JFrame, makes it visible and gets all it's children. Essentially, all the code that people usually put in their JFrame sublclass can go into a Object subclass.
> 2) A launcher with a main() method is used to
> instantiate the JFrame-oriented class and render the
> JFrame visible. The JFrame-oriented class in turn
> instantiates the JPanel-oriented class and adds its
> JPanel to the JFrame-oriented class's own JFrame.
See above.
> 3) Action events are caught by inner classes of the
> respective GUI components and after low level
> processing (dialog boxes that say "Are you sure you
> want to do that, doofus?!" and so forth) the action
> handlers call methods in separate classes that have
> the actual guts of the program.
I would create custom listeners for your events. Like DoofusMistakeListener, and add listeners to the main class that creates the JFrame.
> 4) The functional classes are also instantiated by
> the launcher and passed as parameters when calling
> the static method of the JFrame-oriented class. That
> class in turn propogates them down to the
> JPanel-oriented class.
The JFrame-oriented class doesn't need to have a static interface. You can deal with an instance of the JFrame-oriented class. The same can go for the JPanel. Making them static will make it harder to have multiple instances of the GUI components.
> There are more details that could use hashing out but
> this sets forth the basic pattern I am looking at.
>
> Is it good OOP?
You are on the right track. The key to OOP is encapsulation and the API. Start the design from the outside in. That is, define the API, and then implement it. Often people work in the other direction (implementation first, API second) and it tends to produce poor OO designs.
> If you aren't changing the behavior of the JPanel,
> then there is no reason to extend it. In most GUI
> applications, we just add things to the JPanel
> instance. This is no more changing the behavior of
> the JPanel class than adding Strings to a List
> changes the List class.
What would you say about behaviors of, say, a panel for editing an Order comparing to a panel for viewing a list of Clients?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> > If you aren't changing the behavior of the JPanel,
> > then there is no reason to extend it. In most GUI
> > applications, we just add things to the JPanel
> > instance. This is no more changing the behavior
> of
> > the JPanel class than adding Strings to a List
> > changes the List class.
>
> What would you say about behaviors of, say, a panel
> for editing an Order comparing to a panel for viewing
> a list of Clients?
Do either change the JPanel class or do they decorate it?'
Again, lets put the question is perspective:
What would you say about behaviors of, say, a HashMap containing components for editing an Order comparing to a HashMap containing components for viewing a list of Clients?
Swing Containers are just that: containers.
Let me ask you this: what rationale do you have for extending JPanel to add components to it? What does doing this give you?
> This makes sense for the JPanel but are you going to
> be passing the JFrame to anything? Just create a
> class that creates the JFrame, makes it visible and
> gets all it's children. Essentially, all the code
> that people usually put in their JFrame sublclass can
> go into a Object subclass.
To tell you the truth, and I know this is a lame reason, but partly the problem is that it drives me insane seeing the little $1.class file that I end up with because of the anonymous inner Runnable that I pass to SwingUtilities in main(). I was thinking of having my launcher implement Runnable and putting the stuff for building the GUI in run(). Then I could just pass a new instance of the launcher to SwingUtilities in main().
> The JFrame-oriented class doesn't need to have a
> static interface. You can deal with an instance of
> the JFrame-oriented class. The same can go for the
> JPanel. Making them static will make it harder to
> have multiple instances of the GUI components.
So instead it becomes something like this?
MainGUI theGUI = new MainGUI();
theGUI.getFrame().setVisible(true);
Thanks for the comments. I really want to get this stuff right from the get-go.
Drake
> So instead it becomes something like this?
>
> MainGUI theGUI = new MainGUI();
> theGUI.getFrame().setVisible(true);
>
> Thanks for the comments. I really want to get this
> stuff right from the get-go.
There's rarely 'one true way' to do things in developement and the more complicated the problem the more that is the case. Writing a good GUI is tough so don't expect to get it perfect right off the bat (is it ever possible in development?)
The goal is to not paint yourself into a corner.
Without knowing your application, I can only make vague suggestions. First of all, are you going to be doing this with MVC? If so, then that's going to lead your design to a large degree.
> First of all, are you going to be doing this with MVC?One would hope that Swing would push the OP in that direction. :^)- Saish
> Sorry, one more detail. Should the JFrame-oriented
> class be a singelton?
Nobody has addressed this one yet (unless I missed it) so let me barge in.
Do you trust the users of the JFrame class to create only one instance per application? Is actual harm caused if two or more instances are created?
If the answers to those questions are NO and YES then make it a singleton. But I suspect you're going to be the only user of the class and why on earth would you create more than one instance anyway? If that's how you see it then why bother making it a singleton?
> Do you trust the users of the JFrame class to create
Trust? Suuuure.. I trust people. Hey, could you hand me that tinfoil hat over there on the desk?
> only one instance per application? Is actual harm
> caused if two or more instances are created?
The application deals with files... so if they were both working with the same file at once I guess there could be harm. Also the runtime properties (locale, window position) could get saved incorrectly (heavens help us!!!).
Not like this is actually going to happen. The unstated background here is that I am actually doing a lot more "laying of principles" and learning of techniques than actual application writing (although I do intend to finish the app, and it will likely be used by at least a dozen or so people per day as a webstart app or applet).
Before September I had never touched object oriented programming in my life, so basically I am trying to do everything as perfect as possible... like if you learn how to swing a bat or shoot a bow wrong at first it takes forever to unlearn that. But maybe I am taking it a little far.
Drake
Here is my advice,
A 'correct' UI is 90% about where and how you apply the business logic, and 10% about how you implement the view layer (i.e., uses of inheritance, etc)
(1) Use MVC like this: Controller and Model classes extend from Object. Your view classes are pretty much all JComponents.
(2) Model classes are basically data containers, and handle database/network communication.
(3) The Controller contains all the business logic. What does this mean? It means they know how communication between model/view works. Controllers instantiate your model and view classes. They add the listeners and any other connections. They handle all events (like button clicks). Controllers know which model/view components will participate in handling an event, and call their methods.
(4) For example, when the user clicks Ok, the view component receives the button click event, and delegates it to the controller, by calling getController().doOkAction();
(5) The controller knows if the OK event requires a SwingWorker to coordinate the time-consuming work, and the Controller is free to make method calls to the view and model.
(6) It is easy to find classes if you extend JPanel for pretty much every GUI widget that acts as a container.
(7) For example: You have a login dialog. In there, you have a user/password text fields, and their corresponding JLabels and the Ok/Cancel buttons. Throw all that into a custom JPanel called LoginPanel.===============================================
Here is an example that uses these ideas: You want to show a login dialog.
There is more detail after this snippet of pseudo code, but the basic algorithm for using MVC is here:// (1) Create the controller and call its show() method.
LoginController controller = new LoginController();
controller.show();
// (2) In the show() method, the controller threads its work using a swing worker:
new SwingWorker () {
Non-UI method() {
(3) Create Model object.
(4) Load Model with data (from either DB or local Properties).
// Item (4) May need to enforce security/permissions, etc.
(5) Create View object (view object does all its layout)
(6) Load view object with data from the model.
(7) Create connections between view/model (i.e. PropertyChangeListeners)
(8) Pack() the view object.
}
UI method() {
(9) Make the view object visible.
}
}
===============================================
Here is a more detailed look at creation & action handling:
Instantiate a LoginController object and call LoginController.showLoginDialog();
Here is what the LoginController does:
It creates a swing worker to separate the UI and Non-UI work.
In the non-UI section of the swing worker:
The controller creates an empty LoginModel object (and gives the model a reference to the Controller). Then it loads the model with data (such as the last remembered username/encrypted password) by calling LoginModel.loadFromDefaults(), which populates itself with data from a Properties object or by contacting the database.
The controller creates a LoginDialog (which extends JDialog) (and gives the dialog a reference to the Controller). The LoginDialog constructor creates all the dialog抯 children and lays them out.
The controller initializes the text fields in the LoginDialog with real data by calling, LoginDialog.loadDataFromModel(). This is a composite pattern, so the dialog calls this method on all its children, and each widget loads its data from the model.
In the UI-section of the swing worker:
The controller calls LoginDialog.pack() (pack() can also be performed in Non-UI section for performance improvement, since the dialog is not visible yet), and then calls LoginDialog.setVisible(true);
At this point, the dialog is visible. The user then clicks OK, to login.
===============================================
The ActionListener attached to the OK button delegates to LoginController.doOkAction();
The doOkAction() will spawn another SwingWorker.
In the non-UI section of the swing worker:
// First it makes sure all the data the user entered is in valid range. If there is a problem, this throws some kind of input exception to stop the Ok action. The ranges are stored in the model.
The controller calls LogonDialog.validateData()
For example: LoginPanel.validateData () {
String text = getUsernameTextField().getText();
if(text.trim().length > getModel().getMaxUsernameLength()) {
throw new InputException(揟he user name is too long, etc?;
}
}
If the data is valid, the controller moves it from the UI widgets into the model, by calling
LogonDialog.saveDataToModel().
This can be implemented by a composite pattern, so all the individual view component handle saving their data.
The actual login (network communication) is handled by another layer, and only the model knows how to use that layer to contact the DB or network, etc.
Now that the model has all the user-entered data, the controller calls:
LoginModel.doLogin();
// throws ConnectivityException, InvalidUsernameAndPasswordException, etc.
In the UI section of the swing worker:
Check if it was successful, if the login worked, then the Controller calls:
LogonDialog.close();
GetMainFrame.setLoggedIn(true); // updates the rest of the user interface.
Thanks for the comments jvaudry, and everyone else. I think I have got some direction with this thing now.Drake
> If you are looking for pure OO design then you should
> be aware that static methods are symptoms of
> procedural design.
>
> Then why should we stop inherit from JPanel to create
> custom panels? I disagree. Could you provide a link
> to discussion?
>
>
> Denis Krukovsky
> http://dotuseful.sourceforge.net/
> http://dkrukovsky.blogspot.com/
The only place I saw him mention static methods is in reference to using a static factory instead of constructors. I wouldn't call that procedural at all and is in most cases much preferred to a public constructor. In my experience this is especially true in classes dealing with the GUI where it's especially important you avoid the pitfalls of public constuctors.
Just my .02.
> > 3) Action events are caught by inner classes of
> the
> > respective GUI components and after low level
> > processing (dialog boxes that say "Are you sure
> you
> > want to do that, doofus?!" and so forth) the
> action
> > handlers call methods in separate classes that
> have
> > the actual guts of the program.
>
> I would create custom listeners for your events.
> Like DoofusMistakeListener, and add listeners to the
> e main class that creates the JFrame.
Are you suggesting a publicly visible class "DoofusMistakeListener" rather than an inner or anonymous class? If that's the case, I'd have to strongly disagree. I can fathom instances where this would be appropriate, but the vast majority of event handling that I find my GUIs need is low level handling that is directly tied to the GUI component itself or the class controlling that component. Generally, the event handling itself is a implementation detail I do my best to avoid exposing.
> > 4) The functional classes are also instantiated
> by
> > the launcher and passed as parameters when calling
> > the static method of the JFrame-oriented class.
> That
> > class in turn propogates them down to the
> > JPanel-oriented class.
>
> The JFrame-oriented class doesn't need to have a
> static interface. You can deal with an instance of
> the JFrame-oriented class. The same can go for the
> JPanel. Making them static will make it harder to
> have multiple instances of the GUI components.
>
Forwarding through static methods like that is a bad idea. Maybe that's what a previous poster was referring to as procedural (I didn't notice this before now) in which case I agree with them.
> You are on the right track. The key to OOP is
> encapsulation and the API. Start the design from the
> outside in. That is, define the API, and then
> implement it. Often people work in the other
> direction (implementation first, API second) and it
> tends to produce poor OO designs.
Quoted for emphasis.
> > If you aren't changing the behavior of the JPanel,
> > then there is no reason to extend it. In most GUI
> > applications, we just add things to the JPanel
> > instance. This is no more changing the behavior
> of
> > the JPanel class than adding Strings to a List
> > changes the List class.
>
> What would you say about behaviors of, say, a panel
> for editing an Order comparing to a panel for viewing
> a list of Clients?
>
> Denis Krukovsky
> http://dotuseful.sourceforge.net/
> http://dkrukovsky.blogspot.com/
I'd say that the behavior differences between the two is not directly related to the JPanel. The JPanel is just a container, as dubwai said you're just "decorating" it (i like that term) rather than changing the behavior of the JPanel itself. The behavior of what is inside the JPanel is not something that sould be part of the JPanel itself. It's rather like making a new car just to change the horsepower of the engine. The horsepower of the engine is part of the behavior of the engine, not the entire car.
> > This makes sense for the JPanel but are you going
> to
> > be passing the JFrame to anything? Just create a
> > class that creates the JFrame, makes it visible
> and
> > gets all it's children. Essentially, all the code
> > that people usually put in their JFrame sublclass
> can
> > go into a Object subclass.
>
> To tell you the truth, and I know this is a lame
> reason, but partly the problem is that it drives me
> insane seeing the little $1.class file that I end up
> with because of the anonymous inner Runnable that I
> pass to SwingUtilities in main(). I was thinking of
> having my launcher implement Runnable and putting the
> stuff for building the GUI in run(). Then I could
> just pass a new instance of the launcher to
> SwingUtilities in main().
In the absence of any compelling reason to not have that $1.class file, don't use that as a reason. Get over it and ignore it. It's just absurd and foolish to make important design decisions around a pet peev about the compiled class names.
> In the absence of any compelling reason to not have
> that $1.class file, don't use that as a reason. Get
> over it and ignore it. It's just absurd and foolish
> to make important design decisions around a pet peev
> about the compiled class names.
As it turns out I cannot get rid of that thing anyway, but I am wondering if what I proposed doesn't make more sense regardless. Why should the GUI element itself have a main() method? Here is what my launcher looks like right now:
public class DeathByKanji implements Runnable {
public static void main(String[] arguments) {
SwingUtilities.invokeLater(new DeathByKanji());
}
public void run() {
JFrame theFrame = new DBKMainGUI(new DBKEngine()).getFrame();
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setVisible(true);
}
}
As you can see, to instantiate the DBKMainGUI I need to pass it an instance of a DBKEngine (which is the "model" in the MVC paradigm). I could put this code into DBKMainGUI like this...
public static void main(String[] arguments) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame theFrame = new DBKMainGUI(new DBKEngine()).getFrame();
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.setVisible(true);
}
});
}
However, I am wondering what business DBKMainGUI has instantiating a DBKEngine.
Let me use an example, and this is not hypothetical. I was thinking of having the constructor for DBKMainGUI take an interface as an argument rather than an actual DBKEngine. This is because I was thinking of creating a network "link" version of the engine (which in turn linked to a remote instance of the actual DBKEngine) so that I could send only the GUI by java web start to a user and run all the actual code on the server end.
If I was not using the launcher pattern, this would result in two 90% identical versions of DBKMainGUI.java (having different main() methods only). With the launcher pattern, I just use a different launcher to pass a DBKEngineLink instead of a DBKEnginewhen instantiating the DBKMainGUI.
Returning to another issue implicated by the above code, there seems to be a disagreement as to whether I should use a public constructor for the GUI elements or a static method. What are the concrete reasons for each view?
1) Static factory methods have names. They can be named according to what they do where a constructor cannot be. In many cases this is irrelevent, in some cases it is crucial to have two static factory methods that accept the same parameter(s).
2) Static factory methods aren't required to create a new instance. This allows use of preconstructed instances or for instances to be cached. This is especially important for immutable classes.
3) Static factory methods can return any subtype of the declared type. You can return objects without even making the actual class public. You can change the actual implementation returned without changing the method signature or affecting client code in any way.
4) Static factory methods can be declared in interfaces. Guaranteeing that all implementations provide a common methodology for creation. I've never personally used this, I'm not sure I agree with putting it in an interface but it still provides for the option if you feel it's appropriate.
5) Static factory methods can perform initialization that must be performed after construction has finished.
6) Static factory methods can return null. In some cases it may be necessary to prevent a client from ever trying to use an object whose initialization failed. You can prevent this with a constructor by either throwing an exception when it's initialized or throwing one when they try to invoke it. You have two choices, either have code checking the object's initialization everywhere or have it in one place. They'll still get a null pointer if they try to invoke something on it, but at least then I don't have to check 50 million times in my code in case they do. It puts the burden on them.
The only disadvantage to static factory methods is you can't subclass without any protected constructors. However, as a general rule you should either design a class for inheritance or forbid it, so this is only an issue in the very rare class that is designed for inheritance.
Given the flexibility of a static factory and lack of flexibility in a constructor I put the burden of proof on the constructor prove it should be used. If I design for inheritance I use a constructor because I have to. In all other cases, only when I'm absolutely sure I need none of the advantages of a static factory (and won't in the future need them) will I use a constructor. This results in 95% of my classes using it.
dubwai,
> > What would you say about behaviors of, say, a
> panel
> > for editing an Order comparing to a panel for
> viewing
> > a list of Clients?
>
> Do either change the JPanel class or do they decorate
> it?'
They extend the responsibilities of JPanel. What exact meaning you give to "decorate" here?
> What would you say about behaviors of, say, a HashMap
> containing components for editing an Order comparing
> to a HashMap containing components for viewing a list
> of Clients?
A subjective analogy. Let's keep on main question.
> Let me ask you this: what rationale do you have
> for extending JPanel to add components to it?
> What does doing this give you?
Modularity. The code responsible for maintaining JPanel content stays with it.
kablair,
> I'd say that the behavior differences between the two
> is not directly related to the JPanel. The JPanel is
Name an object to which these differences are related.
> just a container, as dubwai said you're just
> "decorating" it (i like that term) rather than
> changing the behavior of the JPanel itself. The
I do need to add additional responsibilities to JPanel.
> behavior of what is inside the JPanel is not
> something that sould be part of the JPanel itself.
Name an object you have in mind here.
> It's rather like making a new car just to change the
> e horsepower of the engine. The horsepower of the
> engine is part of the behavior of the engine, not the
> entire car.
This is very subjective analogy. Do you think we can use it as an objective argument?
> The only place I saw him mention static methods is in
> reference to using a static factory instead of
> constructors. I wouldn't call that procedural at all
Then we seem to have different meaning for "procedural", or at least we can name the design as "more object-oriented" or "less object-oriented". If my design manipulates with objects which can be instantiated, extended, but some part of my code can't, I would say this part of code is less OO.
Now you guys who think we should stop extending UI classes, would you provide some of your UI code to prove it?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> dubwai,
> > > What would you say about behaviors of, say, a
> > panel
> > > for editing an Order comparing to a panel for
> > viewing
> > > a list of Clients?
> >
> > Do either change the JPanel class or do they
> decorate
> > it?'
>
> They extend the responsibilities of JPanel. What
> exact meaning you give to "decorate" here?
If you can create a completely equivalent JPanel without extension, I don't see how this is the case.
> > What would you say about behaviors of, say, a
> HashMap
> > containing components for editing an Order
> comparing
> > to a HashMap containing components for viewing a
> list
> > of Clients?
>
> A subjective analogy. Let's keep on main question.
If you want to claim the analogy is not correct, the burden is upon you to explain why. Both are containers.
> > Let me ask you this: what rationale do you have
> > for extending JPanel to add components to
> it?
> > What does doing this give you?
>
> Modularity. The code responsible for maintaining
> JPanel content stays with it.
How is that not true in a class that doesn't extend JPanel?
> kablair,
> > I'd say that the behavior differences between the
> two
> > is not directly related to the JPanel. The JPanel
> is
>
> Name an object to which these differences are
> related.
>
> > just a container, as dubwai said you're just
> > "decorating" it (i like that term) rather than
> > changing the behavior of the JPanel itself. The
>
> I do need to add additional responsibilities to
> JPanel.
>
> > behavior of what is inside the JPanel is not
> > something that sould be part of the JPanel itself.
>
> Name an object you have in mind here.
>
> > It's rather like making a new car just to change
> the
> > e horsepower of the engine. The horsepower of the
> > engine is part of the behavior of the engine, not
> the
> > entire car.
>
> This is very subjective analogy. Do you think we can
> use it as an objective argument?
Provide a different anology that is just as valid.
How about this for objective: You don't need to extend JPanel to add controls to an instance of one. That's provable. Now explain why we should extend classes unecessarily.
If you play around with the SWT libraries, you'll find very quickly that none of the core classes are meant to be extended. This was intentional because the authors wanted to break developers of this dirty habit.
> Now you guys who think we should stop extending UI> classes, would you provide some of your UI code to> prove it?Prove what?
> If you can create a completely equivalent JPanel
> without extension, I don't see how this is the case.
> You don't need to extend JPanel to add controls ...
With extension, you can create JPanel with added responsibilities. For example, a Panel which can be asked on what the user entered as "Order #". And this new panel is different from JPanel.
SWT. I consider inability to extend SWT classes not as an advantage but as a limitation.
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> > If you can create a completely equivalent JPanel
> > without extension, I don't see how this is the
> case.
>
> > You don't need to extend JPanel to add controls
> ...
>
> With extension, you can create JPanel with added
> responsibilities. For example, a Panel which can be
> asked on what the user entered as "Order #". And this
> new panel is different from JPanel.
I can create a HashMap class that has methods that return the values for keys that I know will be in the map. That doesn't mean it's necessary. Your argument is circular. You are basically saying that adding methods to the class create responsibilities and therefore it's valid to extend the class to add a method. By that logic, all class extensions are just as valid as any other. You don't believe that do you?
Extending JPanel to add a method like the above is merely coupling your application logic to a GUI framework component. It's not needed and it's not desireable. It makes the code much harder to reuse and refactor.For example, if the application needs to be moved to a web app, your code will need to be ripped from the JPanel and all classes that used that method will need to be changed to use a new class.
The thing is that the given responsibilty doesn't belong in a GUI framework class.You can get the same information about a JPanel from a class that doesn't extend JPanel. Is the issue here that you don't understand how this would be done?
> SWT. I consider inability to extend SWT classes not> as an advantage but as a limitation.You can extend them. It's that you should not and the classes were specifically crafted to disuade developers from doing so.
> Name an object to which these differences are
> related.
javax.swing.Box extends related functionality. It is rare, hence why I almost never extend any class.
> I do need to add additional responsibilities to
> JPanel.
Are these responsibilities part of the JPanel itself or is the JPanel simply part of how it carries out it's responsibilities? Is every single method inherited and it's implementation directly related to the functionality of the class? If it's not, then it shouldn't be extending a JPanel.
> Name an object you have in mind here.
Take a GUI where the user is presented with a form, a composition of text fields and other GUI components. This is not a JPanel, it should not be a JPanel, it may use one, but it is not itself a JPanel. A JPanel does not care that a user entered text, or clicked a button, or asked to move on to the next page, all it cares about is presenting it's contents. This form on the other hand may handle that functionality. The form is a composition of the GUI components to provide a higher level component that can encapsulate the functionality.
> This is very subjective analogy. Do you think we can
> use it as an objective argument?
Every analogy is subjective. Could you point out what part of the analogy is invalid?
> Then we seem to have different meaning for
> "procedural", or at least we can name the design as
> "more object-oriented" or "less object-oriented". If
> my design manipulates with objects which can be
> instantiated, extended, but some part of my code
> can't, I would say this part of code is less OO.
The design manipulates Objects. I fail to see how no permitting direct instantiation by a client creates procedural code. Concrete inheritance violates encapsulation, a key OOP. A constructor often times does as well because it exposes the implementation by exposing how the Object is created. Static factory methods are often used to encapsulate creation and initialization so that it is not exposed, I would call that more OO not less OO.
> Now you guys who think we should stop extending UI
> classes, would you provide some of your UI code to
> prove it?
Prove what? It's a subjective intellectual debate, it can't be proven because it's not a matter of fact.
I never said we should "stop extending UI classes". I do believe, however, that concrete inheritance violates encapsulation and that there are very very few instances where any class should be extended. In this particular debate I don't think inheritance was appropriate in the examples I've seen.
I'll give you an example of one case where I, rightly or wrongly, did feel inheritance was appropriate and it was with a GUI component.
With my work one the single most common GUI component is a "query" of some type. This query needs to present the user with a table of elements they can select from and needs to have a lot of speciailized functionality (selecting row by keypad numbers, double clicking, hotkeys, rows that alternate color, rows with multiple lines of text, adding more elements when they scroll near the end, or page down past what's available, or use down arrow at the end of available, etc.). This query needed to be able to present an indefinite number of incrementally added elements where the element used (and returned) was of a generic type.
I created an AbstractQuery that encapsulated all of this functionality. It provided public methods for getting the element and performing other operations such as setting the title all of which were final. It provided protected methods for manipulating the table (so that a subclass can specifiy column order at runtime without exposing the table). It also had two abstract protected methods in which a subclass must provide an element at a specified index (for returning the selected element) and another case in which it must return the next n elements in a String[] parsed to meet the original column order when it was instantiated.
Now, this AbstractQuery used JDialog, JTable, JButton, JPanel, JScrollPane and many other GUI component. However, none of these components are exposed. I could have easily extended JDialog, but it's simply not appropriate because the AbstractQuery is NOT a JDialog. It isn't modal or non-modal, for example. It should not be used as a Component, Container, Window or Dialog (all of which it would be if I did) and does NOT provide their functionality. It can not have items added to it, it can not have it's content pane set, it can not have it's menus set or changed. All of this functionality does not belong to the AbstractQuery, it belongs to the JDialog the AbstractQuery uses.
That is why it was no appropriate for AbstractQuery to inherit. But it was an appropriate (IMHO) use for AbstractQuery to be inherited from. A subclasses functionality is part of the AbstractQuery. A ClaimQuery displays a list of Claims, everything about the ClaimQuery is directly tied to the AbstractQuery, every piece of functionality present in the AbstractQuery is and always will be part of ClaimQuery. Anything in AbstractQuery that changes SHOULD change in ClaimQuery too. A ClaimQuery, however, manages the functionality of actually returning Claims and parsing them to be added to the AbstractQuery. I could have used composition, however, it would not have been appropriate as the functionality of a ClaimQuery IS the functionality of an AbstractQuery. It does not encapsulate it, it does not make use of it it, it IS it.
I've been able to reuse this in half a dozen applications with everything from XML-RPC to JDBC to local file access as a backing for the query. I feel that in this particular case, it was a good decision, both for AbstractQuery to NOT inherit from JDialog but also for all the queries of this nature to inherit from AbstractQuery. No single subclass of AbstractQuery encapsulates a single bit of code that isn't directly tied to (by necessity) the AbstractQuery. If it did, then I'd have probably use composition with a "QueryUI" that encapsulated AbstractQuery's functionality instead.
>
> To tell you the truth, and I know this is a lame
> reason, but partly the problem is that it drives me
> insane seeing the little $1.class file that I end up
> with because of the anonymous inner Runnable that I
> pass to SwingUtilities in main().
Making it annoymous is a choice. You can make it explicit as well.
> > What would you say about behaviors of, say, a
> HashMap
> > containing components for editing an Order
> comparing
> > to a HashMap containing components for viewing a
> list
> > of Clients?
>
> A subjective analogy. Let's keep on main question.
>
> > Let me ask you this: what rationale do you have
> > for extending JPanel to add components to it?
> > What does doing this give you?
>
> Modularity. The code responsible for maintaining
> JPanel content stays with it.
That statement is subjective and just wrong for non-trivial applications.
It might be 'nice' to have my customer window also have the database code for storage, but in a non-trivial application that is always an incorrect choice to make. It does however keep all of the code in one place.
> I can create a HashMap class that has methods that
> return the values for keys that I know will be in the
> map. That doesn't mean it's necessary. Your
> argument is circular. You are basically saying that
> adding methods to the class create responsibilities
> and therefore it's valid to extend the class to add a
> method. By that logic, all class extensions are just
> as valid as any other. You don't believe that do
> you?
Too much. I didn't get it.
> Extending JPanel to add a method like the above is
> merely coupling your application logic to a GUI
> framework component. It's not needed and it's not
> desireable. It makes the code much harder to reuse
> and refactor.For example, if the application needs
> to be moved to a web app, your code will need to be
> ripped from the JPanel and all classes that used that
> method will need to be changed to use a new class.
No. You got something wrong. We are not touching business logic here. It stays as it was. Show me where do you think the coupling has appeared.
> The thing is that the given responsibilty doesn't
> belong in a GUI framework class.You can get the
How it can be the responsibilty doesn't
belong to a GUI if I need a GUI?
> same information about a JPanel from a class that
> doesn't extend JPanel. Is the issue here that you
> don't understand how this would be done?
Show me how.
kablair,
> > Name an object to which these differences are
> > related.
>
> javax.swing.Box extends related functionality. It is
> rare, hence why I almost never extend any class.
I hardly understand you. I'm asking on how do you build your objects if you need UI for an Order, and UI for a list of Clients?
> > I do need to add additional responsibilities to
> > JPanel.
>
> Are these responsibilities part of the JPanel itself
> or is the JPanel simply part of how it carries out
> it's responsibilities? Is every single method
> inherited and it's implementation directly related to
> the functionality of the class? If it's not, then it
> shouldn't be extending a JPanel.
Yes responsibilities are part of a Panel (an Order UI Panel). Yes methods are inherited - what other can you expect?
> Every analogy is subjective. Could you point out
> what part of the analogy is invalid?
The part which is invalid is that it is subjective.
> > Then we seem to have different meaning for
> > "procedural", or at least we can name the design
> as
> > "more object-oriented" or "less object-oriented".
> If
> > my design manipulates with objects which can be
> > instantiated, extended, but some part of my code
> > can't, I would say this part of code is less OO.
>
> The design manipulates Objects. I fail to see how no
> permitting direct instantiation by a client creates
> procedural code. Concrete inheritance violates
What if you would need two instances?
>Concrete inheritance violates
> encapsulation, a key OOP. A constructor often times
I don't think so. See
http://dkrukovsky.blogspot.com/2005/06/is-inheritance-bad.html
>A constructor often times
> does as well because it exposes the implementation by
> exposing how the Object is created. Static factory
No. Constructor exposes what it needs for object to be created, not how.
> Static factory
> methods are often used to encapsulate creation and
> initialization so that it is not exposed, I would
> call that more OO not less OO.
Encapsulation - ensuring that users of an object cannot change the internal state of the object in unexpected ways; only the object's own internal methods are allowed to access its state.
Shall I explain more?
Now thanks for your example of GUI desing. Would you tell us such parameters as the number of lines of code and Coupling Between Objects rate for your AbstractQuery class?
jschell,
> > Modularity. The code responsible for maintaining
> > JPanel content stays with it.
>
> That statement is subjective and just wrong for
> non-trivial applications.
>
> It might be 'nice' to have my customer window also
> have the database code for storage, but in a
> non-trivial application that is always an incorrect
> choice to make. It does however keep all of the code
> in one place.
Why it is wrong? It is correct if you note that we can distinguish between UI logic and DB logic.
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> > I can create a HashMap class that has methods that
> > return the values for keys that I know will be in
> the
> > map. That doesn't mean it's necessary. Your
> > argument is circular. You are basically saying
> that
> > adding methods to the class create
> responsibilities
> > and therefore it's valid to extend the class to add
> a
> > method. By that logic, all class extensions are
> just
> > as valid as any other. You don't believe that do
> > you?
>
> Too much. I didn't get it.
I don't have time to spell it out. Sorry.
> > Extending JPanel to add a method like the above is
> > merely coupling your application logic to a GUI
> > framework component. It's not needed and it's not
> > desireable. It makes the code much harder to
> reuse
> > and refactor.For example, if the application
> needs
> > to be moved to a web app, your code will need to
> be
> > ripped from the JPanel and all classes that used
> that
> > method will need to be changed to use a new class.
>
> No. You got something wrong. We are not touching
> business logic here. It stays as it was. Show me
> where do you think the coupling has appeared.
I did not mention business logic.
> > The thing is that the given responsibilty doesn't
> > belong in a GUI framework class.You can get the
>
> How it can be the responsibilty doesn't
> belong to a GUI if I need a GUI?
I didn't say it doesn't belong to a gui. I said it doesn't belong to the gui framework.
> > same information about a JPanel from a class that
> > doesn't extend JPanel. Is the issue here that you
> > don't understand how this would be done?
>
> Show me how.
It's really quite simple.
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class NameForm
{
JFrame frame;
JPanel panel;
JTextField field;
public static void main(String[] args)
{
new NameForm();
}
public NameForm()
{
frame = new JFrame();
panel = new JPanel();
panel.setLayout(new GridLayout(1, 1));
frame.getContentPane().add(panel);
field = new JTextField();
panel.add(field);
field.addKeyListener(new TextActionListener());
frame.pack();
frame.setVisible(true);
}
private class TextActionListener implements KeyListener
{
public void keyTyped(KeyEvent e)
{
System.out.println(field.getText());
}
public void keyPressed(KeyEvent e){}
public void keyReleased(KeyEvent e){}
}
}
> I don't think so. See
> http://dkrukovsky.blogspot.com/2005/06/is-inheritance-
> bad.html
This is just you stating the same opinion you have stated here with even less explanation. Is having a blog supposed to make you an expert?
Ok, your example is acceptable while it is short. Now would you explain how do you able to reuse your NameForm in larger solution?Denis Krukovsky http://dotuseful.sourceforge.net/ http://dkrukovsky.blogspot.com/
> Ok, your example is acceptable while it is short. Now> would you explain how do you able to reuse your> NameForm in larger solution?Give me a specific example of what you think I can't do with it.
How would you reuse this code if you would need to present the user an editable list of names? If you would need to present complex UI like say an Order where Name is part of it?
How your code is ready to changes if you'll need to have say a Name to be presented as TextField, a Label and a Button?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> How would you reuse this code if you would need to
> present the user an editable list of names? If you
> would need to present complex UI like say an Order
> where Name is part of it?
You are just going to have to use your imagination. I don't have time to write a complex UI just to show you how something works. I don't write GUIs like what you describe anymore. The last gui I wrote has a dynamicly generated view and doesn't extend any GUI framework classes.
> How your code is ready to changes if you'll need to
> have say a Name to be presented as TextField, a Label
> and a Button?
Are you really unable to see that this is trivial? I wrote a quick example. Arguing that my trivial example isn't complex is a really stupid premise.
How does extending JPanel help you solve this problem?
private String name;
public void setName(JTextField t)
{
t.setText(name);
}
public void setName(JButton b)
{
b.setText(name);
}
public void setName(JLabel l)
{
l.setText(name);
}
Thanks for sharing your experience. Probably I would understand you better if you would provide some code on how your view is dynamically generated.
My point is that the class you provided is not reusable. I gave you a set of situations where you can't reuse it, and you provided no example which can do the job and reuse your NameForm.
By "reusable" I mean that the class can be reused without changes.
Note that if custom UI class extends JComponent it can be reused to present the user an editable list of names, to be part of Order where Name is part of it, and such.
Then look at what name you gave to your NameForm class. If it is a Form than why shouldn't it extend JForm?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> Thanks for sharing your experience. Probably I would
> understand you better if you would provide some code
> on how your view is dynamically generated.
>
> My point is that the class you provided is not
> reusable. I gave you a set of situations where you
> can't reuse it,
No you didn't. You gave me a bunch of requirements that had no relationship to the original design.
> and you provided no example which can
> do the job and reuse your NameForm.
> By "reusable" I mean that the class can be reused
> without changes.
The example I gave was a trivial proof-of-concept. How about this. Create a class that does what you originally asked me to do using your extension approach. Then when you are done and have posted it. I will give you some arbitrary requirement. Your class will have to fufill it without changes. OK?
> Note that if custom UI class extends JComponent it
> can be reused to present the user an editable list of
> names, to be part of Order where Name is part of it,
> and such.
Why do you think you have to extend JComponent to do this? Show me why this is necessary. Give me and exmple of this using extension and I will show you how to do the same thing without extending anything. There is no doubt in my mind that I can do this. Put your money where your mouth is. You are the one making unorthodox claims. How about proving them?
> > The thing is that the given responsibilty doesn't
> > belong in a GUI framework class.You can get the
>
> How it can be the responsibilty doesn't
> belong to a GUI if I need a GUI?
>
> > same information about a JPanel from a class that
> > doesn't extend JPanel. Is the issue here that you
> > don't understand how this would be done?
>
> Show me how.
It needs a GUI, but the functionality it needs isn't part of the GUI itself.
>
> kablair,
> > > Name an object to which these differences are
> > > related.
> >
> > javax.swing.Box extends related functionality. It
> is
> > rare, hence why I almost never extend any class.
>
> I hardly understand you. I'm asking on how do you
> build your objects if you need UI for an Order, and
> UI for a list of Clients?
You build an Object through composition that creates the UI components for this and handles any of the additional functionality, such as listeners. If it were nothing more than a container for the UI components then it could be a JPanel as you suggest. However, it's the additional functionality that is almost always needed that makes it not a JPanel.
> > > I do need to add additional responsibilities to
> > > JPanel.
> >
> > Are these responsibilities part of the JPanel
> itself
> > or is the JPanel simply part of how it carries out
> > it's responsibilities? Is every single method
> > inherited and it's implementation directly related
> to
> > the functionality of the class? If it's not, then
> it
> > shouldn't be extending a JPanel.
>
> Yes responsibilities are part of a Panel (an Order UI
> Panel). Yes methods are inherited - what other can
> you expect?
I can expect it to be misused. Isn't that the debate? If your Order UI should inherit and implement all functionality of a JPanel and provides no functionality that isn't part of the UI then it's not necessarily a bad thing. I think the argument is about when you add functionality that isnt' part of the UI, yet extend a UI component.
> > Every analogy is subjective. Could you point out
> > what part of the analogy is invalid?
>
> The part which is invalid is that it is subjective.
Every analogy is invalid?
> > > Then we seem to have different meaning for
> > > "procedural", or at least we can name the design
> > as
> > > "more object-oriented" or "less
> object-oriented".
> > If
> > > my design manipulates with objects which can be
> > > instantiated, extended, but some part of my code
> > > can't, I would say this part of code is less OO.
> >
> > The design manipulates Objects. I fail to see how
> no
> > permitting direct instantiation by a client
> creates
> > procedural code. Concrete inheritance violates
>
> What if you would need two instances?
Then the class would permit it. If a class is defined such that no two instances are identical, then you shouldn't have two identical instances. What if you need two? If you need two identical instances of a class that should never have two identical instances then you need to make your own, or rethink your design.
>
> >Concrete inheritance violates
> > encapsulation, a key OOP. A constructor often
> times
>
> I don't think so. See
> http://dkrukovsky.blogspot.com/2005/06/is-inheritance-
> bad.html
I don't see anything there that contradicts what I said. I never said inheritance was "bad", I said it violates encapsulation. When a class inherits from another class it inherits all of it's functionality and in Java this exposes implementation because the implementation directly affects the subclasses usage. Joshua Bloch demonstrated the problems with it more articulately than I ever could so I'll defer to whatever item that was in his book for an example of what I mean.
> >A constructor often times
> > does as well because it exposes the implementation
> by
> > exposing how the Object is created. Static
> factory
>
> No. Constructor exposes what it needs for object to
> be created, not how.
In an ideal world, yes. Note that I never said all constructors expose implementation, merely that some must. What happens when you can't connect to a file you need during initialization? You either throw an unchecked exception that blows up unexpectedly in the client's face or you force them to use a checked exception. Is the exception an implementation detail leaking out? What happens when I have a DAO and implementations that use an XML-RPC, database, and local files. Each has it's own requiremetns for initialization, but the only way to prevent having to check state is to throw an exception. If it's an IOException because the file couldn't be found this is an implementation detail. You could wrap them all in some "InitializationException" but then you're just forcing the client to go through the trouble of catching it when they probably can't do anything with it. I'll take an if (ob == null) over a try { ob = new Object()) catch {InitializationException e) { // do nothing } any day, especially since optimizations that could otherwise be applied can't be.
With a static factory method you can check the state of an Object after it's initialized and before returning it. Then no exceptions that derive from an implementation detail escape nor is a checked exception required nor is the client required to check the Object's state.
In many cases a constructor is fine. In most, in my experience, it's probably not a good idea. That doesn't mean all are bad, just that static factory methods can be used to wrap them to keep implementation from leaking or causing the client unnecessary headache.
> > Static factory
> > methods are often used to encapsulate creation and
> > initialization so that it is not exposed, I would
> > call that more OO not less OO.
>
> Encapsulation - ensuring that users of an object
> cannot change the internal state of the object in
> unexpected ways; only the object's own internal
> methods are allowed to access its state.
> Shall I explain more?
If the client has to check it's state to determine it's been initialized correctly, then that violates it.
> Now thanks for your example of GUI desing. Would you
> tell us such parameters as the number of lines of
> code and Coupling Between Objects rate for your
> AbstractQuery class?
>
>
> jschell,
> > > Modularity. The code responsible for maintaining
> > > JPanel content stays with it.
> >
> > That statement is subjective and just wrong for
> > non-trivial applications.
> >
> > It might be 'nice' to have my customer window also
> > have the database code for storage, but in a
> > non-trivial application that is always an
> incorrect
> > choice to make. It does however keep all of the
> code
> > in one place.
>
> Why it is wrong? It is correct if you note that we
> can distinguish between UI logic and DB logic.
>
>
> Denis Krukovsky
> http://dotuseful.sourceforge.net/
> http://dkrukovsky.blogspot.com/
The premise is the DB logic and the UI logic are in the same class. I don't think that makes them distinguishable.
>
> jschell,
> > > Modularity. The code responsible for maintaining
> > > JPanel content stays with it.
> >
> > That statement is subjective and just wrong for
> > non-trivial applications.
> >
> > It might be 'nice' to have my customer window also
> > have the database code for storage, but in a
> > non-trivial application that is always an
> incorrect
> > choice to make. It does however keep all of the
> code
> > in one place.
>
> Why it is wrong? It is correct if you note that we
> can distinguish between UI logic and DB logic.
>
Basically because "objects'" are supposed to do one thing not many.
> Basically because "objects'" are supposed to do one
> thing not many.
This can include other necessary logic though can't it? For example, an ArrayList can both "add" and "remove". They are two parts necessary two achieve the "one" thing it's doing, right?
That's my understanding at least. From there it's only logical then that anything outside of this objects "purpose" does not belong in that object. I would say a JPanel's purpose is to contain UI components, therefore any logic relating to the interaction between those components, such as a button click populating a field, would not belong in the JPanel's logic or by extension any subclasses logic. To contrast this, however, if you wanted to extend JPanel's functionality to have all UI components be transparent to show a background image present in the JPanel that logic could reasonably belong in the JPanel.
Am I wrong?
I thought I would weigh in on this - on the side of not extending..
1) "Extension" versus "decoration", inheritance versus composition in JPanel example
(someone): Do either change the JPanel class or do they decorate it?
Dkrukovsky: They extend the responsibilities of JPanel. What exact meaning you give to "decorate" here?
This is the key issue. I think the original question could have been better stated as, "do they extend the essential functionality of JPanel or are they contained by it?"
Dkrukovsky stated that his components "extend the responsibilities of JPanel". As pure English, this is correct. Certainly the resposibilities of the JPanel have increased. But they key word is "extend" - an OOP term.
What is happening here is not a modification of the essential behavior of JPanel. JPanel is a descendant of Container - its purpose in life is to contain and display GUI elements. Its responsibilities have not been extended. Entirely new responsibilities which have nothing to do with the basic nature of JPanel have been added to it.
(someone): What would you say about behaviors of, say, a HashMap containing components for editing an Order comparing to a HashMap containing components for viewing a list of Clients?
Dkrukovsky: A subjective analogy. Let's keep on main question.
Actually, that is a *perfect* analogy. A HashMap is a container that contains data objects. A JPanel is a GUI container that contains GUI elements. Extending JPanel to include code which operates on those elements is very much like extending HashMap to provide code which operates on its contents.
I could extend HashMap to provide a method which saved the HashMap's contents to file, but I think we can all agree that this would be an abuse of inheritance. I would not be extending the basic functionality of the class. I would be adding unrelated new functionality.
2) Modularity, reuse, extendability
(someone)L: Let me ask you this: what rationale do you have for extending JPanel to add components to it? What does doing this give you?
Dkrukovsky: Modularity. The code responsible for maintaining JPanel content stays with it.
This is not a reason. The code stays with the JPanel and the JPanel is reusable whether you solve this problem using inheritance or composition.
In the case of inheritance, the class can simply be freely extended, and can be freely instantiated and added to a higher level container.
In the case of composition, the class can still be freely extended. It can also be reused as long as you provide a public method to get a reference to its JPanel. There are two obvious ways to do this, which I discussed in a previous post. The first is to turn the complex GUI class into a factory:
JPanel instance = MyGUIThingy.instantiateAndReturnPanel();
The static method instantiates the complex GUI object and returns its JPanel.
The second is simply to provide a public getPanel() method and instantiate the complex GUI manually. Either way the class remains extendible and the JPanel can be freely accessed and modified at runtime. Unfortunately, the example code that Jschell provided a few posts ago did not include either of these so I can see how Dkrukovsky would think the code is not reusable. However, it would be trivial (as JSchell said) to change this.
Dkrukovsky: Now you guys who think we should stop extending UI classes, would you provide some of your UI code to prove it?
It is not terribly clear to me what Dkrukovsky is asking for here, but I think he wants to see that the non-inheritance pattern will work.
http://www.immortalcoil.org/drake/example.zip
That is the complete code to date for the project I am currently working on, and the GUI parts follow the factory pattern just described. If you open MainGUI.java or ReadingQuizGUI.java you will see.
3) Uniformity, logical grouping
There is one observation that I would like to make that I think will trigger the "something is wrong here" instinct in anybody. I do not think that anybody who writes GUIs extends EVERY component. But they are extending their top level containers and in many cases some lower level containers as well. Does it not strike you as odd that you should be using two patterns to do the same thing in the same app? Specifically, why are you extending some containers, and not others, even though they are both having their functionality "extended" (supplemented)?
The answer is probably grouping. Let's say you have an application with one JFrame containing a menu bar and two JPanels. The JPanels are on a CardLayout and are switched between so that one is viewable at any given time. One is extremely simple - it just says "Welcome" or something. The other has all kinds of things going on. I suspect that those in the inheritance camp would design this as two classes - one for the JFrame including the simple JPanel, and one for the complex JPanel. Otherwise they would have to put all that complicated code into their JFrame class.
This smells. Your inheritance tree should be dictated by character of functionality - not amount of functionality or object members.
With the composition pattern your inheritance tree is not dictated by your grouping.
4) Why this issue is so confusing.
First, the people at Sun have created some of this confusion and many tend to take what they do as gospel. The GUI extension pattern is common in the examples that we get from Sun and on top of that, there does not even seem to be any way to make an applet without extending.
Second, a visible GUI element creates a special and misleading impression on the human mind. Because it represents the aspects of the program that are visible to a person the person tends to have a special affinity for it and to think of it as if it were more than it is.
This is especially true of the top level container. To your eyes, every part of the program is IN that top level container. There is a certain satisfaction in the idea that everything in the program is a member of that JFrame and that when all is said and done the program is a JFrame. Unfortunately human models and good OOP models do not always line up this way.
The more complicated your GUI becomes the more the falsity of that symmetry becomes apparent. It is fairly easy to see menus, listeners and what not as inherent to the JFrame (as a class). But say you have an "open" command in a menu on the JFrame and this fires up a JFileChooser as pure UI logic before any business logic comes into play. Can you keep a straight face while calling the JFileChooser a kind of JFrame functionality?
(someone): ...just a container, as dubwai said you're just "decorating" it (i like that term) rather than changing the behavior of the JPanel itself.
Dkrukovsky: I do need to add additional responsibilities to JPanel.
This states the confusion succinctly I think. You need your object JFrame to have components with functionality. You do not need the JFrame class to be anything other than what it already is. Your program is not a JFrame. It has a JFrame.
Anyway given that the modularity issue does not apply I think we can fall back on the solid principle that you should not extend when you can avoid it very easily. Clearly that is the case here, unless I am missing something big.
Like I said, I think the actual damage caused by extending is close to zero, but principle says thou shalt not in this case.
Drake
dubwai!
> > My point is that the class you provided is not
> > reusable. I gave you a set of situations where you
> > can't reuse it,
>
> No you didn't. You gave me a bunch of requirements
> that had no relationship to the original design.
You asked for situations where your class would be not reusable and you got them. You can name them as having no relationship to the original design. But extension approach can be reused to present the user an editable list of names, to be part of Order where Name is part of it - where your class can't. Can we state that extension approach is more reusable?
You can go further and add some factory method to your NameFrame which would provide some needed Component. But then the question is - why do I need a component factory if I just need a Component? Which approach is simpler?
> > and you provided no example which can
> > do the job and reuse your NameForm.
> > By "reusable" I mean that the class can be reused
> > without changes.
>
> The example I gave was a trivial proof-of-concept.
Unfortunately your proof of concept requires modifications meeting with changed requirements. Can we have a better approach which is not requires to be changed?
> How about this. Create a class that does what you
> u originally asked me to do using your extension
> approach. Then when you are done and have posted it.
> I will give you some arbitrary requirement. Your
> r class will have to fufill it without changes. OK?
Unfortunately it is very difficult to create absolutely reusable class, and I'm not going to do it. The thing we can discuss is which approach will be more reusable?
> > Note that if custom UI class extends JComponent it
> > can be reused to present the user an editable list
> of
> > names, to be part of Order where Name is part of
> it,
> > and such.
>
> Why do you think you have to extend JComponent to do
> this? Show me why this is necessary. Give me and
It is useful approach. You can get a custom component which is itself responsible for its creation, and the component can be reused everywhere in Swing.
> this? Show me why this is necessary. Give me and
> exmple of this using extension and I will show you
> how to do the same thing without extending anything.
> There is no doubt in my mind that I can do this.
> . Put your money where your mouth is. You are the
> one making unorthodox claims. How about proving them?
Probably you can. But which approach will be more friendly to changes? Which one will be "more OO"?
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
Just for reference, here is non-complete piece of sample UI extending JComponent in pseudocode
class PersonUI extends JPanel {
PersonModel model;
JTextField fName;
JTextField lName;
public PersonView(PersonModel newModel) {
model = newModel;
fName = new JTextField(model.getFName());
add(fName);
lName = new JTextField(model.getLName());
add(lName);
}
public String getFName() {
return fName.getText();
}
public String getLName() {
return lName.getText();
}
}
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
kablair!
> > > The thing is that the given responsibilty
> doesn't
> > > belong in a GUI framework class.You can get
> the
> >
> > How it can be the responsibilty doesn't
> > belong to a GUI if I need a GUI?
> >
> > > same information about a JPanel from a class
> that
> > > doesn't extend JPanel. Is the issue here that
> you
> > > don't understand how this would be done?
> >
> > Show me how.
>
> It needs a GUI, but the functionality it needs isn't
> part of the GUI itself.
So put GUI functionality into GUI, and keep other otherwhere. Isn't organizing JTextFeilds and labels on JPanel the part of the GUI?
> You build an Object through composition that creates
> the UI components for this and handles any of the
> additional functionality, such as listeners. If it
> were nothing more than a container for the UI
> components then it could be a JPanel as you suggest.
> However, it's the additional functionality that is
> s almost always needed that makes it not a JPanel.
I agree here and I don't know why you are arguing. Organize GUI components using a class extending JPanel and keep additional functionality outside. Do we have misunderstanding here?
> I can expect it to be misused. Isn't that the
> debate? If your Order UI should inherit and implement
> all functionality of a JPanel and provides no
> functionality that isn't part of the UI then it's not
> necessarily a bad thing. I think the argument is
> about when you add functionality that isnt' part of
> the UI, yet extend a UI component.
No, I do not accept non-GUI functionality in GUI classes.
> > > > Then we seem to have different meaning for
> > > > "procedural", or at least we can name the
> design
> > > as
> > > > "more object-oriented" or "less
> > object-oriented".
> > > If
> > > > my design manipulates with objects which can
> be
> > > > instantiated, extended, but some part of my
> code
> > > > can't, I would say this part of code is less
> OO.
> > >
> > > The design manipulates Objects. I fail to see
> how
> > no
> > > permitting direct instantiation by a client
> > creates
> > > procedural code. Concrete inheritance violates
> >
> > What if you would need two instances?
>
> Then the class would permit it. If a class is
> defined such that no two instances are identical,
> then you shouldn't have two identical instances.
> What if you need two? If you need two identical
> l instances of a class that should never have two
> identical instances then you need to make your own,
> or rethink your design.
In OO world evetything should be an object. Name me an object to which static method refers to, and tell me how can I have more than one instance of it, and how can I benefit from OO features like inheritance and polymorphism with it. Unfortunately I can't, so I don't have these OO features with static methods, so I name it less OO.
> I don't see anything there that contradicts what I
> said. I never said inheritance was "bad", I said it
> violates encapsulation. When a class inherits from
> another class it inherits all of it's functionality
> and in Java this exposes implementation because the
> implementation directly affects the subclasses usage.
> Joshua Bloch demonstrated the problems with it more
> e articulately than I ever could so I'll defer to
> whatever item that was in his book for an example of
> what I mean.
Yes inheritance can be treated as a problem if you view your hierarchy as separated classes. I'm giving you a view where it is not a "problem" but something what is expected. Your Car IS A Vehicle, so when you do change your Vehicle you do expect your Car to be affected, right?
> > >A constructor often times
> > > does as well because it exposes the
> implementation
> > by
> > > exposing how the Object is created. Static
> > factory
> >
> > No. Constructor exposes what it needs for object
> to
> > be created, not how.
>
> In an ideal world, yes. Note that I never said all
> constructors expose implementation, merely that some
> must. What happens when you can't connect to a file
> you need during initialization? You either throw an
> unchecked exception that blows up unexpectedly in the
> client's face or you force them to use a checked
> exception. Is the exception an implementation detail
> leaking out? What happens when I have a DAO and
> implementations that use an XML-RPC, database, and
> local files. Each has it's own requiremetns for
> initialization, but the only way to prevent having to
> check state is to throw an exception. If it's an
> IOException because the file couldn't be found this
> is an implementation detail. You could wrap them all
> in some "InitializationException" but then you're
> just forcing the client to go through the trouble of
> catching it when they probably can't do anything with
> it. I'll take an if (ob == null) over a try { ob =
> new Object()) catch {InitializationException e) { //
> do nothing } any day, especially since optimizations
> that could otherwise be applied can't be.
If the constructor you are calling may fail, your calling code must be prepared for it, so there should be no "can't do anything with it". If you want null then you can keep your code calling constructor in one place and return null, but don't pretend constructors leak implementation. It's badly written constructors which throw implementation-specific exceptions.
> With a static factory method you can check the state
> of an Object after it's initialized and before
> returning it. Then no exceptions that derive from an
> implementation detail escape nor is a checked
> exception required nor is the client required to
> check the Object's state.
Encapsulation violation.
> > Encapsulation - ensuring that users of an
> object
> > cannot change the internal state of the object in
> > unexpected ways; only the object's own internal
> > methods are allowed to access its state.
> > Shall I explain more?
>
> If the client has to check it's state to determine
> it's been initialized correctly, then that violates
> it.
We shall have some axioms to be able to say "more OO" or "less OO". Encapsulation is one of them.
> The premise is the DB logic and the UI logic are in
> the same class. I don't think that makes them
> distinguishable.
They should be separated.
Denis Krukovsky
http://dotuseful.sourceforge.net/
http://dkrukovsky.blogspot.com/
> Just for reference, here is non-complete piece of
> sample UI extending JComponent in pseudocode
The following is more resuable and doesn't extend JPanel.
class PersonUI {
PersonModel model;
Container container;
JTextField fName;
JTextField lName;
public PersonView(PersonModel newModel, Container c) {
model = newModel;
container = c;
fName = new JTextField(model.getFName());
c.add(fName);
lName = new JTextField(model.getLName());
c.add(lName);
}
public String getFName() {
return fName.getText();
}
public String getLName() {
return lName.getText();
}
}
> > Basically because "objects'" are supposed to do
> one
> > thing not many.
>
> This can include other necessary logic though can't
> it? For example, an ArrayList can both "add" and
> "remove". They are two parts necessary two achieve
> the "one" thing it's doing, right?
An inventory control system only does "one" thing. Does that mean you are going to have only one object?
>
> That's my understanding at least. From there it's
> only logical then that anything outside of this
> objects "purpose" does not belong in that object. I
> would say a JPanel's purpose is to contain UI
> components, therefore any logic relating to the
> interaction between those components, such as a
> button click populating a field, would not belong in
> the JPanel's logic or by extension any subclasses
> logic. To contrast this, however, if you wanted to
> extend JPanel's functionality to have all UI
> components be transparent to show a background image
> present in the JPanel that logic could reasonably
> belong in the JPanel.
>
> Am I wrong?
I was referring to having db logic in the same object. And the general idea that one object should only do one thing.
I am not the best person (or even average) to ask what gui parts should be with other gui parts. But I doubt that an object that did a customer order screen should also contain the logic to enter each individual order.
>
> You asked for situations where your class would be
> not reusable and you got them. You can name them as
> having no relationship to the original design. But
> extension approach can be reused to present the user
> an editable list of names, to be part of Order where
> Name is part of it - where your class can't. Can we
> state that extension approach is more reusable?
>
Nope - not without an actual history of reuse.
> You can go further and add some factory method to
> your NameFrame which would provide some needed
> Component. But then the question is - why do I need a
> component factory if I just need a Component? Which
> approach is simpler?
There are many simple approaches that are inappropriate in terms of the overall goals of the project. It would certainly be simpler for me if applications did not use GUIs and did nothing but batch processing. But I suspect, for example, that banking customers might take exception to requesting cash one day and then coming back the next to actually pick it up.
>
> Probably you can. But which approach will be more
> friendly to changes? Which one will be "more OO"?
>
I suspect you think that OO relates only to inheritence? Or do you feel that the suggestion of many real users (via journals, books and web) based on experience who favor composition over inheritence does not apply in this case?
>
> In OO world evetything should be an object.
That isn't true in the pratical world of programming. There are many parts for many reasons in any non-trivial application which are not objects.
>
> Yes inheritance can be treated as a problem if you
> view your hierarchy as separated classes. I'm giving
> you a view where it is not a "problem" but something
> what is expected. Your Car IS A Vehicle, so when you
> do change your Vehicle you do expect your Car to be
> affected, right?
>
That depends on the problem domain.
I certainly don't expect the behavior of the car that I drive around every day to change simply because there are now some cars now that are powered by electricity.
> If the constructor you are calling may fail, your
> calling code must be prepared for it, so there should
> be no "can't do anything with it". If you want null
> then you can keep your code calling constructor in
> one place and return null, but don't pretend
> constructors leak implementation. It's badly written
> constructors which throw implementation-specific
> exceptions.
>
Again that depends on the problem domain.
For most applications they will never "do" anything when a memory exception occurs. The problem domain for most applications does not include running out of system memory.
Some will. Although even then I suspect that it more likely to happen if a few cases rather than every case. Do you write code with the expectation that the String constructor will fail?
> You asked for situations where your class would be
> not reusable and you got them. You can name them as
> having no relationship to the original design. But
> extension approach can be reused to present the user
> an editable list of names, to be part of Order where
> Name is part of it - where your class can't. Can we
> state that extension approach is more reusable?
That's true. What's unstated is that it must be within the context of the original design and intent. Without that context no object is "reusable" because no object can be reused in every situation where it was not intended for use. You didn't so much point out situations in which the methodology is flawed as simply come up with a requirement that had nothing to do with the design intent.
> You can go further and add some factory method to
> your NameFrame which would provide some needed
> Component. But then the question is - why do I need a
> component factory if I just need a Component? Which
> approach is simpler?
While a client may need access to a Component it does not mean that your class is that Component. When you need to implement new functionality that is not part of that Component, rather than extending the Component you should be providing access to a Component you use to implement the functionality.
Off the top of my head I'll try and give you a concrete example of what I mean. One of the abuses of inheritance I've seen is with TableCellRenderer's extending Swing components. It's what Sun did with their DefaultTableCellRenderer and what they show as an example in the tutorial. As far as I'm concerned, it's a horrible abuse of inheritance.
A DefaultTableCellRenderer is not a JLabel. All the evidence you need of this is that invalidate(), repaint() and many other methods are overridden to do nothing. When you find that a subclass has to override methods to prevent their implementation being used precisely because they should never be called on the subclass then it has no business extending the class it's extending. A DefaultTableCellRenderer is not a JLabel, JComponent, Container, Component, MenuContainer, or ImageObserver yet by extending JLabel they've made it all of those types! It's expected to have all that functionality when almost none of it belongs to it!
This is a prime example where composition should have been used, not inheritance. A DefaultTableCellRenderer provides functionality that wraps a JLabel. It modifies the JLabel based upon the parameters passed to it in the methods defined by TableCellRenderer. All of the implementation details concerning a JLabel and all of it's supertypes should never have been exposed, yet they are.
With composition functionality can still be easily added at a later date. This functionality will have the added benefit of not having to worry about the implementation of supertypes and how new functionality will interact with it. Furthermore, with inheritance anything added to a supertype in the future will be added to the subtype, even though it's likely to be completely in appropriate since the subtype is not intended to have most of the functionality it's supertypes do. Worse yet, these future additions to the supertype may break the subtype because the subtype is dependent on the specific implementation (not just the API) of the supertype. Even worse than a simple change in implementation is what happens when a supertype adds a method with the same signature as a subtype where the subtype had a lower visibility. A seemingly harmless change to JLabel, JComponent, Container, or Component could accidentally contain the same signature as a private method of the subtype, making the subtype incompatible resulting in errors at runtime.
On the contrast, with composition any functionality that's desired in the DefaultTableCellRenderer must be added. So long as JLabel stays within the specifications of it's API the DefaultTableCellRenderer will never break regardless of implementation. If the internal implementation using a JLabel ever needs to change it can do so completely invisible to the clients using it.
I think that's about the most concrete example there is. It's part of J2SE and I 'm more than able to argue the point of composition versus inheritance in this case. It's a clear cut example of a situation where inheritance was totally inappropriate. A Component needs to be returned, but the class returning it is not itself a Component nor should it be. It needs to implement new and unrelated functionality. If you want an example of how a TableCellRenderer is done better I'll be happy to post one.
> > > and you provided no example which can
> > > do the job and reuse your NameForm.
> > > By "reusable" I mean that the class can be
> reused
> > > without changes.
> >
> > The example I gave was a trivial proof-of-concept.
>
> Unfortunately your proof of concept requires
> modifications meeting with changed requirements. Can
> we have a better approach which is not requires to be
> changed?
If you change the requirements of a specific object from that of the original design it always requires change. An object is reusable when the requirements fall within the scope of the original design. This is why it's important to design objects as generically as possible and in as module as possible so that they can be reused in the broadest number of contexts. I think your example is rather like changing the requirements from being an "Orange" to an "Apple". An orange is not an apple, an object designed to be an orange should not be reusable as an apple. Yes, more analogies, I know how you hate them. A better change in requirements would be to resuse an orange to make orange juice.
His original design had very specific intent, it was not supposed to work in the context which you suggested. The funny thing about inheritance with UI components is that quite often they are reusable in ways they shouldn't be which leads to problems. A DefaultTableCellRenderer can be reused in both a JTable and a JPanel, but it shouldn't be. Doing so will result in erratic and undesired behavior that violates the API and contracts of it's supertypes. A DefaultTableCellRenderer is not a JLabel and should never be used as one, in fact, in order to use one safely you would have to delve into the actual implementation of it. That's bogus.
> > How about this. Create a class that does what you
> > u originally asked me to do using your extension
> > approach. Then when you are done and have posted
> it.
> > I will give you some arbitrary requirement. Your
> > r class will have to fufill it without changes.
> OK?
>
> Unfortunately it is very difficult to create
> absolutely reusable class, and I'm not going to do
> it. The thing we can discuss is which approach will
> be more reusable?
I don't think it's difficult so long as you constrain "absolutely reusable" to mean always reusable where the requirements fall within the context of what the object does. It's when we add functionality to objects that the functionality does not belong in that we end up causing them to be unusable in contexts where they should be usable.
> > > Note that if custom UI class extends JComponent
> it
> > > can be reused to present the user an editable
> list
> > of
> > > names, to be part of Order where Name is part of
> > it,
> > > and such.
> >
> > Why do you think you have to extend JComponent to
> do
> > this? Show me why this is necessary. Give me and
>
> It is useful approach. You can get a custom component
> which is itself responsible for its creation, and the
> component can be reused everywhere in Swing.
It is a useful approach if and only if the component is in fact everything it's supertypes are and is intended to be used in every way it's supertypes are and provides no new functionality that violates it's supertypes contracts and APIs and it in no way depends on specific implementation details. Unfortunately, in Java the very last condition is almost impossible to meet, so the best you can do is simply say that while it depends on implementation details, the supertypes implementations are well defined and the implementation details it depends on are guaranteed not to change.
> > this? Show me why this is necessary. Give me and
> > exmple of this using extension and I will show you
> > how to do the same thing without extending
> anything.
> > There is no doubt in my mind that I can do this.
> > . Put your money where your mouth is. You are
> the
> > one making unorthodox claims. How about proving
> them?
>
> Probably you can. But which approach will be more
> friendly to changes? Which one will be "more OO"?
>
>
> Denis Krukovsky
> http://dotuseful.sourceforge.net/
> http://dkrukovsky.blogspot.com/
Composition is almost always more friendly to changes because it is almost always more flexible. The only flexibility inheritance offers is the addition of relevent functionality in a supertype without having to change the subtype. Unfortunately that carries as many risks as it does benefits.
> So put GUI functionality into GUI, and keep other
> otherwhere. Isn't organizing JTextFeilds and labels
> on JPanel the part of the GUI?
I think we diagree on whether or not the functionality you advocate putting into it is really GUI functionality.
> I agree here and I don't know why you are arguing.
> Organize GUI components using a class extending
> JPanel and keep additional functionality outside. Do
> we have misunderstanding here?
Apparently.
> No, I do not accept non-GUI functionality in GUI
> classes.
Perhaps, but you seem to extend GUI components in order to add new, unrelated, functionality because it's easier than composition in the short run.
> > > > > Then we seem to have different meaning for
> > > > > "procedural", or at least we can name the
> > design
> > > > as
> > > > > "more object-oriented" or "less
> > > object-oriented".
> > > > If
> > > > > my design manipulates with objects which can
> > be
> > > > > instantiated, extended, but some part of my
> > code
> > > > > can't, I would say this part of code is less
> > OO.
> > > >
> > > > The design manipulates Objects. I fail to see
> > how
> > > no
> > > > permitting direct instantiation by a client
> > > creates
> > > > procedural code. Concrete inheritance
> violates
> > >
> > > What if you would need two instances?
> >
> > Then the class would permit it. If a class is
> > defined such that no two instances are identical,
> > then you shouldn't have two identical instances.
> > What if you need two? If you need two identical
> > l instances of a class that should never have two
> > identical instances then