If Singletons Are Bad Then Why Is a Service Container Good

If Singletons are bad then why is a Service Container good?

Service Locator is just the lesser of two evils so to say. The "lesser" boiling down to these four differences (at least I can't think of any others right now):

Single Responsibility Principle

Service Container does not violate Single Responsibility Principle like Singleton does. Singletons mix object creation and business logic, while the Service Container is strictly responsible for managing the object lifecycles of your application. In that regard Service Container is better.

Coupling

Singletons are usually hardcoded into your application due to the static method calls, which leads to tight coupled and hard to mock dependencies in your code. The SL on the other hand is just one class and it can be injected. So while all your classed will depend on it, at least it is a loosely coupled dependency. So unless you implemented the ServiceLocator as a Singleton itself, that's somewhat better and also easier to test.

However, all classes using the ServiceLocator will now depend on the ServiceLocator, which is a form of coupling, too. This can be mitigated by using an interface for the ServiceLocator so you are not bound to a concrete ServiceLocator implementation but your classes will depend on the existence of some sort of Locator whereas not using a ServiceLocator at all increases reuse dramatically.

Hidden Dependencies

The problem of hiding dependencies very much exists forth though. When you just inject the locator to your consuming classes, you wont know any dependencies. But in contrast to the Singleton, the SL will usually instantiate all the dependencies needed behind the scenes. So when you fetch a Service, you dont end up like Misko Hevery in the CreditCard example, e.g. you dont have to instantiate all the depedencies of the dependencies by hand.

Fetching the dependencies from inside the instance is also violating Law of Demeter, which states that you should not dig into collaborators. An instance should only talk to its immediate collaborators. This is a problem with both Singleton and ServiceLocator.

Global State

The problem of Global State is also somewhat mitigated because when you instantiate a new Service Locator between tests all the previously created instances are deleted as well (unless you made the mistake and saved them in static attributes in the SL). That doesnt hold true for any global state in classes managed by the SL, of course.

Also see Fowler on Service Locator vs Dependency Injection for a much more in-depth discussion.


A note on your update and the linked article by Sebastian Bergmann on testing code that uses Singletons : Sebastian does, in no way, suggest that the proposed workaround makes using Singleons less of a problem. It is just one way to make code that otherwise would be impossible to test more testable. But it's still problematic code. In fact, he explicitly notes: "Just Because You Can, Does Not Mean You Should".

What are drawbacks or disadvantages of singleton pattern?

Paraphrased from Brian Button:

  1. 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.

  2. They violate the single responsibility principle: by virtue of the fact that they control their own creation and lifecycle.

  3. They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.

  4. 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.

Are Singletons really that bad?

The key thing to remember is that design patterns are just a tool to help you understand the abstract concepts. Once you have that understanding, restricting yourself specifically to a "recipe" from a book is pointless and hurts your ability to write the code most appropriate for your purpose.

That said, reading books like GoF will present you with more ways to think about problems so that when the time comes to implement something on your own, you'll have a wider set of perspectives to approach the problem from.

In your case, if using singleton makes sense in every case, then go right ahead. If it "sort of" fits and you have to implement it in some clunky way, then you need to come up with a new solution. Forcing a pattern that isn't perfect is somewhat like hammering a square peg in a round hole.

Given that you say "this approach has been effective and proven very practical to our circumstances," I think you're doing fine.

Here are some good books:

Gang of Four Book - the classic book for design patterns

Head First Design Patterns - I've heard this recommended by a few people as an alternative

Why use service container instead of new class

The purpose of Dependency Injection, which is achieved via Laravels IoC container (service container), is to separate the creation and use of objects. So I would argue that having app(Employee::class); is no better than new Employee or Employee::create(). IoC is more than just registering objects with the container and pulling them out when you need them.

A practical example for this might be where you have a class which transmits SMS messages.

class Sms
{
private $smsProvider;

public function send()
{
$this->smsProvider->send();
}
}

So your SmsSender class requires a smsProvider to function. Rather than creating a new instance of the SMS provider in Sms class, you would inject the provider into the class (usually the constructor in such examples) and make use of it that way. If you wanted to make it even more flexible, you would define an interface for SMS providers and then allow the service container to inject the correct concrete class from your IoC mappings.

class Sms
{
private $smsProvider;

public __construct(ISmsProvider $smsProvider)
{
$this->smsProvider = $smsProvider;
}

public function send()
{
$this->smsProvider->send();
}
}

class SmsTwillio implements ISmsProvider
{
}

class SmsNexmo implements ISmsProvider
{
}

You would then define mappings in the service container to bind references to ISmsProvider to a concrete implementation such as SmsTwillio or SmsNexmo.

Using your example, you might define a PayrollService:

class PayrollService
{
public function process($employees, $start, $end = null)
{
foreach ($employees as $employee) {
$salary = $this->calculateNetSalary($employee);
}
}

private function calculateNetSalary(Employee $employee)
{
}
}

class ProcessPayrollController extends ProcessPayrollController
{
private $payrollService;

public __constructor(PayrollService $PayrollService)
{
$this->payrollService = $payrollService;
}

public function __invoke()
{
$result = $this->payrollService->process(Employee::all(), '2021-06-01');
}
}

I don't think your Payroll class needs Leave or Timesheet. I would define functions or scopes on your Employee class which returns that data:

$employee->daysUnpaidLeave('2021-06-01');

$employee->hoursLate('2021-06-01');

Where the dates are the start date to calculate from until today, or provide an optional end date as a second argument.

You could go even deeper down the rabbit hole and look at other creational design patterns for creating objects at runtime, however, design patterns can be quickly abused and add complexity rather than reduce it, so keep it simple.

The Registry in OpenCart is a similar principal. In that example $registry->get('language'); will always return the same instance of Language, so yes it would be a singleton.

Your Payroll could be a singleton as you don't need multiple instances of it as it is simply processing data you provide to it.

Should a Service class be singleton in java?

Singletons are bad, if you develop them. If you are using dependency injection, let the DI container handle the singleton nature of your Service object. If you are not using dependency injection, use a static method instead of a singleton.

Classic example of bad:

public class HootUtility // singleton because developer was a goofball.
{
...
public void blammy(...) { ... }
public HootUtility getInstance() { ... }
}

... somewhere in the code.

HootUtility.getInstance().blammy(...); // This is silly.

Better implementation of the above:

public class HootUtility // Not a singleton because I am not a ______. (fill in the blank as you see fit)
{
// You can limit instantiation but never prevent instantiation.
// google "java reflection" for details.
private HootUtility()
{
throw new UnsuppotedOperationException();
}

public static void blammy(...) { ... }
}

... somewhere in the code.

HootUtility.blammy(...);

If you have a service interface that has an concrete implementation, use a dependency injection framework to inject the implementation (DI frameworks include: spring and guice).

Edit: If I was using spring, I would choose singleton scope (the default).

Is DI the only solution to Singleton and/or static objects?

Logging is usually the example where static singletons are OK. You don't need to mock your logging anyway, do you?

Why are singletons worse than having many references to the same object?

There isn't a lot of differences between a singleton and a global variable. The biggest difference is that a singleton of class T allows only one globally available instance of class T available everywhere, whereas having a global instance of T doesn't restrict the program from creating more instances of T.

The issue that the snippet is talking about is an issue of decoupling the actual singleton from any code that uses the singleton. If you're writing class Foo, ideally you would want to design the class where all the necessary information about how Foo can be used is available in the interface. Now if Foo uses Singleton Bar, this is part of Foo's implementation. If a user sees no need to have Bar and removes the class definition from the program, they would suddenly see that Foo doesn't compile, and it's not inherent why just by looking at Foo's interface. They would have to actually look through the implementation and see the code that grabs the singleton instance and uses it.

You would run into the exact same issue with global variables as well if class Foo were to use a globally available instance of Bar, but there's a small difference about it. If Foo contains a reference to the global variable inside itself, then the fact that it uses an instance of Bar is inherent in the interface, and the user of Foo would at least understand that it is dependent upon Bar, whereas the global singleton is always available and uncopyable, so it is likely that the writter of Foo would store the reference to the singleton, and the relationship would not be inherent from the interface.

Now, there are a lot of other issues about using global variables in general, for example Unit Testing Foo. If for some reason Bar were to fail a unit test for one of its common functionalities, then most likely Foo would also fail its unit tests for functions that use the singleton/global Bar, even if the Foo function itself is written perfectly.

Singletons are marginally worse than global variables as far as making clear interfaces goes due to not implying the relationship to Bar, but the article you linked is likely arguing that you should avoid both Singletons and global variables.



Related Topics



Leave a reply



Submit