The problem:
For simplicity i'll use a simple example. In a sample application, a Class-A reads some data.
Class-A then instantiates Class-B, which then instantiates Class-C. So in all there is a 3-level-hierarchy of classes A --> B --> C
Class B creates & manages custom objects based on the data found in class A. I have simply passed that data from class-A to class-B in the form of function/method parameters.
Class C, however, is different. It has 2 functions/methods that give it its data.
firstMethod(arg)
- which gives it data to work with. (called on class-B)otherMethod(otherArg)
- with gives it other data to work with.
It was instanced by Class-B and the data that it needs from Class-B will be passed to it passed to by class-B through firstMethod(arg1)
. However, there is another data set that class-C needs to run properly. This other parameter has nothing to do with class-B; Therefore it would seem wrong to pass this information through this classes as a parameter to get to class-C.
How can I get this information from class-A to class-C without using class-B? What I did was create a Singleton class-S.
Class-S has defined within it the methods/functions (not variables) of class-A. After setting it up, Class-C can now call its otherMethod(otherArg)
on Class-S which calls the needed function and returns the needed data directly from class A.
A-->B-->C
^ |
|___S___|
The Questions:
I've listed some of the problems of singletons ive seen around the net.
- From this link, They are generally used as a global instance, why is that so bad? Because you hide the dependencies of your application in your code, instead of exposing them through the interfaces. Making something global to avoid passing it around is a code smell. [my edit] - While class-S is being used as a global instance,
Class-c
has its dependencies clearly defined. The methods/functions that's needed for it to work is defined in its interface. - from this link, They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases. [my edit] is this setup still tightly coupling class-C with the singleton? If Class-C is to be unit tested, the singleton is not needed (at least i don't think so). Since the functions are clearly defined a mock class can fake its data.
- They carry state around for the lifetime of the application. Another hit to testing since you can end up with a situation where tests need to be ordered which is a big no no for unit tests. Why? Because each unit test should be independent from the other. [my edit] Given the setup I'm not sure this will be a problem?
Any pointers on this will help much. Please no hate.. I googled but didn't find many definitive answers. Thanks. Any thoughts?
[My Edit] --> A comment below asked the following question that i thought important enough to add here.
Why don't you want to pass the additional data to B? If this data is needed to execute the command [
in class-C
], then it should be a parameter. Regardless if B uses it directly or passes it on
The Answer would be that this is a simple example using A, B, & C. The truth is the app is much more complex. Class-B is a reusable class that is used in many other parts of the app. If I made class-B accept the additional data as a parameter to pass to class-C, then in every other part of the app where class-B is used, I would have to pass nil
for that parameter. Also, if class B is used in more places in the application: B->C
...B->X
...B->Y
...B->Z
... etc, then the reusable class-B would now have to carry around the parameters of C, X, Y and Z.. or more. Class-B is a class that can instance classes of different types, but it does not know the information about the Classes it is has instanced/allocated. Its job is simply to instance/allocate a class, give the instanced-class the general information it needs via parameters and finally do some additional setup. But class-B should not know of the other specific data of its instanced classes. So it would not know that class-C needs to go to class-A to get its information. Only class-C would know where to get its specific information.