I’m planning to implement a word processor using Java, and Swing for the GUI.
I was thinking how I can implement the text area where the user types the text.
My idea was to subclass JTextArea
. It already has a lot of functionality for setting text, changing font size, etc. I will than add all the additional functionality, such as the ability to be affected by button presses on the UI, etc.
This seems reasonable to me, however I have a question regarding this:
Is it considered reasonable in a ‘serious’ program, to implement a major part of the program by subclassing an existing class of a common library? Is it a ‘serious’ and ‘professional’ thing to do?
Of course professional developers use and subclass existing code all the time, no point in reinventing the wheel.
But here I’m talking about implementing a major part of the application, probably the largest part, largely by subclassing an existing library class.
I am not a professional, and I know I don’t have to work ‘as professionals do’. But I am interested in how professionals view this.
1- Is something like this considered reasonable in a ‘professional’ application?
2- Have you ever seen this in practice in a ‘professional’ application?
4
First and foremost, I don’t know Swing and the capabilities or enhanceabilities of JTextArea
, so I can’t comment on your decision to make it the central point of your solution (which @DanPichelman seems to question in his answer). For the argument’s sake I assume that it’s fine.
Whatever is wrong with it, such technical details are better discussed on StackOverflow – we focus on general design here, and thus I’ll try to tackle the OOD aspect of your question.
You need to ensure that your class does not get too big. It can seriously hurt the maintanability of your code. How do you know when it’s too big?
The simpliest factor is the sheer size of its code. There is no magic number that separates “overly big” from “still fine”, but there’s common sense and some rules of thumb. I’m quite liberal in this aspect, but by my standards any class being bigger than 200 or 300 lines of code raises a red flag.
The conceptual rule is the Single Responsibility Principle. What is the responsibility of your class – let’s give it a working title of JRichTextArea
?
It’s displaying rich text. Displaying. That’s its field of expertise. It shouldn’t know a thing about the outside world, as it breaks encapsulation and adds a secondary responsibility to it.
The only form in which its “ability to be affected by button presses on the UI” should be implemented is exposing some public API allowing other agents to pass it content (in some understandable format) that it’s able to display, or render. There is no reason under the sun why it would have to know about button presses on the UI, or the very existence of any buttons, for that matter.
For instance, formatting rules (that’s the business logic in this case) are not of its concern. JRichTextArea
is a View, and it should be nothing but. Proper View is dumb and has no opinion about the data it displays. The alternative is known as a smart view anti-pattern.
Even rendering your content is not necessarily its job. If you wanted to add an option allowing your user to create and edit complex mathematical equations, should the mechanism be embedded in JRichTextArea
?
These equations would not only be drawn on the screen, inside the text area. Printing documents would require drawing them in a graphic buffer. So the mechanism would better be moved into some separate EquationsRenderer
class.
What about the content itself; the formatted text? It’s data – it’s not equal to its visual representation. The user will probably need to save formatted documents to disk. They are actually going to save the data, not its graphic representation from JRichTextArea
.
The content belongs then to some sort of a Model class. That would be consistent with the MVC pattern. It’s not a Holy Grail, but a good, universally recognized way to achieve separation of concerns.
Furthermore, interactions with the UI are a job for some sort of a controller class, delegating user commands to the model (eg. RichDocument
). The controller gets notified that such and such button was pressed (make the selected text bold, for example), and it tells RichDocument
what to do with the data.
JRichTextArea
is at the end of this chain and its only job is to obediently display the results of this chain of operations.
This is one example of what I would call a ‘serious’ and ‘professional’ approach here.
4
If it will work for what you want, then it’s a reasonable thing to do.
We see far because we stand on the shoulders of the giants who came before us. As serious professional programmers, we can and should take advantage of every available tool or library (within reason) that will make our job of producing a finished product quicker & easier.
BUT…
While I’m not personally familiar with JTextArea, I doubt it’s up to the task of being a complete word processor in a can.
According to the documentation,
It is intended to be a lightweight component …
You’ll need to test it to see what happens when you try to use it with 100+ pages of formatted text. Also, what happens when you try to print? Will word wrap, page breaks, etc. work as expected?
My guess is that you’ll be disappointed.
Many years ago Apple put out a TechNote essentially saying that their multi-column scrolling list box was not a spreadsheet. Their arguments were similar – the lightweight tool won’t scale up to application levels, performance will be terrible, and writing an entire app is more complicated than putting a wrapper around an existing UI element.
2
Some classes are designed to be inherited, and it’s OK to inherit them and base a large portion of the project on the inherited classes.
JTextArea
(and the rest of the Swing component classes) is not one of them. Unless you aim to create a new reusable component you shouldn’t inherit a component class.
JTextArea
– like many of the Swing component classes – provides a vast selection of hooks(a.k.a. listeners) that you can use to build on it’s functionality. This is the preferred way to use JTextArea
– not inheritance.
That being said – sometimes it’s more comfortable to inherit the class but use it via the hooks. That means you don’t override any method, but use the constructor to add listeners to your JTextArea
. This approach allows you to add fields and new methods to the component, that both the listeners and other classes could use. This is common with container component classes(eg. JFrame
, JPanel
), where child components are created in the constructor and the important ones are stored in fields, but if your JTextArea
is big enough this approach can be useful to you – as long as you avoid overriding JTextArea
‘s methods!
7
I’m going to ignore the specific class entirely and try to just answer your questions. As mentioned elsewhere, specifics are better handled on Stack overflow.
Is it considered reasonable in a ‘serious’ program, to implement a major part of the program by subclassing an existing class of a common library? Is it a ‘serious’ and ‘professional’ thing to do?
1- Is something like this considered reasonable in a ‘professional’ application?
2- Have you ever seen this in practice in a ‘professional’ application?
My answer is no, don’t do it, you will live to regret it. No, this is not a ‘professional’ approach. No, I have not seen it done well and those who have tried have wished they hadn’t.
As summarised here, there are good reasons for inheritance (subtyping and polymorphism) and then there is implementation inheritance, which is inherently evil in all but the most compelling cases.
Yes, if you are going to implement a new kind of button it makes a lot of sense to inherit from the Button class and add you own behaviour. You will be unable to preserve the encapsulation of your code because it will be all mixed up with Button code but it will probably be worth it.
However, if your new code merely has as number of features in common with a button then use aggregation. Include a button somewhere in your implementation to the extent that it makes sense to use button features, but keep it at a distance. Preserve your encapsulation. Keep layers between you and code supplied by others.
One way of looking at this is the “is a/has a” test. If your code really “is a” button then inherit away. If you code merely “has a” need for button features than include a button, but don’t inherit from it.
Another reason: the services of the class from which you think to inherit should be (from your point of view) substitutable. That is, you should be free at some future time to discard that specific class in favour of a different one. Instead of using the Button class, you should be able to switch over to using the SpecialButton class or the SuperButton class.
You ability to do this will be sharply controlled by the level of encapsulation you make around your code. If you inherit, that ability to switch is probably lost.
Finally: I think you can probably have it both ways. Write a (small) subclass that captures the essential features that you need, and use it to expose an API customised to your specific needs. Then write the rest of your code to consume that API as a service. If you need to substitute the inherited class you will only need to rewrite the small subclass to inherit a different parent class or consume a different service, and your internal API remains intact. That preserves your encapsulation.
On reflection, this is the path I would probably take. I can make it clearer what it means if needed.