Are Behaviors a leaky design?

Take a look at the example below:

component = new Component();

component->attach(DoerBehaviour::class) //DoerBehaviour injects a do() method

component->do() //This calls the do() of the DoerBehaviour

Problem: How the user knows that the component has a do() method whilst there’s no interface involved to guarantee it?

If you mean ‘the programmer’ with ‘user’, if he is using PHPStorm (and maybe other IDE) he can add @mixin DoerBehaviour to the PHPDoc of class Component if the behavior is always available. Then it’s at least documented. You are right it’s not guaranteed/forced in some way by the PHP parser.

Thanks for your input.
I meant the program or the code that uses the do() functionality when I wrote “user”.
Now, I know that the problem with the PHP parser is not all in my head. However, I reckon that it could be a major problem from the design perspective.

I believe that there might be another design problem with behaviours.
A behavour can call a method of its owner while there’s no interface involved as a contract.
For example:

class DoerBehavior 
{
     do()
    {
        $this->owner->run(); // Nothing guarantees that the owner has a run() method. 
    }

}

Or even worse:
The owner can call a behaviours method that breaches the Hollywood principle whilst it reduces the readability insanely.

Behavours add a huge power to our codes. Nevertheless, I always ask my self that is it worth using them instead of Strategy Design Pattern? Is it a rational trade-off?

On attaching, you can test/ensure that $this->owner instanceof Component or just write some unit tests that are run when building/testing your application versions to ensure Components have the right behaviors attached. I haven’t much knowledge of Design Patterns and can not help you further on pattern decissions.

The idea behind behaviours is the ability to add certain functionality to external classes.
That being said you can control the work-flow of a system that is otherwise not entirely manageable by yourself.

In that regard you really don’t need to create a behavior for a custom class for yourself that only you (or a team member for your software) would call.

The idea is more about extending yii2 classes so you can enhance them without the requirement to rewrite many things.

Best examples are yii2 based content management systems. With behaviours you are able to extend existing cms classes, add functions and do your stuff on top of the cms.

You can always grab a behavior via

$class = $component->getBehavior(DoerBehaviour::class);
if( $class instanceof DoerBehaviour ) {
    $class->do();
} 

So you make sure you call the function in the correct class.

Of course in terms of testing and reliability behaviour are a mess - that’s why they don’t exist in yii3. However as for the ability to extend 3rd party classes they are great - but you should not use them just to include functionality in your own code that you could control otherwise.

I believe that it’s about the “program” that uses the functionality. It’s not about the “programmers”.

The solutions like the one you proposed (instanceof) is better than nothing. However, it looks more of a hack than development.

What you said about the absence of the Behaviours in Yii3 is a priceless information. Thank you! Now, I’m sure that I should stop using them. I reckon that relying on them leads to dangerous dependencies.
They’re useful only for event handling I guess.

@nima_naraghi I think you mentioned 3 important things

  • calling method “do()” which without deeper look doesn’t seem to be part of the class
  • calling potentially not existing method of owner (It has happened to me actually)
  • “Behaviors add a huge power to our codes.”

You actually summed it up nicely. It’s a trade-off.

The manual is very helpful here, mainly the comparison to traits, which has some similar drawbacks and they can be used in wrong way same as behaviors - Key Concepts: Behaviors | The Definitive Guide to Yii 2.0 | Yii PHP Framework

I had the same inner dialog to this dilemma as you. My conclusion was - yep, behaviors aren’t the best “clean solutions”, but programming isn’t about to blindly follow some otherwise useful rules. You have to know “where” you can use “what” and some times “the ends justify the means”.

So in the Yii2 project, if you are aware of the thin ice of behaviors and it’s handy, I think wouldn’t be wise to ignore such a helpful behaviors like PositionBehavior, AttributeTypecastBehavior or TimestampBehavior or even your own – I created two very simple behaviors, where the benefit is large but the space for errors small.

1 Like

I agree with every single word you wrote.
That was an outstanding conclusion.
Thanks.