Jump to content

How and when to use traits?


NotionCommotion

Recommended Posts

I am new to traits, and am trying to figure out when and how to use them.  Please comment on the following.  While this is a question about traits, I would welcome (provided advice is also given) any criticism about my use of interfaces should criticism be warranted.  Thanks

<?php
interface CarInterface
{
    public function go();
    public function stop();
    public function turn();
    public function resistRust();
}

class Car implements CarInterface
{
    public function go()
    {
        //bla bla bla
        $acceration=$this->accerationRate();
        //bla bla bla
    }
    public function stop()
    {
        //bla bla bla
        $deacceration=$this->deaccerationRate();
        //bla bla bla
    }
    public function turn()
    {
        //bla bla bla
        $turnRadius=$this->turnRadius();
        //bla bla bla
    }
    public function resistRust()
    {
        //bla bla bla
        $resistanceFactor=$this->resistanceFactor();
        //bla bla bla
    }
}

class Ford extends Car
{
    protected function accerationRate(){/* some script */}
    protected function deaccerationRate(){/* some script */}
    protected function turnRadius(){/* some script */}
    protected function resistanceFactor(){/* some script */}
}

class Chevrolet extends Car
{
    protected function accerationRate(){/* some script */}
    protected function deaccerationRate(){/* some script */}
    protected function turnRadius(){/* some script */}
    protected function resistanceFactor(){/* some script */}
}

class FordWithDealerRustProofOption extends Ford
{
    use DealerRustProofOption;
}

class ChevroletWithDealerRustProofOption extends Ford
{
    use DealerRustProofOption;
}

trait DealerRustProofOption {
    protected function resistanceFactor() {/* some script */}
}

$car=new Ford();
$car=new Chevrolet();
$car=new FordWithDealerRustProofOption();
$car=new ChevroletWithDealerRustProofOption();
Link to comment
Share on other sites

The code is very odd, pretty much from top to bottom. Are you using a good IDE with code analysis? It should show several complaints.

 

Your car interface is fine. However, then you define this strange base class with methods coming out of nowhere. I guess you want the Template method pattern, but then you need to define the template methods (typically they're abstract).

 

Starting out with a generic interface and then immediately setting a very specific base implementation in stone is also odd. The whole point of an interface is to not dictate implementation details. The inner workings of a car are irrelevant; it just has to be able to drive, stop, turn etc. You're free to define a base class for your own cars, but then don't call it “Car”. The cars of somebody else may work entirely different.

 

Traits exist to provide features (mostly public methods, rarely attributes) which can be mixed into existing classes. For example, Ruby has the big Enumerable mixin which adds lots of useful methods to collection-like classes. It's very much like inheritance, except that you can have multiple providers.

 

A trait which only has protected method doesn't make much sense. I think you're again confusing the outer API of a class with its inner workings. Traits are about the API.

Edited by Jacques1
Link to comment
Share on other sites

Thanks Jacques1,

 

My need is to learn how to extend more than one class.  I go from general to specific, and all is good.  Then I need to add some more methods and get stuck.

 

Okay, I maybe see your point about traits should be public methods and not attributes, however, it still seems useful sometimes to have the ability to tweak the resultant object.  Regardless, let me concede this point.

 

What if I have both CorvetteStingray extends Corvette extends Chevrolet extends Car and and ShelbyGT350 extends Mustang extends Ford extends Car.

 

The dealer I am talking to offers some cool option where they strap a propeller to the back of any car, and now it can fly!  Where can I add the fly() method without giving all cars the ability to fly and without duplicating the fly() method script in multiple classes?

Link to comment
Share on other sites

The dealer I am talking to offers some cool option where they strap a propeller to the back of any car, and now it can fly!  Where can I add the fly() method without giving all cars the ability to fly and without duplicating the fly() method script in multiple classes?

You can't. Not with the model you're using.

 

You're talking about something that happens at runtime - unless you want to make a class for every single possible combination of features (spoiler: you don't) then that sort of behavior needs to be managed dynamically.

 

As a first draft,

interface ObjectFeature {
	public function __construct($object);
}

interface ObjectHasFeatures {
	public function addFeature($feature);
	public function getFeature($class);
	public function hasFeature($class);
}

trait HasFeatures {
	private $_hasfeatures_features = [];
	public function addFeature($class) { $this->_hasfeatures_features[$class] = new $class($this); }
	public function getFeature($class) { return $this->_hasfeatures_features[$class]; }
	public function hasFeature($class) { return isset($this->_hasfeatures_features[$class]); }
}

class Car implements ObjectHasFeatures {
	use HasFeatures;
	private $name;
	public function __construct($name) { $this->name = $name; }
	public function getName() { return $this->name; }
}

class FlyingFeature implements ObjectFeature {
	private $car;
	public function __construct($object) { assert($object instanceof Car); $this->car = $object; }
	public function fly() { echo "*{$this->car->getName()} starts flying*\n"; }
}

$car = new Car("Notion's Car");
$car->addFeature(FlyingFeature::class);

$car->getFeature(FlyingFeature::class)->fly();
https://3v4l.org/Qmm0D
Link to comment
Share on other sites

Thanks requinix

 

I've never seen __construct() put in an interface, however, I suppose it makes sense.  Include the fly() method as well, right?

 

You are right, I don't wish to make a class for every single possible combination of features, and your approach makes sense.

 

You can't. Not with the model you're using.

 

What was specific about the model I am using which prevents doing so?  Are there other models which are commonly used that better support doing so?

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.