one question that always pops up when my java project gets bigger is if there is an easy way of making references to a specific object thats cannot be referenced by super or getParent(). The following graph should illustrates my current problem:
For every FileListItem I want to instatiate a new FileListItemController which needs methods from ProtocolController. Is there a way of referencing the ProtocolController object instatiated by main in the FileListItems other than passing it on through mainWindow, FileListContentPanel, FileListItems?
First thanks to all your answers.
I am using the model, view, control pattern for my project.
The singleton pattern sounds interesting but my feeling is that it does not solve my issue. Here is some code to illustrate my problem:
public class Main {
public static void main(String [ ] args) {
ProtocolController pc = new ProtocolController();
mainWindow mw = new mainWindow();
}
}
public class ProtocolController {
File protocol;
public ProtocolController(File protocol){
this.protocol = protocol;
}
public void writeSomethingToProtocolFile(String something){
// write something to th protcol file specified by the object
}
}
public class mainWindow {
public mainWindow(){
FileListContentPanel flcp = new FileListContentPanel();
}
}
public class FileListContentPanel {
public FileListContentPanel(){
int numListItems = 10;
for (int i = 0; i < 10; i++) {
FileListItem fli = new FileListItem();
FileListItemController flic = new FileListItemController(fli);
}
}
}
public class FileListItemController {
public FileListItemController(FileListItem fli){
}
public void addSomethingToProtocol(String something){
// at this point I want to use a method from the ProtocolController class instantiated by the main method
}
}
There are different approaches for this.
For instance, if you only need one ProtocolController instance, you could use the singleton pattern. Each FileListItemController is then able to retrieve the same ProtocolController object.
class ProtocolController {
private static instance;
private ProtocolController() { }
public static ProtocolController getInstance() {
if (instance == null) {
instance = new ProtocolController();
}
return instance;
}
}
You can then get ProtocolController.getInstance() from within FileListItemController and then call the desired method.
If you have only one instance of FileListItemController, you can use Singleton pattern with "Lazy instantiation".
And ProtocolController instantiates FileListItemController like this :
FileListItemController fileListItemControllerInstance = FileListItemController.getInstance();
EDIT
If each FileListItem references one FileListItemController, you can use a Factory (Abstract Factory Pattern)
In this case, your ProtocolController can instiate a FileListItemController factory. You can pass ProtocolController reference to the factory if needed.
Then FileListItem can use this factory to create new FileListItemController instances.
This looks like a use case for Model-View-Controller design pattern.
Your FileListItem objects are model (You'll need a new class to hold them). Both Controller and View (FileListControlPanel) get a reference to Model. Controller then uses methods on model to change its contents, and View subscribes to model's events to show the changes.
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
Window doesn't need to know about the protocol. In fact, Panel doesn't need to know about the protocol either.
Use dependency injection.
Panel
Since Panel creates the controllers, Panel will need a reference to Protocol. So you'll need to inject the protocol into the panel.
class Panel {
public Panel(Protocol protocol) {
for(...) {
Item item = new Item();
Controller controller = new Controller(item, protocol);
}
}
}
I don't recommend populating the list in the constructor like this, but I kept it so the code is familiar.
Window
To prevent Window knowing about Protocol, you should inject the panel.
class Window {
public Window(Panel panel) {
// ...
}
}
Update main
With these new changes, you'd be interfacing with your types as follows:
Protocol protocol = new Protocol();
Panel panel = new Panel(protocol);
Window window = new Window(panel);
Now Window doesn't need to depend on Protocol.
Introduce a factory
I mentioned how Panel doesn't need to depend on Protocol either.
It's the Controller that uses the protocol; the panel only references the protocol so it can pass it to newly created controllers. The protocol is needed for the creation of controllers.
You could introduce a factory to handle the creation of controllers. The factory would reference the protocol:
class ControllerFactory {
private Protocol protocol;
public ControllerFactory(Protocol protocol) {
this.protocol = protocol;
}
public Controller newController(Item item) {
return new Controller(item, protocol);
}
}
Panel would now depend on the factory instead of the protocol:
class Panel {
public Panel(ControllerFactory factory) {
factory = factory;
for(...) {
Item item = new Item();
Controller controller = factory.newController(item);
}
}
}
Your new usage would be as follows:
Protocol protocol = new Protocol();
ControllerFactory factory = new ControllerFactory(protocol);
Panel panel = new Panel(factory);
Window window = new Window(panel);
Neither Window or Panel know about the protocol.
Related
I use Vaadin 14 and would know whether it is possible to report changes in the nested list to objects in the main view.
A rough example is shown in the picture. Above you can see the sum as size (here 2), if I press Delete it should change to 1.
Is that possible and how?
concept
I don't have any code yet, it's a thought where I would like to have a hint about what would be possible, e.g. Observer Pattern or something, but code could look something like this
code:
#Rout("")
public class MainView extends VerticalLayout {
private List<CustomDetails> customDetails = new ArrayList<>();
public MainView(){
final var form = new FormLayout();
customDetails.forEach(form::add);
add(H1("Header"), form)
}
}
public class CustomDetails extends Details{
private CustomForm customForm;
private final Service service;
public CustomDetails(){
customForms = new CustomForm(service.getListOfObjects());
this.setContent(customForms)
}
}
public class CustomForm extend FormLayout{
private FormLayout formLayout = new FormLayout();
private List<Object> objects = new LinkedList<>();
public CustomForm(List<Object> list){
this.objects = list;
setUp();
add(new Paragraph("SUM: "+ list.size()), layout);
}
private void setUp(){
objects.forEarch(o->{
....
layout.add(...)
})
}
}
In Vaadin there is an utility class Binder which is used to bind data to forms. If your use case is related to this, i.e. your so called nested layout is in fact a form and objects you refer to are data beans you want bind into that form. I recommend to study that first.
If you have list editor, I would also investigate if it fits your application to implement it with Grid or IronList/VirtualList, which is backed by DataProvider. Say you edit one item, and after saving the item, you can call dataProvider.refreshItem(item) to update the view.
Observer Pattern or something...
Yes, that is a solution. It is a lot of work and has been done before.
One such library is Beanbag.
Note: I wrote this (or rather, I started writing it a day ago).
EDIT:
As of this edit, we have the ObservableCollection interface. You can use it like so:
// You have a collection called "strings".
// Wrap it in an ObservableCollection.
final ObservableCollection<String, Collection<String>, BasicObservableCollection.Default<String, Collection<String>>> observableStrings = ObservableCollections.observableCollection(strings);
// Add a removed observer.
observableStrings.addElementRemovedObserver(observation -> System.out.println("\"" + observation.getValue() + "\" was removed.");
// Remove an element.
observableStrings.remove("hello");
If you need the wrapper to have List methods, just wait until tomorrow evening EST. I'll have the code up by then and will update this post accordingly.
I decided to split the last part of that question here into a new question here: https://softwareengineering.stackexchange.com/questions/411738/extension-of-classes-where-to-put-behaviour-how-much-direct-access-is-allowe
If i have a lib and i want to use it, i wrote mostly a own class. This class has one method. In that there is the code how to instantiate the lib/framework. Sometimes there are a few more methods, with them i not only instantiate the class but use it. For example if i want to start a http-server i have there a start-method.
class Container
{
TheLib theLib;
public void init() //or a constructor
{
//some init of the theLib
}
public void start() //
{
theLib.doSomething(...)
theLib.doSomethingmore(...);
theLib.start(...);
}
//important!
public TheLib getTheLib()
{
return this.theLib; //after i started configured it and so on, i want of course use all methods,
which the lib have in some other parts in my application
}
}
But it seems not to be the best solution.
Are there any better solutions, that OO is?
Often i also use only one method, a own class for this seems to be here a big overhead?
Exposing the lib breaks encapsulation? Tell-Dont-Ask is also violated?
Everything depend on what you actually need or how you have access to your 'the lib' instance.
public class Container {
private TheLib theLib;
/* #1: Do you already created the instance before? */
public Container(TheLib theLib) {
this.theLib = theLib;
}
/* #2: Do you need to created the instance each time? */
public Container() {
this.theLib = new TheLib();
}
public void start() {
theLib.doSomething(...)
theLib.doSomethingmore(...);
theLib.start(...);
}
public TheLib getTheLib() {
return this.theLib;
}
public static void main(String[] args) {
/* #1 */
TheLib theLib = ...;
Container container = new Container(theLib);
/* #2 */
Container container = new Container();
/* Continue the flow of your program */
container.start();
container.getTheLib().doSomethingEvenMore();
}
}
Or maybe you actually need only one instance of your 'Container' class. In this case, you should look on how to make a singleton: Java Singleton and Synchronization
Anwser: Often i also use only one method, a own class for this seems to be here a big overhead?
Well, in Java, you cannot do formal programming like in C, so everything line of code that you write, or will be using, has to be in a class of some sort.
If your piece of code is small and don't really need an object, static function might do the work.
I'm playing arround with JavaFX and various scenes that are loaded by FXML. So I got the idea to write a manager that handles scene switching.
So far everything works, but I'm unsure if this is a good implementation.
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class SceneManager {
private static final String[] fxmlFiles = {"../gui/MainWindow.fxml", "../gui/NewGameWindow.fxml"};
private static SceneManager instance = null;
private static Stage rootStage = null;
private FXMLLoader[] loadedFxml;
private Pane[] loadedPanes;
private Scene[] scenes;
public enum States {
MAIN_MENU, NEW_GAME;
}
private SceneManager() {
try {
this.loadedFxml = new FXMLLoader[States.values().length];
this.loadedPanes = new Pane[States.values().length];
this.scenes = new Scene[States.values().length];
for(int i = 0; i < fxmlFiles.length; i++) {
loadedFxml[i] = new FXMLLoader(getClass().getResource(fxmlFiles[i]));
loadedPanes[i] = loadedFxml[i].load();
scenes[i] = new Scene(loadedPanes[i]);
}
rootStage.setScene(scenes[0]);
rootStage.setResizable(false);
rootStage.show();
} catch(IOException e) {
e.printStackTrace();
}
}
public static SceneManager getInstance() {
if(instance == null) {
instance = new SceneManager();
}
return instance;
}
public static void setUp(Stage stage) {
SceneManager.rootStage = stage;
}
public void switchScene(States state) {
rootStage.setScene(scenes[state.ordinal()]);
}
}
So what I plan to do is, load the FXML via the loader, assign it to a pane, create all scenes.
Then I set a scene as its starting scene and do the rest via the getInstance().switchScene() method in the controller.
It works well but I'm unsure if this is a good approach.
The implementation is imho pretty bad for several reasons:
The singleton pattern is not properly implemented
The singleton pattern is used to access an instance containing the relevant data/functionality via a static method, but this instance should contain all the relevant data as instance fields.
rootStage and setUp are static though.
You store data in fields that is never needed again
You never read from loadedFxml or loadedPanes outside of the loop. You could instead create local variables in the loop body.
Everything is loaded at once
For several small scenes this may not make much difference, but as you add more and more scenes this will increase startup time. Consider lazily loading the scenes.
Data for scenes is kept in different data structures
Not much of an issue, but it makes the class a bit harder to maintain. The enum stores one part of the data used to create/access the scenes fxmlFiles contains the other half. Every time you add/remove a scene you need to update both parts of your code. In cases like this it would be preferable to store the url data in the enum itself:
public enum States {
MAIN_MENU("../gui/MainWindow.fxml"), NEW_GAME("../gui/NewGameWindow.fxml");
private final url;
States(String url) {
this.url = url;
}
}
for(States state : States.values()) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(state.url));
...
}
Note that you use .. in your urls here which ceases to work, if you package your program as a jar.
But using a enum in the first place is a questionable decision. This way you won't be able to add/remove scenes without recompiling.
Using static doesn't seem necessary at all
It's good to avoid the use of static at all if possible. (see Why are static variables considered evil?).
If my assumption that you only use the SceneManager class from scenes loaded by it (and for displaying the initial scene) is correct, it's not hard to pass the SceneManager instance to the controllers of the scenes to avoid the need of using SceneManager.getInstance in those classes (see Passing Parameters JavaFX FXML):
superclass for controllers
public class BaseController {
protected SceneManager sceneManager;
void setSceneManager(SceneManager sceneManager) { // if SceneManager and BaseController are in different packages, change visibility
this.sceneManager = sceneManager;
}
}
FXMLLoader loader = ...
Pane pane = loader.load();
BaseController controller = loader.getController();
controller.setSceneManager(this);
Using the url as identifiers for the scenes for simplicity, you could improve the implementation:
public class SceneManager {
private final Stage rootStage;
public SceneManager(Stage rootStage) {
if (rootStage == null) {
throw new IllegalArgumentException();
}
this.rootStage = rootStage;
}
private final Map<String, Scene> scenes = new HashMap<>();
public void switchScene(String url) {
Scene scene = scenes.computeIfAbsent(url, u -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource(u));
try {
Pane p = loader.load();
BaseController controller = loader.getController();
controller.setSceneManager(this);
return new Scene(p);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
rootStage.setScene(scene);
}
}
This allows you to
create different managers for different stages
load scenes when they are needed first
dynamically add more scenes
prevents state where switchScene is called but the stage is null
simplifies access to the SceneManager in controller classes to sceneManager.switchScene
SceneManager is possibly available for garbage collection before the program completes since there is no static reference to it.
I got a class Config wich looks like that:
public Class Config {
public static int someIntValue = 0;
public static String someText = "some text";
}
What i want to do now is saving and loading that config and there is also that inital config if there is no need to load another. So the config can change at any point in the programm.
What i came up with was a Singelton like pattern
public Class Config {
public static Config instance;
private int someIntValue = 0;
private int String someText = "some text";
public static Config getInstance(){
if(instance == null)
instance = new Config();
return instance;
}
public void setInstance(Config config){
this.instance = config;
}
//getter/setter
...
}
But in the end it doesnt look like the best approach and im not quite happy with it :/
Maybe you guys can help me out with a usual / "best practice" way to do that.
Best Regards
Made
I would just use java.util.Properties, or some wrapper around it. Another good approach is java bean and something like xstream to save/load stuff.
Usually in Java for configuration use properties files. And then use ResuorseBundle for reading properties.
Your "singleton" is not a Singleton in the conventional sense.
1) Field instance must be private
2) Remove SetInstance method
3) And you should make your singleton thread safe.
If you'd consider avoiding writing the boilerplate code around java.util.Properties, you can have a look at something that does it for you: OWNER API.
It's configurable to tailor your needs and it offers some additional neat features if compared to java.util.Properties (read the docs).
Example. You define an interface with your configuration parameters:
public interface ServerConfig extends Config {
int port();
String hostname();
#DefaultValue("42")
int maxThreads();
#DefaultValue("1.0")
String version();
}
Then you use it like this:
public class MyApp {
private static ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
private MainWindow window;
public MyApp() {
// you can pass the cfg object as dependency, example:
window = new MainWindow(cfg);
}
public void start() {
window.show();
}
public static void main(String[] args) {
// you can use it directly, example:
System.out.println("MyApp version " + cfg.version() + " copyright (c) ACME corp.");
MyApp app = new MyApp();
app.start();
}
}
You can define the cfg object as member instance on the classes where you need, or you can pass the instance to constructors and methods where you need it.
Version 1.0.4 will be released soon and it will include also "hot reload" and many improvements.
I have a dude about how to implement Actions in Swing.
My idea is create a Class for each action of my application extending AbstractAction so I can use in many components that must have the same behavior. So I finaly have something as:
public class ActionExample extends AbstractAction {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
Well, now when I want to use it I have three options in my mind:
public void makeUI1() {
JButton btn = new JButton(new ActionExample("Do it"));
JMenuItem mi = new JMenuItem(new ActionExample("Do it"));
}
public void makeUI2() {
Action a = new ActionExample("Do it");
JButton btn = new JButton(a);
JMenuItem mi = new JMenuItem(a);
}
Or use it as a singleton (also changing ActionExample):
public void makeUI2() {
JButton btn = new JButton(ActionExample.getInstance());
JMenuItem mi = new JMenuItem(ActionExample.getInstance());
}
public class ActionExample extends AbstractAction {
private static final ActionExample INSTANCE = new ActionExample("Do it");
public static Action getInstance() {
return INSTANCE;
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
My first opinion was make it through singleton instance but I see in oracle tutorial that it make a new instance before setting it into components and in the I also see that many code create new instance for each component so I don't know what it's better and why.
Is preferred one method to be used over the other?
The multi instance action allows you to save data in the moment of the action for further use.
Imagine you want to add undo/redo functionality. You need to save what actions have been done for every action.
Singleton does not provide any advantage in this case.
I think the best thing to do would be to use the MVC pattern. Your AbstractAction class is a controller. It's responsible for extracting the information necessary for the model (ie: business logic) to use. The model/business logic is the part you reuse, but the controller may differ greatly even if it uses the same business logic.
For example, you may have a JComponent that you need to add a KeyListener to. Suddenly, your pre-made AbstractAction has become worthless because it can't be used in this situation. But, as long as you reuse all the business logic code in your KeyListener that you used in your AbstractAction, you're doing things right.