Often I find myself in a situation where I have a class in which I only have one public method and want to call it. Let's imagine we have a class to send mails to a specific user
class SendMail
{
public function handle(User $user)
{
//
}
}
Now we can call the method as follows
(new SendMail)->handle($this->user);
but I don't like the noise for initiating a new object. Is there perhaps a better solution? π€
What if we include a tiny bit PHP-Magic β¨ and add a static function that creates the object for us and calls the method? Like this
class SendMail
{
public function handle(User $user)
{
//
}
public static function execute(User $user)
{
(new static)->handle($user);
}
}
With the new static method we can call it like this now
SendMail::execute($this->user);
Hooray π this looks much cleaner in my eyes. But why are we going to all this trouble now? π
(new SendMail)->handle($this->user);
// vs
SendMail::execute($this->user);
Both lines do the same thing and readers with a trained eye would now ask why not just execute the code in the static method instead of creating a new object. Depending on the functionality it is not necessary to do it. However in my experience most functions are not as simple like in our example.
Have I sparked your interest? Just give it a try. Maybe you also like it π
Edit:
Thank you all for the feedback π dev.to is really great π
I have taken a very simplified example in this article and added further explanation when to use this pattern in the following comment https://dev.to/suddenlyrust/comment/e58i
Top comments (20)
Is there a reason you wouldn't just use the magic method
__invoke()
?Example:
And to invoke it you simply do
Thank you for your comment. I tried your example. But somehow the invoke method is never called π€. Could you help me with that?
Here is a link to a "Online PHP Compiler": repl.it/repls/DisastrousViciousFeed
Edit: Sorry, the right way is
I tried your example but it returns an
Fatal Error
. Do you have a working snippet for me?Here is a link to a "Online PHP Compiler": repl.it/repls/ImpressionableCuddly...
Try the following as a replacement of line 11.
Still getting following fatal error
PHP Fatal error: Uncaught Error: Call to undefined function Something() in /home/runner/main.php:12
What am I missing? π
repl.it/repls/EssentialMarvelousWe...
You are missing the class instantiation (
new
), but I think that is my fault, I copied the wrong thing.It's working now π₯³ we found the missing key to the puzzle. The invoke variant looked promising but at the end it still requires 2 lines of code.
But thank you Jamieson for showing me this variant. Did not know how to call the magic invoke method #TIL. This can be handy for sure π€
I think this approach is bad (for like 99% of the cases, like this one). How are you going to parametrize your send process? How are you going to switch from SMTP to something else?
If you would have properly structured the code, you would you DI (maybe an interface). When using static methods, you will never be able to easily switch the implementation. You are locked into your current implementation.
Thank you for your feedback Christian. Yeah for the use case of sending mails this makes no sense. It is a very simplified example. I use this method in the following case. Imagine you have a pretty big class. Like 500 or more lines and it is pretty hard to read it. You have many functions that perform different tasks. Now you can use this pattern to split the class in different little classes. Let's call the little classes "actions". Each action you can call now on your own and refactor it out of the big class.
We can now refactor it like this.
In my eyes this helps to clean up my big classes. You get following benefits.
I hope this explanation gives you a better understanding.
While I understand your case (and I, for sure, have also written such code) I have two main concerns about your example:
You say βa huge class that performs many tasksβ. Iβll just refer to βSingle Responsibility Principleβ here: 1 Class -> 1 Purpose/Task
How would you ever write a Unit test for your RefactoredBigClass? There is no way you could ever test your handle() function there.
Thank you for your concern, Christian.
At your first point: I don't know what you mean by that π€
And to your second point: I'm not unit testing this class π
For the first point, see: scotch.io/bar-talk/s-o-l-i-d-the-f...
For the second point: Well, that's your fault ;)
This is a wrong way in my opinion. You need split data structures too, coupled with methods.
this is exactly why people say "OOP is stupid and not used correctly". The object in this case has no business with any of its functions, its just a namespace-delivery-system.
A simple function send_email() would be enough, this adds about 5x the bloat and you dont even create objects, just call a single static function.
Thank you for your feedback Maximillian. You are absolutely right. For the use case of sending a mail this pattern does not have any benefit.
Further explanation of this approach if you are interested πdev.to/suddenlyrust/comment/e58i
Better way:
Why? You may inject object
$sendmail
into another method. You may simple replace this object with$nullObject
or$stubObject
when you want run tests.I generally support this, as it can also allow extra management logic like sharing object instances (like what's usual with object factories so common in Java).
As long as the number of arguments is kept at an acceptable minimum, that is.
I find this approach difficult to maintain ever once somebody introduces a parametrized constructor: you would have to change the calls in the static methods on every change of the signature.
I was accepting this idea in the past, but for now I think it makes me confusing between concept of static function and 'normal' function. Finally, keep it simple and open the eyes is better solution, IMO.