aspose file tools*
The moose likes Java in General and the fly likes Inheritance nightmare. Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "Inheritance nightmare." Watch "Inheritance nightmare." New topic
Author

Inheritance nightmare.

Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
EDIT: If you're just visiting this (convoluted) thread you may want to skip down a few posts before you start reading.

I'm writing some objects to do imaging, the idea being to create a small library of reusable objects for displaying images, scaling images, drawing text, shapes or other images (as an overlay) on top of them, etc. The most basic component needed is an "ImageLabel" that can paint an image, provide two simple methods to change the scale and be able to be used in Swing as a JComponent. There might be more than one implementation of this, for example I already have one that paints an Image using AWT and one that paints a RenderedImage using JAI, so I want to use an interface so that clients don't have to refer to a specific implementation. Initially I came up with:



Great, except ImageLabel is not a JComponent and can't be used as one. In fact, it can't be added to a GUI in any way without referring to a specific implementation.

There's only two solutions I've been able to come up with. Either add public JComponent getJComponent() to the ImageLabel interface or create an ImageLabel class that extends JComponent and provides those methods but does not provide a public constructor:



But that is ugly. It can be instantiated when it shouldn't be and it prevetns an ImageLabel from subclassing a subclass of JComponent (I don't think I'd ever do that anyway, but still). So for the moment, I've gone with adding the getJComponent() method to the interface because it's more flexible despite the fact that it's somewhat redundant as an ImageLabel is intended to BE a JComponent, not return one.

Wondering if anyone has insight into a better way of doing things or what to look out for. I really friggin' hate the fact that the Swing designers stuck us in this inheritance nightmare in the first place, but I suppose they had their reasons. Is there a better solution to achieve this?
[ December 09, 2005: Message edited by: Ken Blair ]
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

I agree that Swing should have been designed with interfaces rather than so many concrete classes. There are some unbelievable messes in there.

But in your case, I wonder if Swing doesn't already provide a sensible alternative. A number of components (chiefly JLabel and all manner of different JButtons) know how to work with Icons. Icon is an interface that represents a thing that knows how to draw itself. It has only three methods:

int getIconHeight()
int getIconWidth()
void paintIcon(Component c, Graphics g, int x, int y)

So what if you made your first example interface extend the Icon interface? Now you can say

ImageLabel myIL = ImageLabelFactory.createSomeRandomImplementation();
JLabel container = new JLabel(myIL);
myJFrame.getContentPane().add(container);

and every implementation of ImageLabel (I'd want to change that name!) just needs to know how to render itself to a Graphics2D, which, presumably, was already the case, and this client code doesn't depend on the implementation at all.

Whaddaya think?


[Jess in Action][AskingGoodQuestions]
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
You have encountered a language limitation that I call failure to achieve "Perfect Symbiosis of Contractual Operations". No (type-safe) language currently exists that permits achieving Perfect Symbiosis for any given point in time (Java laboriously permits it at a specific time). This is what I also attribute the recent trend to dynamically-typed languages to (since contracts and types are not intrinsic) - you'll find that your problem is easily solved in one of these languages by "loosening contract" i.e. no type-safety (though Java is not a complete type-safe language).

I am currently attempting to put together a publication to explain exactly what it is I'm on about, but the ContractualJ API Specification provides an example of an attempt to approach Perfect Symbiosis in the meantime. Currently, I'm at an estimated 40% complete.

All contracts (interfaces) declare one (and only one) operation, unless they are composite interfaces, in which case, they make an effort to approach "Perfect Symbiosis of contractual operations" (to be defined).

Sorry for providing an incomplete answer, but whatever the case, I do sympathise with anyone such as yourself who is suffering the consequences of the suboptimal tools that we are currently forced to live with.


Tony Morris
Java Q&A (FAQ, Trivia)
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
Normally when I run into problems like this I try to remember to not get focused too much on one aspect of it. I've come to realize that there was an inherent 'requirement defect' in my initial design, I was putting too much responsbility on one object. This library should essentially have:

1. An object that can paint an image of a generic type.
2. An object that can paint shapes and text over an image.
3. An object that can overlay an image over another image.
4. A JComponent that can display an image, shapes, text and an overlay.
5. A component usable in Swing that can display an image with shapes, text and an overlay in a scroll pane and provides a method of zooming (scaling) the image.

Of course it should also be done in a way that makes those objects as reusable as possible for adding new functionality or creating new GUI components. So what I really need at the lowest level is simply an object that, given a Graphics context and a Component, can paint an image of a generic type. ImageLabel did far more than that and even ImageRenderer is assigned too much responsibility because it provided scaling. I've revised ImageRenderer to thus:



Even the width and height methods are probably above and beyond what it should be responsible for. The problem is most of the components won't know what kind of image it actually is but will need to know how big it is. I could always wrap the image in some kind of interface, but that seems like it's going through too much trouble over such a minor issue. The only two objects that should *care* about the specific type is the ImageRenderer and the class that creates the ImageRenderer. Basically, once that ImageRenderer is created I want my library to not give a rats behind what kind of image it is or how it's being painted, just that it can be painted properly when passed a Graphics and Component. So this seems pretty okay to me, but I'm kind of confused on where to go from there.

I need some way to provide scaling. I'm wrestling with whether or not this should be an object that is passed an ImageRenderer, or if it should be an ImageRenderer itself that provides the additional functionality. However, in typing this I just realized that it must be an ImageRenderer itself otherwise it will have no idea how to perform the scaling. How do you scale an image if you have no idea what kind of image it is? So I guess it'll be something like:



But then I need to draw on top of the image, however I don't think that requires any knowledge of how the image itself is drawn. I just realized, typing this, that it doesn't need to know anything at all. If I just pass it a Graphics and a Component and tell it what it should be drawing then it doesn't matter what it's drawing over does it? Heck, I guess I could then use that outside of anything to do with images, just drawing period.



A painter is something that paints...something! Right? Then make an interface for each requirement:



Repeat for Shapes and overlays. Although now that I think about it an overlay is really just another ImageRenderer that has transparency painting over the top of the first one. Looking at the Painter interface is there any reason I can't just make ImageRenderer an "ImagePainter" and extend Painter? I'm not sure how to handle the "OverlayPainter" though.

From those interfaces it seems I could create GUI components that delegate painting to those interfaces. With composition I could create more complex components comprised of a few of those components and so on. Plus, I don't see any reason I couldn't reuse something like an ImageRenderer(Painter) if I wanted to paint the same image in two different places.

Ernest:

I have to strongly disagree with the suggestion. From the documentation an Icon is:


A small fixed size picture, typically used to decorate components.


I don't intend this to be small nor fixed size. While I suppose you could, I also don't really intend this to be used as an Icon in a JButton, JLabel, or anywhere you would normally use an Icon. Thank you for the suggestion though, sorry if this post (or the entire thread) is rather convoluted. I'm pretty good at writing good objects, designing an architecture, framework, library, etc. is definitely one of my weaker points so i'm kind of stumbling through this trying to get it right and looking for input.
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
Originally posted by Tony Morris:
You have encountered a language limitation that I call failure to achieve "Perfect Symbiosis of Contractual Operations". No (type-safe) language currently exists that permits achieving Perfect Symbiosis for any given point in time (Java laboriously permits it at a specific time). This is what I also attribute the recent trend to dynamically-typed languages to (since contracts and types are not intrinsic) - you'll find that your problem is easily solved in one of these languages by "loosening contract" i.e. no type-safety (though Java is not a complete type-safe language).

I am currently attempting to put together a publication to explain exactly what it is I'm on about, but the ContractualJ API Specification provides an example of an attempt to approach Perfect Symbiosis in the meantime. Currently, I'm at an estimated 40% complete.

All contracts (interfaces) declare one (and only one) operation, unless they are composite interfaces, in which case, they make an effort to approach "Perfect Symbiosis of contractual operations" (to be defined).

Sorry for providing an incomplete answer, but whatever the case, I do sympathise with anyone such as yourself who is suffering the consequences of the suboptimal tools that we are currently forced to live with.


No worries, I didn't understand this much of the answer so it's likely I wouldn't understand the rest had you written it!

Actually, in all seriousness I feel I have some idea of what you're talking about, which is that Java doesn't provide contracts that have one single responsibility and are symbiotic with other contracts. I don't know that contracts that are a composition are even necessary. I mean if you have List and Set (not necessarily in the context of Java's Collections since List and Set both exceed a single responsibility in the first place) you might need a ListSet contract that was the composition of the two. But if you could declare a type was a List and a Set then why would you need ListSet? Of course, the code could get awfully verbose.

Then again maybe I just totally missed the point. Regardless, Java and Swing is what I have to work with so I am interested to hear input, especially on my most recent post, on how to best go about it.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Inheritance nightmare.