PHP 中的特性——任何真实世界的例子/最佳实践?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7892749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Traits in PHP – any real world examples/best practices?
提问by Max
Traitshave been one of the biggest additions for PHP 5.4. I know the syntax and understand the idea behind traits, like horizontal code re-use for common stuff like logging, security, caching etc.
特性一直是 PHP 5.4 最大的新增功能之一。我知道语法并理解特征背后的想法,比如水平代码重用于常见的东西,比如日志记录、安全性、缓存等。
However, I still don't know how I would make use of traits in my projects.
但是,我仍然不知道如何在我的项目中使用特性。
Are there any open source projects that already use traits? Any good articles/reading material on how to structure architectures using traits?
是否有任何已经使用特征的开源项目?关于如何使用特征构建架构的任何好的文章/阅读材料?
采纳答案by NikiC
My personal opinion is that there is actually very little application for traits when writing clean code.
我个人的观点是,在编写干净的代码时,实际上很少有 trait 的应用。
Instead of using traits to hack code into a class it is better to pass in the dependencies via the constructor or via setters:
最好通过构造函数或 setter 传入依赖项,而不是使用 trait 将代码破解到类中:
class ClassName {
protected $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
// or
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
}
The main reason why I find that better than using traits is that your code is much more flexible by removing the hard coupling to a trait. For example you could simply pass a different logger class now. This makes your code reusable and testable.
我发现这比使用 trait 更好的主要原因是你的代码通过移除与 trait 的硬耦合而更加灵活。例如,您现在可以简单地传递一个不同的记录器类。这使您的代码可重用和可测试。
回答by Gordon
I guess one would have to look into languages that have Traits for some time now to learn the accepted Good/Best practices. My current opinion on Trait is that you should only use them for code that you would have to duplicate in other classes that share the same functionality.
我想人们现在必须研究具有特征的语言一段时间才能学习公认的良好/最佳实践。我目前对 Trait 的看法是,您应该只将它们用于必须在共享相同功能的其他类中复制的代码。
Example for a Logger trait:
Logger 特性的示例:
interface Logger
{
public function log($message, $level);
}
class DemoLogger implements Logger
{
public function log($message, $level)
{
echo "Logged message: $message with level $level", PHP_EOL;
}
}
trait Loggable // implements Logger
{
protected $logger;
public function setLogger(Logger $logger)
{
$this->logger = $logger;
}
public function log($message, $level)
{
$this->logger->log($message, $level);
}
}
class Foo implements Logger
{
use Loggable;
}
And then you do (demo)
然后你做(演示)
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
I guess the important thing to consider when using traits is that they really are just pieces of code that get copied into the class. This can easily lead to conflicts, for instance, when you try to change visibility of methods, e.g.
我想在使用特征时要考虑的重要一点是,它们实际上只是被复制到类中的代码片段。这很容易导致冲突,例如,当您尝试更改方法的可见性时,例如
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
The above will result in an error (demo). Likewise, any methods declared in the trait that are also already declared in the using class will not get copied into the class, e.g.
以上将导致错误(演示)。同样,任何在 trait 中声明的方法也已经在 using 类中声明过,也不会被复制到类中,例如
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
will print 2 (demo). These are things you will want to avoid because they make errors hard to find. You will also want to avoid putting things into traits that operate on properties or methods of the class that uses it, e.g.
将打印 2 ( demo)。这些是您要避免的事情,因为它们会使错误难以发现。您还将希望避免将事物放入对使用它的类的属性或方法进行操作的特征中,例如
class A
{
use T;
protected $prop = 1;
protected function getProp() {
return $this->prop;
}
}
trait T
{
public function foo()
{
return $this->getProp();
}
}
$a = new A;
echo $a->foo();
works (demo) but now the trait is intimately coupled to A and the whole idea of horizontal reuse is lost.
工作(演示)但现在特征与 A 紧密耦合,并且水平重用的整个想法丢失了。
When you follow the Interface Segregation Principleyou will have many small classes and interfaces. That makes Traits an ideal candidate for the things you mentioned, e.g. crosscutting concerns, but not to compose objects (in a structual sense). In our Logger example above, the trait is completely isolated. It has no dependencies on concrete classes.
当您遵循接口隔离原则时,您将拥有许多小类和接口。这使得 Traits 成为您提到的事物的理想候选者,例如横切关注点,但不是组合对象(在结构意义上)。在我们上面的 Logger 示例中,特征是完全隔离的。它不依赖于具体的类。
We could use aggregation/composition(like shown elsewhere on this page) to achieve the same resulting class, but the drawback of using aggregation/composition is that we will have to add the proxy/delegator methods manually to each and every class then that should be able to log. Traits solve this nicely by allowing me to keep the boilerplate in one place and selectively apply it where needed.
我们可以使用聚合/组合(如本页其他地方所示)来实现相同的结果类,但使用聚合/组合的缺点是我们必须手动将代理/委托方法添加到每个类然后应该能够登录。Traits 很好地解决了这个问题,它允许我将样板文件放在一个地方,并在需要的地方有选择地应用它。
Note: given that traits are a new concept in PHP, all opinion expressed above is subject to change. I've not have had much time to evaluate the concept myself yet. But I hope it is good enough to give you something to think about.
注意:鉴于特征是 PHP 中的一个新概念,以上表达的所有观点都可能发生变化。我还没有太多时间自己评估这个概念。但我希望它足以让你思考。
回答by D. Marti
:) I don't like to theorize and debate about what should be done with something. In this case traits. I'll show you what I find traits useful for and you can either learn from it, or ignore it.
:) 我不喜欢理论化和辩论应该做什么。在这种情况下特征。我将向您展示我认为有用的特质,您可以从中学习,也可以忽略它。
Traits- they are great to apply strategies. Strategy design patterns, in short, are useful when you want the same data to be handled (filtered, sorted, etc) differently.
特性- 它们非常适合应用策略。简而言之,当您希望以不同方式处理(过滤、排序等)相同数据时,策略设计模式非常有用。
For example, you have a list of products that you want to filter out based on some criteria (brands, specs, whatever), or sorted by different means (price, label, whatever). You can create a sorting trait that contains different functions for different sorting types (numeric, string, date, etc). You can then use this trait not only in your product class (as given in the example), but also in other classes that need similar strategies (to apply a numeric sort to some data, etc).
例如,您有一个产品列表,您希望根据某些条件(品牌、规格等)过滤掉这些产品,或者按不同方式(价格、标签等)进行排序。您可以创建一个排序特征,其中包含针对不同排序类型(数字、字符串、日期等)的不同函数。然后,您不仅可以在您的产品类(如示例中给出)中使用此特征,还可以在需要类似策略的其他类中使用此特征(将数字排序应用于某些数据等)。
Try it:
尝试一下:
<?php
trait SortStrategy {
private $sort_field = null;
private function string_asc($item1, $item2) {
return strnatcmp($item1[$this->sort_field], $item2[$this->sort_field]);
}
private function string_desc($item1, $item2) {
return strnatcmp($item2[$this->sort_field], $item1[$this->sort_field]);
}
private function num_asc($item1, $item2) {
if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
return ($item1[$this->sort_field] < $item2[$this->sort_field] ? -1 : 1 );
}
private function num_desc($item1, $item2) {
if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
return ($item1[$this->sort_field] > $item2[$this->sort_field] ? -1 : 1 );
}
private function date_asc($item1, $item2) {
$date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
$date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
if ($date1 == $date2) return 0;
return ($date1 < $date2 ? -1 : 1 );
}
private function date_desc($item1, $item2) {
$date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
$date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
if ($date1 == $date2) return 0;
return ($date1 > $date2 ? -1 : 1 );
}
}
class Product {
public $data = array();
use SortStrategy;
public function get() {
// do something to get the data, for this ex. I just included an array
$this->data = array(
101222 => array('label' => 'Awesome product', 'price' => 10.50, 'date_added' => '2012-02-01'),
101232 => array('label' => 'Not so awesome product', 'price' => 5.20, 'date_added' => '2012-03-20'),
101241 => array('label' => 'Pretty neat product', 'price' => 9.65, 'date_added' => '2012-04-15'),
101256 => array('label' => 'Freakishly cool product', 'price' => 12.55, 'date_added' => '2012-01-11'),
101219 => array('label' => 'Meh product', 'price' => 3.69, 'date_added' => '2012-06-11'),
);
}
public function sort_by($by = 'price', $type = 'asc') {
if (!preg_match('/^(asc|desc)$/', $type)) $type = 'asc';
switch ($by) {
case 'name':
$this->sort_field = 'label';
uasort($this->data, array('Product', 'string_'.$type));
break;
case 'date':
$this->sort_field = 'date_added';
uasort($this->data, array('Product', 'date_'.$type));
break;
default:
$this->sort_field = 'price';
uasort($this->data, array('Product', 'num_'.$type));
}
}
}
$product = new Product();
$product->get();
$product->sort_by('name');
echo '<pre>'.print_r($product->data, true).'</pre>';
?>
As a closing note, I think about traits like accessories (which I can use to alter my data). Similar methods and properties that can get cut out from my classes and be put into a single place, for easy maintenance, shorter and cleaner code.
作为结束语,我考虑了配件等特征(我可以用它来改变我的数据)。类似的方法和属性可以从我的类中删除并放在一个地方,以便于维护、更短、更干净的代码。
回答by thaddeusmt
I am excited for Traits because they solve a common issuewhen developing extensions for the Magento ecommerce platform. The problem occurs when extensions add functionality to a core class (like say the User model) by extending it. This is done by pointing the Zend autoloader (via a XML config file) to use the User model from the extension, and have that new model extend the core model. (example) But what if two extensions override the same model? You get a "race condition" and only one is loaded.
我对 Traits 感到兴奋,因为它们解决了为 Magento 电子商务平台开发扩展时的一个常见问题。当扩展通过扩展向核心类(比如用户模型)添加功能时,就会出现问题。这是通过指向 Zend 自动加载器(通过 XML 配置文件)使用扩展中的用户模型来完成的,并让新模型扩展核心模型。(示例)但是如果两个扩展覆盖了同一个模型呢?你得到一个“竞争条件”,并且只加载了一个。
The solution right now is to edit the extensions so one extends the other's model override class in a chain, and then set the extension configuration to load them in the correct order so the inheritance chain works.
现在的解决方案是编辑扩展,这样一个扩展在链中扩展另一个的模型覆盖类,然后设置扩展配置以正确的顺序加载它们,以便继承链工作。
This system frequently causes errors, and when installing new extensions it's necessary to check for conflicts and edit extensions. This is a pain, and breaks the upgrade process.
该系统经常导致错误,并且在安装新扩展时需要检查冲突并编辑扩展。这很痛苦,并且会中断升级过程。
I think using Traits would be a good way to accomplish the same thing without this annoying model override "race condition". Granted there could still be conflicts if multiple Traits implement methods with the same names, but I would imagine something like a simple namespace convention could solve this for the most part.
我认为使用 Traits 将是完成相同事情的好方法,而无需这种烦人的模型覆盖“竞争条件”。当然,如果多个 Traits 实现具有相同名称的方法,仍然可能存在冲突,但我想像简单的命名空间约定之类的东西可以在很大程度上解决这个问题。
TL;DR I think Traits could be useful for creating extensions/modules/plugins for large PHP software packages like Magento.
TL;DR 我认为 Traits 可以用于为大型 PHP 软件包(如 Magento)创建扩展/模块/插件。
回答by Nico
You could have a trait for read-only object like this:
你可以有这样的只读对象的特征:
trait ReadOnly{
protected $readonly = false;
public function setReadonly($value){ $this->readonly = (bool)$value; }
public function getReadonly($value){ return $this->readonly; }
}
You could detect if that trait is used and determine wheter or not you should write that object in a database, file, etc.
您可以检测是否使用了该特征并确定是否应该将该对象写入数据库、文件等中。