How to construct two objects with each other as parameter / member

I have two classes, each needing an instance of each other. Usually, if an object needs another object to run, I like to pass it in the constructor. But I cannot do it in this case, because one object has to be created for another, and therefore the second object does not exist to pass to the first constructor of the object.

I can resolve this by passing the first object to the second object's constructor and then calling the setter on the first object to pass the second object to it, but that seems a little awkward and I'm wondering if there is a better way out there:

backend = new Backend();
panel = new Panel(backend);
backend.setPanel();

      

I have never studied MVC; I believe I am dealing with a Model here (Backend) as well as a View or Controller (Panel). Any ideas I can get from MVC?

+1


source to share


6 answers


It's time to take a look at MVC. :-) When you have a model view situation, the consensus is that the model shouldn't know about the view controller (MVC often plays the role of M-VC), but the opinion is invariably known about the model.



If the model needs to say something, it does so by notifying its listeners, of which it can have multiples. Your look should be one of them.

+8


source


In a scenario with a circular construct, I would use the factory class / factory method. I usually make the build logic private to the factory (using friend construct, package level security or similar) to make sure no one can instantiate without using a factory.

Using setter / constructor is really part of the contract between the two classes and the factory, so I would only use what is convenient.



As already pointed out, you should really try to find a non-circular solution.

+2


source


First of all, contrary to what others have said here, there is no problem with circular links. For example, the Order object is expected to have a reference to the Customer object of the person who placed the order. Likewise, it would be natural for a Customer object to have a list of the Orders that it has placed.

In a reference-based language (like Java or C #) there is no problem. In a value based language (like C ++), you have to care about designing it.

However, you are designing:

backend = new Backend();
panel = new Panel(backend);
backend.setPanel(panel);

      

This is the only way to do it.

+2


source


Better to avoid circular links. I will personally try to rethink my objects.

+1


source


panel = new Panel(backend);

      

You do it in this routine something like

  Public Sub Panel(ByVal BackEnd as BackEnd)
        Me.MyBackEnd = BackEnd
        BackEnd.MyPanel = Me
  End Sub

      

You don't need BackEnd.SetPanel

Better to use a proxy. A proxy associates one object with another by creating an event. The parent passes the proxy to the child. When the child needs a parent, it calls the GetRef method on the proxy. The proxy then creates an event that the parent uses to return itself to the proxy, which then passes it on to the child.

Using the Event / Delegate mechanism avoids any circular reference problems.

So there you have it (assuming the backend here is the "parent")

  Public Sub Panel(ByVal BackEnd as BackEnd)
        Me.MyBackEnd = BackEnd.Proxy
        BackEnd.MyPanel = Me
  End Sub

  Public Property MyBackEnd() as BackEnd
     Set (ByVal Value as BackEnd)
        priBackEndProxy = BackEnd.Proxy
     End Set
     Get
        Return priBackEndProxy.GetRef
     End Get
  End Property

      

Here's a more detailed discussion of the circular reference problem. Although it is focused on fixing it in Visual Basic 6.0.

Dynamic memory allocation

Also another solution combines Panel and BackEnd into another object. This is a common occurrence if both elements are user interface controls and need to behave consistently.

Finally, as MVC goes on, I recommend using the Model View Presenter approach.

Basically you have a form. Implementation of the IPanelForm interface. It registers itself with the Panel class, which executes all of the UI logic. The BackEnd must have events that the Panel can connect to when the model changes. The panel handles the event and updates the form through the IPanelForm interface.

  • User clicks a button

  • The form navigates to the panel for the user to click the button

  • The panel processes the button and retrieves data from the backend

  • The panel formats the data.

  • The panel uses the IPanelForm interface to display data in the form.

0


source


I put off implementing the lessons learned, giving me plenty of time to think about the right way. As other people have said, having a clear separation of where backend objects have listeners when their properties change is definitely the way to go. Not only will it solve the specific problem I was asking about in this question, it will make a lot of other nasty design smells in this code look better. In fact, there are many different Backend classes (according to the generic class names I used in my example), each with their respective Panel class. And there are even a few places where you can move things around to separate other class pairs from Backend / Panel pairs, following the same pattern, and reducing the number of misses as parameters.

The rest of this answer will be language specific as I am using Java.

I didn't really care about "JavaBeans", but I found that the following basic JavaBean conventions have been very helpful to me in the past: mostly using standard getters and setters for properties. It turns out there is a JavaBean convention that I didn't know about that would really help here: bound properties. Bound properties are properties available through standard getters and setters that fire PropertyChangeEvents when they change. [I don't know for sure, but the JavaBeans standard may dictate that all properties must be "bound properties". This does not apply to me at the moment. Keep in mind also that "standard" getters and setters can be very non-standard by using the BeanInfo classes to define the exact JavaBean interface, but I never use that.] (The main other JavaBean convention I choose to follow, or not so appropriate in every situation, is the no-argument constructor, I already follow it in this project because each of these Backend objects must be serializable.)

I found this blog post , which helped me a lot in linking me to the problem of bound properties / PropertyChangeEvents and helped me build a plan how I am going to rework this code.

Currently all of my backend objects inherit from a generic class called Model, which provides a couple of things that every backend on this system needs, including serialization support. I'm going to create an additional JavaBean class as a superclass of the model, which will provide me with the necessary PropertyChangeEvent support inherited by each model. I will update the setters in each model to fire the PropertyChangeEvent when called. I can also have a JavaBean inherited by multiple classes that are not technically models in the same sense as these, but which can also be useful if other classes are registered as listeners for them. A JavaBean class may not fully implement the JavaBean specification; as I said, there are a few details that I don't care about.But that's good enough for this project. It looks like I can get all of this by inheriting from java.awt.Component, but these are not components in some way that I can justify, so I don't want to. (I also don't know what that might entail.)

Once each JavaBean model is bundled with PropertyChangeEvent support, I'll do a lot of cleanup of the code: Models that currently hold panel references will be updated, and panels will register as listeners. So much cleaner! The model doesn't need to know (and shouldn't have known in the first place) which methods the Panel should call on its own when updating a property.

0


source







All Articles