When developing custom controls for JavaFX I would highly recommend to follow in the footsteps of the core JavaFX controls and to make the API of your controls as final as possible and to put the “final” keyword in front of all your method declarations.
Example
In FlexGanttFX I have a lot of code that looks like this:
public final Activity getActivityAt(double x, double y) { ... } public final Row getRowAt(double y) { ... } public final void setShowLinks(boolean show) { ... }
Why?
When you design a control you have a specific behaviour of the control in mind. This behaviour can be protected by using “final”. If you do not use it then other developers will subclass the control, they will override its methods, they will run into problems, and they will submit a defect ticket to your issue management tool. In the end you will waste your time on chasing their bugs. Bugs that only exist because your control was used in a way that you couldn’t possibly foresee.
Where?
Protection is especially needed for the public API of the control class, which is managing the “state” of the control. It is the contract between you and the user of your control. It is less true for the skin of the control, because skins are “implementation detail” and people modifying skins know that they are interfering with the inner workings of the control. You still want to make it easy for other developers to modify the “appearance” of the control.
New Concept?
Protecting your controls this way is not a new concept but it wasn’t used very much in Swing. The case for using “final” is stronger now because JavaFX controls are much more observable than Swing controls. They are exposing their state via properties and observable collections. This way applications can react to state changes by “listening” to the control instead of plugging themself into the control by overriding its methods. Another reason is the clean separation into “control” and “skin” in JavaFX. Swing components often had “skin” aspects right in the component itself and not in the UI delegate (e.g. paintComponent()).
[…] Lemmermann continues to post JavaFX-related tips, including ‘Have the Final Word‘, ‘Be Observable‘, ‘Use Transparent Colors‘, and ‘Use CSS Color […]
[…] Lemmermann continues to post JavaFX-related tips, including ‘Have the Final Word‘, ‘Be Observable‘, ‘Use Transparent Colors‘, and ‘Use CSS Color […]
Please don’t. The idea of making api methods final is understandable from the developer’s point of view for the reasons given above. For users, it will quite often situations where they cannot use a given module or control due to oversights in the original design.
When publishing an API you should never assume that your code is working perfectly or even remotely complete in a sense that you have covered all valid use cases. JavaFX itself is especially problematic as the developers knowingly left out concepts existing in swing because ‘you will never need them’.
Furthermore, when building a complex API I will often run into a case where I simply don’t understand why a control will render itself the way it does. Maybe there’s a misunderstanding, there might be a threading issue, a bug in the API or most likely I make a simple mistake but am too blind to see. Having the chance of overwriting methods is a simple way to hooking into the flow without resorting to crutches like aspects, as has helped me on countless occasions.
Building user interfaces is trivial when everything works as it should, but may likely become one of the most frustrating and time consuming things when the API does not work out.
Finally, there’s that situation where you’ve build this massive analytics application and the product owner says ‘Hey, that’s a great tool, but I really need errors bars for that chart’. And to which you’re left to respond: ‘Yeah, no, sorry, the API developer thought errors bars are useless and did not allow me to hook into his rendering routine…’
Obviously I am looking at this from a framework developer’s point of view. However, I do understand that the framework user would like to have the ability to override methods, either to modify the control’s behavior or to understand its inner workings. But I also believe that in the end this will only lead to greater confusion and dirty work-arounds. If something does’t work or is missing, then please talk to the framework developer, so that the feature is either fixed or added. Not being able to modify the control yourself might in the end be the more productive solution, maybe not for the individual, but for the entire group of framework users. It will lead to a better product because if forces you to talk to the framework developer and this ongoing discussion leads to better code.
I see your point, but honestly think it is a bit idealistic.
As far as I can tell, if a developer is missing a feature he will go through significant lengths to get it. If you make it harder for someone to implement, he will come up with a more complex and overall worse implementation, but rarely if ever talk to the developer.
Even if he does, and I for one do try to give the devs feedback, unless you’re talking about a truely unique project, it will not help him. Take a look at how often existing frameworks are being updated. Take a look at JavaFX, which still can be considered to be new and growing, and at the time between releases. Look at JFreeChart (which, incidentally, gives the user a lot of freedom), or really any other framework in the java world. If I need a solution for a commerical project, the one thing I cannot do is wait for 6 months to maybe receive an update.
Which leads directly to the many, many abandoned frameworks where the devs started with great ideas and simply ran out of time.
Finally, and I do agree that this is subjective, when I write code I want others to use, e.g. UI controls, I want them to get the best usability out of it. If a developer is inexperienced and still decides to overwrite methods that are not explicitly meant to be overwritten, chances are he won’t be producing great code anyway. If he is convinced that he knows what he is doing, I prefer to give him the opportunity at his own risk.
[…] The control class stores the state of the control and provides methods to interact with it. State information can be: the data visualized by the control (e.g. the items in TableView), visual attributes (show this, hide that), factories (e.g. cell factories). Interaction can be: scroll to an item, show a given time, do this, do that. The control class is the contract between your framework code and the application using the framework. It should be well designed, clean, stable, and final. […]
Remember what the guru said: “Classes should be open
for extension, but closed for
modification.”
[…] JavaFX design because subclasses should not be able to overwrite your methods. See for example also JavaFX Tip 4: Have the Final Word hence making them final is the […]