Remove unnecessary parameter passing

My other todo note is about that the Application class has a reference to AddSongViewFactory only to pass it on as a parameter to SongsShower. And what's wrong with that? Well, one thing is that it can be quite cumbersome. Currently I have three parameters passed into the Application constructor, making both the caller side and the constructor definition quite long and hard to read. And I'm only starting the development, so I assume the problem will grow worse.

But that's a small problem compared to the real one. The problem with a lot of legacy systems is that they are too tightly coupled: too many things know about too many other things. When you make a change in one place it ripples through the system, affecting things you thought were unrelated. So when it comes to creating a maintainable design, it is desirable to make classes as decoupled as possible. In this case, the Application class has no benefit in having a reference to the AddSongViewFactory class, and therefore I want to try to remove it.

I find it hard to get the whole picture though, now that I have so many classes, and it's difficult to understand what the problem really is. So it's time to draw a few boxes to get a better understanding of the design.

There are a lot of arrows going into the AddSongViewFactory, that is, there are many classes that know about it: ApplicationFake, Application, SongsShower and AddSongShower. By introducing the AddSongShower I seem to have aggravated the situation since there are now two classes, Application and SongsShower, that know about AddSongViewFactory only to pass it on to some other class.

One solution to this problem would be to introduce some kind of global variable that keeps track of the AddSongViewFactory. I could set the variable in the test and in the application start-up respectively, and then read the value in the SongsShower where I need it. There are plenty of software patterns that have to do with keeping track of such a global state, including the Registry Pattern, which resembles the Singleton Pattern.

While this might be a convenient short-term solution, there are some negative sides to it. My biggest objection is that it makes any class that uses the global variable (in this case, the SongsShower) a black box. On the outside, looking at the public interface of the class, there is no way you can see that it is using a global variable. If you want to use it, chances are that you will miss to set the variable, or that someone else has given it the wrong value, and then you will get very surprising results.

Also, the AddSongViewFactory is most likely not the last class that I will want to have different versions of in test and production, and if I use global variables for all such classes I will end up with quite a large and hard to maintain global state.

The good thing is that it is quite easy to avoid these disadvantages. The solution I will use is something that I have used before, but then I used it to solve another problem. The solution is to introduce a factory. Last time I used it to control what version of the view fake is created. Here, I will use it to remove unnecessary dependencies between classes by hiding them inside the factory. Basically, I'm going to create a class, AddSongShowerFactory, that can create AddSongShowers, and that contains all information necessary to do so.

These two types of factories are quite different from each other. The first kind, the one that creates the view, is abstract and also returns an abstract type: the code that uses the factory references an interface (IAddSongViewFactory) and what is created by the factory is also referenced to as an interface (IAddSongView). The kind of factory I will create now is concrete, and it also returns a concrete type. There are no interfaces here.

The normal thing would be to let the factory have a method called Create() that I call to create the shower before showing it, like this:

AddSongShowerFactory.Create().Show();

I will however probably always use the Create() and Show() methods together, so therefore I let the factory only have a Show() method that does the creation internally, to make the client code simpler and to reduce duplication.

AddSongShowerFactory.Show();

Here is what the factory looks like.

public class AddSongShowerFactory
{
private IAddSongViewFactory AddSongViewFactory { get; set; }
 
public AddSongShowerFactory(IAddSongViewFactory addSongViewFactory)
{
AddSongViewFactory = addSongViewFactory;
}
 
public void Show()
{
new AddSongShower(AddSongViewFactory).Show();
}
}

Next, I replace the references to IAddSongViewFactory in Application and SongsShower with a reference to the AddSongShowerFactory and create the AddSongShowerFactory in the ApplicationFake.

public class ApplicationFake
{
// ...
 
public void Start(Action<SongsViewFake> showAction)
{
var asserter = new ViewShownAsserter<SongsViewFake>(showAction);
var addSongViewFactory = new AddSongViewFactoryFake();
var addSongShowerFactory = new AddSongShowerFactory(addSongViewFactory);
var view = new SongsViewFake(asserter.ShowAction, addSongViewFactory);
var application = new Application(view, CloseAction, addSongShowerFactory);
application.Start();
asserter.Verify();
}
}

I do the corresponding change in Program as well. And here is what SongsShower looks like, using the new factory:

public class SongsShower
{
private IAddSongShowerFactory AddSongShowerFactory { get; set; }
// ...
 
private void OnAddButtonClick()
{
AddSongShowerFactory().Show();
}
}

Quite elegant, if I may say so. There are no strange details here, nothing that do not belong in the class, but just something that I can delegate the showing of the Add Song Window to. Let's have a look at how this changed the design.

There are three arrows going into the IAddSongViewFactory, one less than before, which is good. But still I wonder if it is not one too many. The AddSongShowerFactory arrow is there only to pass it as a parameter to the AddSongShower constructor, that is, a bad kind of reference. I can't remove it though.

So I'm wondering if I can do something about AddSongShower instead. What if I let AddSongShowerFactory create the view and then pass it into the AddSongShower? That would reduce the number of classes knowing about IAddSongViewFactory, but increase the number of classes knowing about IAddSongView, since AddSongShowerFactory would now create the view only to pass it on. The ones that know about the view would however do it for a good reason, so I'm going to try that solution.

public class AddSongShowerFactory
{
// ...
 
public void Show()
{
var view = AddSongViewFactory.Create();
new AddSongShower(view).Show();
}
}
 
public class AddSongShower
{
private IAddSongView View { get; set; }
 
public AddSongShower(IAddSongView view)
{
View = view;
}
 
public void Show()
{
View.OnCancelButtonClick += View.CloseView;
View.ShowView();
}
}

I think I like it better, but I'm not convinced. I leave it like this for now, though, sure of that I can easily change it later.

Looking that the design, it seems Application now knows about the AddSongShowerFactory, only to pass it as a parameter to SongsShower. I use the same solution here: introduce a SongShowerFactory.

This is what it currently looks like where the view is shown.

public class Application : ApplicationContext
{
// ...
 
public void Start()
{
View.OnViewClosed += () => CloseAction( this );
new SongsShower(View, AddSongShowerFactory).Show();
}
}

To replace this code with a factory, I need the factory to have references to the view and the AddSongShowerFactory. So I create and use the factory.

public class Application : ApplicationContext
{
// ...
 
public void Start()
{
View.OnViewClosed += () => CloseAction( this );
new SongsShowerFactory(View, AddSongShowerFactory).Show();
}
}
 
public class SongsShowerFactory
{
private ISongsView View { get; set; }
private AddSongShowerFactory AddSongShowerFactory { get; set; }
 
public SongsShowerFactory(ISongsView view, AddSongShowerFactory addSongShowerFactory)
{
View = view;
AddSongShowerFactory = addSongShowerFactory;
}
 
public void Show()
{
new SongsShower(View, AddSongShowerFactory).Show();
}
}

The tests are green. Next I extract the creation of the SongsShowerFactory to Program and ApplicationFake and pass it in as a parameter into Application.

public class Application : ApplicationContext
{
private SongsShowerFactory SongsShowerFactory { get; set; }
// ...
 
public Application(ISongsView view, Action<ApplicationContext> closeAction,
SongsShowerFactory songsShowerFactory)
{
View = view;
CloseAction = closeAction;
SongsShowerFactory = songsShowerFactory;
}
 
public void Start()
{
View.OnViewClosed += () => CloseAction( this );
SongsShowerFactory.Show();
}
}
public class ApplicationFake
{
// ...
 
public void Start(Action<SongsViewFake> showAction)
{
var asserter = new ViewShownAsserter<SongsViewFake>(showAction);
var addSongViewFactory = new AddSongViewFactoryFake();
var addSongShowerFactory = new AddSongShowerFactory(addSongViewFactory);
var view = new SongsViewFake(asserter.ShowAction, addSongViewFactory);
var songsShowerFactory = new SongsShowerFactory(view, addSongShowerFactory);
var application = new Application.Application(view, CloseAction, songsShowerFactory);
application.Start();
asserter.Verify();
}
}

Let's have another look at the design to see what happened.

Now it looks better. One thing is that SongsShowerFactory has a reference to AddSongShowerFactory only to pass it along to SongsShower, but that's ok since it is a factory and factories typically need extra information in order to create other types.

There is a nice symmetry evolving as well. Both views have a shower and both have factories for the shower. The only big difference is that the view is still created in different ways.

There are a lot of types in action now. For the tiny application that I have so far it is clearly overkill to have that many classes and interfaces. But the plan is not to stop here, but to let the application will grow and have more functionality. In order for me to have a nice design, not only now, but also after five or fifty stories it is important to take care of the trash when it appears.

The tests are still green, and the application works fine, just like before, so I check in the code.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>