특성 은 PHP 5.4의 가장 큰 추가 사항 중 하나입니다. 로깅, 보안, 캐싱 등과 같은 일반적인 것들에 대한 수평 코드 재사용과 같은 구문을 이해하고 특성의 개념을 이해합니다.
그러나 나는 여전히 프로젝트에서 특성을 어떻게 활용할 것인지 모른다.
이미 특성을 사용하는 오픈 소스 프로젝트가 있습니까? 특성을 사용하여 아키텍처를 구성하는 방법에 대한 좋은 기사 / 읽기 자료가 있습니까?
답변
내 개인적인 의견은 깨끗한 코드를 작성할 때 실제로 특성에 대한 응용 프로그램이 거의 없다는 것입니다.
특성을 사용하여 코드를 클래스에 해킹하는 대신 생성자를 통해 또는 setter를 통해 종속성을 전달하는 것이 좋습니다.
class ClassName {
protected $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
// or
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}
}
특성을 사용하는 것보다 낫다는 주요 이유는 특성에 대한 하드 커플 링을 제거하여 코드가 훨씬 유연하기 때문입니다. 예를 들어 이제 다른 로거 클래스를 전달할 수 있습니다. 이를 통해 코드를 재사용하고 테스트 할 수 있습니다.
답변
받아 들여진 좋은 / 최고의 관행을 배우기 위해 한동안 특성이있는 언어를 조사해야한다고 생각합니다. Trait에 대한 나의 현재 의견은 동일한 기능을 공유하는 다른 클래스에서 복제 해야하는 코드에만 사용해야한다는 것입니다.
로거 특성의 예 :
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;
}
그리고 당신은 ( 데모 )
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
특성을 사용할 때 고려해야 할 중요한 점은 실제로 클래스에 복사되는 코드 조각 일뿐입니다. 예를 들어 메소드의 가시성을 변경하려고 할 때 쉽게 충돌을 일으킬 수 있습니다.
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
위의 오류가 발생합니다 ( demo ). 마찬가지로, using 클래스에서도 이미 선언 된 특성에 선언 된 메소드는 클래스에 복사되지 않습니다. 예 :
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
2 ( 데모 )를 인쇄 합니다. 오류를 찾기 어렵 기 때문에 피하고 싶은 것들입니다. 당신은 또한 그것을 사용하는 클래스의 속성이나 메소드에서 작동하는 특성에 물건을 넣지 않도록 할 것입니다.
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();
작동 하지만 데모 는 이제 A와 밀접한 관련이 있으며 수평 재사용에 대한 모든 아이디어가 사라집니다.
인터페이스 분리 원칙 을 따르면 많은 작은 클래스와 인터페이스가 있습니다. 즉 특색 당신이 언급 한 것들에 대한 이상적인 후보를 만드는 등 우려를 크로스 커팅 있지만 작성 객체의 (a 구조적인 의미에서). 위의 로거 예제에서 특성은 완전히 분리되었습니다. 구체적인 클래스에 의존하지 않습니다.
우리가 사용할 수있는 통합 / 작문 같은 결과 클래스를 달성하기 위해 (이 페이지에 다른 표시를 같은) 만 집계 / 조성물을 사용의 단점은 우리가 각각의 모든 클래스에 수동으로 프록시 / 위임자 방법을 그해야 추가해야 할 것입니다 기록 할 수 있습니다. 특성은 보일러 플레이트를 한 곳에 보관하고 필요한 곳에 선택적으로 적용 할 수있게 해주므로이 문제를 잘 해결할 수 있습니다.
참고 : 특성은 PHP에서 새로운 개념이므로 위에서 언급 한 모든 의견은 변경 될 수 있습니다. 아직 개념을 직접 평가할 시간이 없었습니다. 그러나 나는 당신에게 생각할만한 것을 줄 수 있기를 바랍니다.
답변
🙂 나는 무언가로 무엇을해야하는지 이론화하고 토론하는 것을 좋아하지 않습니다. 이 경우 특성. 나는 당신에게 내가 찾은 특성이 유용한 것을 보여줄 것이며, 그것을 통해 배우거나 무시할 수 있습니다.
특성 – 전략 을 적용 하기에 좋습니다. 간단히 말하면 전략 디자인 패턴은 동일한 데이터를 다르게 처리 (필터링, 정렬 등)하려는 경우에 유용합니다.
예를 들어 일부 기준 (브랜드, 사양 등)을 기준으로 필터링하거나 다른 방법 (가격, 레이블 등)별로 정렬하려는 제품 목록이 있습니다. 다른 정렬 유형 (숫자, 문자열, 날짜 등)에 대해 다른 기능을 포함하는 정렬 특성을 작성할 수 있습니다. 그런 다음이 특성을 제품 클래스 (예제 참조)뿐만 아니라 유사한 전략이 필요한 다른 클래스 (일부 데이터에 숫자 정렬 적용 등)에서도 사용할 수 있습니다.
시도 해봐:
<?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>';
?>
마지막으로, 액세서리와 같은 특성 (데이터를 변경하는 데 사용할 수 있음)에 대해 생각합니다. 쉬운 유지 관리, 더 짧고 깔끔한 코드를 위해 클래스에서 잘라내어 단일 장소에 배치 할 수있는 비슷한 메서드와 속성입니다.
답변
Magento 전자 상거래 플랫폼을위한 확장 기능을 개발할 때 일반적인 문제 를 해결했기 때문에 Traits에 대해 기쁘게 생각 합니다. 확장이 확장을 통해 사용자 클래스와 같은 확장이 핵심 클래스에 기능을 추가 할 때 문제가 발생합니다. 확장에서 사용자 모델을 사용하도록 Zend 오토로더 (XML 구성 파일을 통해)를 가리키고 새 모델이 코어 모델을 확장하도록합니다. ( 예 ) 그러나 두 개의 확장이 동일한 모델을 재정의하면 어떻게됩니까? “경주 조건”이 발생하고 하나만로드됩니다.
해결책은 확장을 편집하여 체인에서 다른 모델 재정의 클래스를 확장 한 다음 확장 구성을 설정하여 상속 체인이 작동하도록 올바른 순서로로드하는 것입니다.
이 시스템은 종종 오류를 발생 시키며 새 확장을 설치할 때 충돌을 확인하고 확장을 편집해야합니다. 이것은 고통스럽고 업그레이드 프로세스를 중단시킵니다.
나는이 성가신 모델이 “경주 조건”을 재정의하지 않고서도 Traits를 사용하는 것이 같은 일을 달성하는 좋은 방법이라고 생각합니다. 여러 Traits가 동일한 이름으로 메소드를 구현하는 경우 여전히 충돌이 발생할 수 있지만 간단한 네임 스페이스 규칙과 같은 것이 대부분 이것을 해결할 수 있다고 생각합니다.
TL; DR Traits가 Magento와 같은 대규모 PHP 소프트웨어 패키지 용 확장 / 모듈 / 플러그인을 만드는 데 유용 할 수 있다고 생각합니다.
답변
다음과 같이 읽기 전용 객체의 특성을 가질 수 있습니다.
trait ReadOnly{
protected $readonly = false;
public function setReadonly($value){ $this->readonly = (bool)$value; }
public function getReadonly($value){ return $this->readonly; }
}
해당 특성이 사용되는지 감지하고 데이터베이스, 파일 등에서 해당 오브젝트를 작성해야하는지 여부를 판별 할 수 있습니다.