vincent15000's avatar

Single method for different instantiations

Hello,

I need to use this code.

$calculationService->loadVariables();

But the loadVariables method doesn't contain the same code according to the Livewire component in which it is used.

I could simply write the method in both components, but I also need to use it in other contexts.

  • So I thought about an interface, but I'm not sure it's a good idea.

  • I also thought about a service provider which binds the CalculationService class to the right class (for example TableService or PackageService) according to the class passed into the CalculationService, which could be a simple factory

new CalculationService($table)

new CalculationService($package)

What would be the best approach ?

Thanks for your help.

V

0 likes
5 replies
LaryAI's avatar
Level 58

Great question! This is a classic case where polymorphism, interfaces, and the strategy/factory patterns can really help. Here are a couple of clean solutions to keep your code maintainable, reusable, and testable.

1. Use an Interface + Specific Implementations

First, create an interface which each context-specific class must implement:

interface VariableLoader
{
    public function loadVariables();
}

Then, implement this interface in each class (e.g., for Table and Package):

class TableVariableLoader implements VariableLoader
{
    public function loadVariables()
    {
        // Table-specific logic
    }
}

class PackageVariableLoader implements VariableLoader
{
    public function loadVariables()
    {
        // Package-specific logic
    }
}

Now, your CalculationService would depend on the VariableLoader interface:

class CalculationService
{
    protected VariableLoader $loader;

    public function __construct(VariableLoader $loader)
    {
        $this->loader = $loader;
    }

    public function loadVariables()
    {
        return $this->loader->loadVariables();
    }
}

And to use it:

$service = new CalculationService(new TableVariableLoader());
// or
$service = new CalculationService(new PackageVariableLoader());

2. Use a Factory to Decide Which Implementation to Use

If you often need to resolve the right loader class based on the model passed in, a simple factory might help:

class LoaderFactory
{
    public static function make($context)
    {
        if ($context instanceof Table) {
            return new TableVariableLoader();
        }

        if ($context instanceof Package) {
            return new PackageVariableLoader();
        }

        throw new \Exception('No VariableLoader found for this context');
    }
}

Usage:

$loader = LoaderFactory::make($table);
$service = new CalculationService($loader);

3. (Alternative) Use Method Injection

If you don’t want to instantiate the service every time, you could allow passing the "context" to your CalculationService::loadVariables($context) method, then handle branching inside the service. But generally, composition (injecting dependencies) as above is the cleaner way.


Summary:
The interface/strategy approach (option 1) gives you the most flexibility, testability, and maintainability. Combine it with a Factory if you need to resolve the right implementation automatically. Avoid duplicating code in every Livewire component—prefer composition over inheritance or copy-pasting methods.

Let me know if you’d like to see the implementation in more detail for any of these options!

1 like
vincent15000's avatar
vincent15000
OP
Best Answer
Level 63

Finally I don't need any interface or provider, I just need inheritance.

In each Livewire component, I directly instantiate the right Service (TableService or PackageService) and each of these classes inherits the CommonService class).

vincent15000's avatar

Yes interesting too ... but this time the inheritage was a better choice in my case ;).

I use a trait for example for my own image functions when I need to handle images for some models.

Please or to participate in this conversation.