NotionCommotion Posted January 2, 2020 Share Posted January 2, 2020 (edited) For solely to reduce duplicated code and when injection isn't applicable, should one use inheritance or traits? <?php abstract class BaseClass { private $id, $name; public function __construct(int $id, string $name) { $this->id=$id; $this->name=$name; } public function getId():int { return $this->id; } public function getName():string { return $this->name; } } <?php class A extends BaseClass{} class B extends BaseClass{} class C extends BaseClass { private $foo; public function __construct(int $id, string $name, Foo $foo) { parent::__construct($id, $name); $this->foo=$foo; } public function getFoo():Foo { return $this->foo; } } <?php trait MyTrait { private $id, $name; public function __construct(int $id, string $name) { $this->id=$id; $this->name=$name; } public function getId():int { return $this->id; } public function getName():string { return $this->name; } } <?php class A { use MyTrait; } class B { use MyTrait; } class C { use MyTrait; private $foo; public function __construct(int $id, string $name, Foo $foo) { $this->id=$id; $this->name=$name; $this->foo=$foo; } public function getFoo():Foo { return $this->foo; } } Edited January 2, 2020 by NotionCommotion Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/ Share on other sites More sharing options...
requinix Posted January 2, 2020 Share Posted January 2, 2020 Traits are copy and paste. If there is any meaning to the code besides you wanting to simply save yourself some copying and pasting then a trait is not the right answer. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573080 Share on other sites More sharing options...
NotionCommotion Posted January 2, 2020 Author Share Posted January 2, 2020 38 minutes ago, requinix said: Traits are copy and paste. If there is any meaning to the code besides you wanting to simply save yourself some copying and pasting then a trait is not the right answer. Just for copy and past reasons. I am still undecided on whether I prefer inheritance or traits for this scenario. There is no reason why Classes A, B, and C shouldn't have access to the $id and $name property so a trait is better. But then I can just make $id and $name protected so it doesn't matter. But then again using inheritance implies some base functionality and better defines what is additional (i.e. Foo), so maybe inheritance is better. Or maybe it really doesn't matter and I should just be consistent... Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573081 Share on other sites More sharing options...
gizmola Posted January 2, 2020 Share Posted January 2, 2020 Foo/Bar examples aren't helpful in exploring a question like this. I also think you are leaving out a key ingredient when you aren't pairing inheritance with one or more interfaces. Setter/Getter examples are also no helpful when you have __set and __get magic methods available. Where I have seen traits used frequently, is in providing oft repeated exception throwing/handling code. This often includes logging, so you almost always see classes injecting a logger object and then logging out information when exceptions/throwables are caught/thrown. You could also put these routines in a base class, but it's extra work and possibly extra inheritance for something that is necessary but not intrinsic to a specific class hierarchy. Traits and classes are not mutually exclusive nor in my opinion should they be used that way. OOP languages were created in order to help model paradigms and simplify the passing of data, messaging and events between things. Typically it's best to start with OOP, DI and other design patterns, and only when things become particularly odious, or are highly disconnected from the data and behavior you are modeling in the core class hierarchy, would I look for alternatives like a Trait. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573083 Share on other sites More sharing options...
NotionCommotion Posted January 2, 2020 Author Share Posted January 2, 2020 2 hours ago, gizmola said: Foo/Bar examples aren't helpful in exploring a question like this. I also think you are leaving out a key ingredient when you aren't pairing inheritance with one or more interfaces. Just looking for ways to limit cutting and pasting identical code and then making a change to one version and failing to update all the rest. You are likely correct that I am leaving out a key ingredient regarding interfaces and are still trying to better understand their place. 1 hour ago, gizmola said: Setter/Getter examples are also no helpful when you have __set and __get magic methods available. I am mixed when it comes to __set and __get. When first learning about them, I used them exclusively, but then found (at least for me) that they often are more trouble than they are worth for when simple setters and getters can be used. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573086 Share on other sites More sharing options...
maxxd Posted January 3, 2020 Share Posted January 3, 2020 I could be wrong, but I believe you can't type hint to a trait where obviously you can to a parent class. So depending on how you've got things set up and what exactly you're trying to do, that could be a consideration. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573088 Share on other sites More sharing options...
NotionCommotion Posted January 3, 2020 Author Share Posted January 3, 2020 2 hours ago, maxxd said: I could be wrong, but I believe you can't type hint to a trait where obviously you can to a parent class. So depending on how you've got things set up and what exactly you're trying to do, that could be a consideration. Thanks maxxd. If so, then definitely a valid consideration. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573090 Share on other sites More sharing options...
requinix Posted January 3, 2020 Share Posted January 3, 2020 It is true, traits cannot be hinted - they are designed to be, and really do work very much like, language-assisted copy and paste. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573092 Share on other sites More sharing options...
maxxd Posted January 3, 2020 Share Posted January 3, 2020 14 hours ago, requinix said: It is true, traits cannot be hinted - they are designed to be, and really do work very much like, language-assisted copy and paste. Thanks for the verification - it didn't make much sense that you would be able to hint on them given the way they're used, but I don't have a ton of experience with traits... Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573098 Share on other sites More sharing options...
gizmola Posted January 6, 2020 Share Posted January 6, 2020 Echoing Requinix on this, but they aren't all that much different than an include/require of some code in a class body. Obviously better than that, from a documentation point of view, in that at least you see the declaration of the trait at the top of the class, but otherwise, it's about the same. In regards to interfaces, I would say it's best practice and certainly used in large projects and frameworks that class designers start with one or more interfaces to lay out the api contract. For example, take a look at the Laravel Cache system. Let's say you have a type of cache that doesn't come out of the box. How do you make your ACME cache work with Laravel? Laravel has a contracts directory where the interfaces are defined, and for cache we find this interface for a "Store" https://github.com/laravel/framework/blob/5.3/src/Illuminate/Contracts/Cache/Store.php You can see that this interface describes the cache api with methods like get, put, increment, decrement, forget, etc. When you look at the implementation of a specific cache store with bundled support like Redis for example, this is what you find in RedisStore: <?php namespace Illuminate\Cache; use Illuminate\Contracts\Cache\Store; use Illuminate\Contracts\Redis\Database as Redis; class RedisStore extends TaggableStore implements Store { /** * The Redis database connection. * * @var \Illuminate\Redis\Database */ protected $redis; /** * A string that should be prepended to keys. * * @var string */ protected $prefix; /** * The Redis connection that should be used. * * @var string */ protected $connection; /** * Create a new Redis store. * * @param \Illuminate\Redis\Database $redis * @param string $prefix * @param string $connection * @return void */ public function __construct(Redis $redis, $prefix = '', $connection = 'default') { $this->redis = $redis; $this->setPrefix($prefix); $this->setConnection($connection); } /** * Retrieve an item from the cache by key. * * @param string|array $key * @return mixed */ public function get($key) { if (! is_null($value = $this->connection()->get($this->prefix.$key))) { return $this->unserialize($value); } } /** * Store an item in the cache for a given number of minutes. * * @param string $key * @param mixed $value * @param float|int $minutes * @return void */ public function put($key, $value, $minutes) { $value = $this->serialize($value); $this->connection()->setex($this->prefix.$key, (int) max(1, $minutes * 60), $value); } So it should be fairly obvious to someone looking to add this capability to their Laravel project that an Acme cache will need an AcmeStore class that implements Illuminate\Contracts\Cache\Store. I looked around a bit at the Laravel Docs and found this section that outlines the same thing I've discussed using MongoDB as an example: https://laravel.com/docs/5.1/cache#adding-custom-cache-drivers As a dependency injection framework, there's going to be some sort of service system that takes care of instantiating a service, which is why this discusses how to register the store. Here is a MongoDB Laravel cache implementation I found on Packagist: https://github.com/alfa6661/laravel-mongodb-cache/blob/master/src/MongoStore.php When you look at this code, you'll see that it extends the Laravel DatabaseStore class rather than implementing the store interface, but that simply has to do with the fact that Mongo is a type of database and has some database-esque features that most pure cache servers don't have. When you look at the DatabaseStore Class you can see that it too implements the Store interface: class DatabaseStore implements Store { I think looking at how Laravel and Symfony have done things could be helpful in seeing the language features and techniques class designers use to create reusable and extendable class hierarchies. Another interesting example I could suggest to look at would be the Symfony finder component. It's a component library that facilitates searching for and working with files and directories on a local or network file system. The finder class implements 2 Standard PHP Library interfaces: class Finder implements \IteratorAggregate, \Countable This allows a finder object to be used in a statement like: if (count($finder) > 0). Implementing the IteratorAggregate interface facilitates iterating through an object using foreach(). If you are interested look at the implementation public function getIterator() in the class source. This is all far more interesting and important than Traits. You'll also probably start to notice that the interfaces are used to typehint dependency injected objects in related classes, which keeps those classes loosely coupled and yet still with some type resiliency. I can pass an object of a class that implements the interface, but if an object that does not implement the interface is passed, a fatal error will be generated. Quote Link to comment https://forums.phpfreaks.com/topic/309789-traits-or-inheritance-for-reducing-duplicated-code/#findComment-1573146 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.