1

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?

Aviv Cohn
  • 21,190
  • 31
  • 118
  • 178
  • As in, putting most of your code and logic in a single classes (that happens to be derived from a library class)? –  Apr 27 '14 at 14:09
  • @delnan No. I meant that a lot of the logic used for the most important things in the application will actually be inherited from the library class. That doesn't mean that the newly written code won't be separated into different classes – Aviv Cohn Apr 27 '14 at 14:13
  • How come a UI control representing a text area (with associated functionality) is a **major** and actually the largest part of your program? – Konrad Morawski Apr 27 '14 at 14:33
  • @KonradMorawsky Because it's a simple word processor. The GUI is a large area where you can type in and several buttons for adjusting font size, color, aligning text, etc. So a lot of this involves operations done *on* the text area itself, and almost all of them affect it (e.g. when the user presses a button that sets the text color to red, the text area needs to be notified in order to type the text in red from now on. The 'making letters in red' is part of it's functionality, and so is the 'be notified when the user presses the red button'. And these kinds of things is most of the program). – Aviv Cohn Apr 27 '14 at 14:39

4 Answers4

7

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.

Konrad Morawski
  • 9,721
  • 4
  • 37
  • 58
  • Thanks for the detailed answer :) I'm familiar with MVC, and have implemented a simple version of it in two small apps so far (MVC is obviously an overkill in a small app, but I'm practicing :) ). Please confirm if I understand what you mean: What you're saying is that the text area should only be the View - it should only display the data, and not include any other logic. The logic for formatting the text, aligning it, making it blue, making it bold, etc. should all be in the Model. And finally actions on the UI (be it the text area or some button) should be reported to the Controller [...] – Aviv Cohn Apr 27 '14 at 16:18
  • [...] which then manipulates the data in the Model accordingly. And finally the View gets updated according to the new data in the model. For example: User pressed 'red' button on UI **>** View tells controller `redButtonPressed()` **>** controller tells model `colorTheTextInRed()` **>** model colors text in red **>** View gets notified (often via Observer pattern) that the data in the model changed, and updates itself **>** The text it is displaying is now red. Is all of this correct? Thanks – Aviv Cohn Apr 27 '14 at 16:19
  • That's precisely the idea. Keeping `JRichTextArea` small is not a goal in its own right, but it'd be a (beneficial) side effect. With this type of architecture your project is logically structured and flexible. Otherwise how do you implement saving, printing etc. in a clean manner? Also, if one day you decide that `JTextArea` is no longer fit for the purpose (you need to add options of such nature and complexity that subclassing won't do the trick), you only need to substitute the View with something else, but you won't have to copy-paste all the logic from `JRichTextArea` to some new home – Konrad Morawski Apr 27 '14 at 16:32
  • I see. I'll keep Separation of Concerns in mind when designing things. Thanks :) – Aviv Cohn Apr 27 '14 at 16:38
5

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.

Dan Pichelman
  • 13,773
  • 8
  • 42
  • 73
  • Thanks for your answer. Personally, how would you go about implementing such a text-area in a word processor? Would you write the text-area class totally from scratch? – Aviv Cohn Apr 27 '14 at 16:11
  • I would base it off of the Netbeans platform, since they already have that problem solved. From https://netbeans.org/features/platform/index.html - "Building a desktop application on top of the NetBeans Platform can save years of development time." – Brian Knoblauch Apr 28 '14 at 20:06
3

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!

Idan Arye
  • 12,032
  • 31
  • 40
  • Thanks for answering. Say I gave up on using JTextArea at all - how would you go about implementing a text-area kind of thing in a simple word processor? Code the class from scratch? Base it on something else? – Aviv Cohn Apr 27 '14 at 16:30
  • To answer that, I first need to know *why* you give up on using `JTextArea`. I would at least subclass [`JTextComponent`](http://docs.oracle.com/javase/7/docs/api/javax/swing/text/JTextComponent.html) to get good integration with other Swing components, but depending on your reason for not using `JTextArea` maybe you won't want to use `JTextComponent` either. – Idan Arye Apr 27 '14 at 16:57
  • I'm not sure yet, but based on the fact that indeed it is meant to be used as a lightweight component, and both you and another guy here said that it's probably not the best choice, it's likely I'll look into another option. Why do you think JTextComponent might be a better choice than JTextArea? – Aviv Cohn Apr 27 '14 at 17:01
  • I've never said that `JTextArea` is not the best choice - I said that **subclassing** it is not the based choice, since it was designed to be used with listeners rather than by overriding it's methods. Also, I'm not saying that `JTextComponent` is a better choice than `JTextArea` - I'm saying that if you want to create your own custom text component that's not based off `JTextArea` you should consider `JTextComponent` since it's designed to be the base editable text class for Swing. – Idan Arye Apr 27 '14 at 18:08
  • "sometimes it's more comfortable to inherit the class but use it via the hooks" -- In that situation I'd make a class that has a `JTextArea` rather than subclasses it. **Has a** rather than **is a**. Subclassing is more for building an object that can *substitute for* an existing object and extend its code, or to specialize an object designed for subclassing. Subclassing causes tight interdependencies, e.g. does the superclass call methods everywhere you need to override? Overriding any method called by the superclass constructor is risky since the subclass initialization hasn't run yet. – Jerry101 Apr 27 '14 at 23:09
  • @Jerry101 If it was just for adding the hooks in the constructor there was no need for object composition - factory method for creating the `JTextArea` and adding the listeners would suffice. The benefits of subclassing only shows when you need to add your own fields and methods. In that case, it can be useful that your own class is the component, since you'll get a reference to it in the event-handler methods of the listeners. – Idan Arye Apr 28 '14 at 07:51
  • @IdanArye Yes, if a factory method suffices, do that. If a new class is needed, then there are 3 ways to build on `JTextArea`: subclass it, write a new component that delegates to a `JTextArea`, and write a container component that contains a `JTextArea`. Tradeoffs. – Jerry101 Apr 28 '14 at 09:39
1

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.

david.pfx
  • 8,105
  • 2
  • 21
  • 44