Reputation: 155
I'm admittedly new to using design patterns and I seem to be unsure on how to proceed here. I'm building custom graphical symbols in JavaFX. I'm just extending Parent and adding rectangles and circles to my symbol class and then display that symbol object on the scene.
This symbol class also registers event handlers, like setOnMouseEntered and many others, to alter the symbol in response to mouse events. What I want to ask is where does it make the most sense to store all of these event handlers? I'm not sure if they should go into a Controller class instead or if since they only effect the view of that symbol perhaps I should keep them in the symbol's class, and just reserve the Controller for things that have to get communicated from the View to the Model and vice versa. Thank you to anyone who can shed some light on this (or can point me towards good resources)!
Upvotes: 2
Views: 504
Reputation: 20780
View :
Controller :
Model :
I'm still learning about JavaFX and the technology in its whole seems not yet mature.
Feel free to browse a small PoC of mine written in JavaFX 2.2 (IDE : IntelliJ IDEA) at http://twittersearch.codeplex.com
Upvotes: 0
Reputation: 159416
General Advice
Rather than trying to work directly from abstract concepts, I advise looking at examples of these patterns implemented in JavaFX in existing code and seeing how they are coded there. When you find some patterns you like, you can emulate them in your code.
There is a difference in programming in the large where you are applying an MVC pattern across an entire application or scene and programming in the small where you apply a pattern across a specific control type. Your question seems more related to the later case, so I'll mostly consider that in this answer.
JavaFX Controls - Skin, Behavior and Public API
Have a look at existing controls code for JavaFX. For a Button
, you will have:
The ButtonBehavior
provides an abstraction api, so that a button can be bound to different platform and user specific event maps for key, mouse and touch combinations.
The ButtonSkin
allows the visual implementation of the control to be completely swapped in and out without modifying the public API or behaviour.
The Button
public API provides a stable API that users of a button can program to independent of any visual or behavioural changes to the control's implementation.
Removing Abstraction
While the skin and behavior APIs are great for abstracting away details of implementation for behaviour and look and feel, they do add complexity and overhead for controls implementors. You should consider whether the controls you are creating really require this complexity and evaluate alternate, simpler approaches.
The simplest approach is just to extend an existing Pane and place all of your logic in the single extension class (or even just provide a factory method which constructs your control nodes). You lose the abstraction and it's benefits of simple implementation interchangeability and separation of concerns, but you have a very simple implementation. An example of this straightforward extension mechanism is the Custom Control example in the Oracle Mastering FXML tutorial.
A slightly more sophisticated approach I've used is to have a class encapsulating public API and control state information and a separate skin class handling events or UI rendering. I like this approach because it clearly separates out the JavaFX specific logic and code from the underlying control state and public APIs without adding as much conceptual overhead as the behaviour/skin/public api approach of the standard JavaFX controls. Some example code using this approach is in this Tic-Tac-Toe game.
Communication between Skin/Behavior and Public API
Make use of the JavaFX property mechanisms within the class encompassing the control's public API. The properties can define the control's public API as well as encapsulate internal control state through types like ReadOnlyWrappers.
The skin can apply bindings and listeners to the properties so that when the listeners are fired, the skin can trigger an automatic update of itself to reflect the new control state. Users of the control can listen to or bind to the control's internal state to take action as that state changes. An example of this approach would be the selected property of a ToggleButton where the skin changes the look of the toggle button depending on whether or not the button has been selected.
When you create a skin, initialize the skin and behaviour with a back reference to the public api containing the control (e.g. SymbolSkin
and SymbolBehaviour
have a reference to Symbol
). A mouse click could be intercepted by a mouseclickhandler in the SymbolSkin or SymbolBehavior and toggle a selected property on the public api to the Symbol, then user code which listens to changes to the selected property on the symbol can take effect without that user code being tightly coupled to the symbol's skin or behavior.
The public API to Symbol
need only expose a single method getSkin()
which returns the skin as a simple node which can be utilized in a scene graph. By doing this the implementation of the skin and behavior are hidden from calling classes making use of the Symbol
public API.
Extend Region and make use of CSS
By having the control skin extend Region
, the control becomes styleable via css. I highly recommend this approach as this means that many visual aspects of the control (e.g. color fill, backgrounds, borders, shape, etc) can be manipulated in JavaFX's rich css style language.
When doing this, try to instrument all of the nodes in your control skin with separate css style classes. This provides hooks into the control's visual components that you can use to style those components. A sample of this approach to styling complex components is the Oracle tutorial on Styling Charts with CSS.
To define different symbols as in your question, you could create a single Symbol
control. Define a SymbolBehaviour
for handling it's user events and a SymbolSkin
which just extends Region
. Each symbol instance could have it's own style class assigned and the public Symbol API could provide a constructor or factory method which sets this style class on the symbol skin's region. Use the -fx-shape
css specifier to style the shape of the symbol based upon an svg path string.
The css styleclass names themselves can be hardcoded into your SymbolSkin
implementation and documented as part of the public API (as is done in the CSS reference guide for standard controls).
Ship your control with a default css template which defines the default style for the control.
FXML considerations
When you have a larger application or scene which is composed of many sub-controls that need to be co-ordinated, then you can make use of something like FXML to define the scene in a declarative fashion. This allows you to separate the UI design from your imperative programming code base and ensures you don't start writing imperative code in the view.
For a true MVC architecture with FXML, you would probably need more than just an FXML file and backing controller for a scene implementation, you would also need a higher level control mechanism for swapping in and out scenes, and perhaps more sophisticated event processing methods such as an event bus, but that is out of scope for this question.
Related
There is a slightly different, but related question which should help you understand the problem domain better (JavaFX 2.0 - style / template existing control).
Upvotes: 1
Reputation: 630
Since your intention is to alter the objects..
I would understand it the following way:
Model: Everything that represents your displayed data. In your case the symbol class, which contains all circles
Controller: if you modify your model (your symbol objects) in any way, then this is only allowed by using methods within your controller class. Controller is your layer, only through this layer you can access the Model for modification
View: Whatever you need in order to display the model can/should be placed in this part.
So it is up to you, if your program will 'memorize' the current selection, or if your model also has a variable for that. I think both approaches should work.
1.(in Model): In case of a variable in the model, then selecting a symbol would be an action that is called through the controller class (since it changes the model), and since the model would be changed, it would trigger the view class to update the view of the model. (view uses observer pattern to get new model data and redraw, whenever model changed)
2.(in your displaying program that uses View and Controller): If a selection is 'memorized' here, I think some confusion may be created, since the view differs (depending on the selection). Therefore I would suggest to go way 1.
Please read about OSR (One Single responsibility), I am not sure how to combine OSR with MVC, and if it would be better to extend your 'real model' to add a selection, since this keeps your model free of the editing objects that may not be needed in other classes that may use the same model. If you always intend to allow editing of your symbol classes, then don't worry about adding a selection variable to your model.
So about your events for mouse enter and click, try to be strict: You always handle user inputs by your program itself, but if it is an action that can be understood as a manipulation of the model, then you will call a method in controller. You are not forced to follow those MVC rules, but it should help you to keep your project much better to read, you can add multiple views that all automatically update, and extending should be more easy.
IF you are unsure about a structure, a typical approach is to try defining all possible solutions and then to compare them. Try to find a way that is simple (easy to understand and easy to extend). loose coupled, OSR and DRY are nice hints for that. General enough to try applying them as often as you can. But also keep in mind, if a solution just follows design patterns, but it 'feels' much too complex, then also think about building a simple class or another appraoch. Sometimes we build more complexity than needed. e.g. Factory and Builder pattern are nice design patterns, but if a class requires a very complex configuration, but not mutiple different constructions and does not relate on other objects, then.. a class with the code in the constructor may by a better approach.
As a good resource I recommend books "Head First Design Patterns" and "Head First Object-Oriented Analysis and Design" from OReally. Also books from the publisher Wesley are good. Usually you can find them at your universities library to borrow for free. Try to find real uses for design patterns, this helps a lot to understand them (why useful) and you get some experience about their usage.
Upvotes: 1