खोज…
क्षैतिज कोड पुन: उपयोग की सुविधा के लिए लक्षण
मान लें कि हमारे पास लॉगिंग के लिए एक इंटरफ़ेस है:
interface Logger {
function log($message);
}
अब कहते हैं कि हम में से दो ठोस कार्यान्वयन है Logger
इंटरफ़ेस: FileLogger
और ConsoleLogger
।
class FileLogger implements Logger {
public function log($message) {
// Append log message to some file
}
}
class ConsoleLogger implements Logger {
public function log($message) {
// Log message to the console
}
}
अब यदि आप कुछ अन्य वर्ग Foo
को परिभाषित करते हैं जिसे आप लॉगिंग कार्य करने में सक्षम होना चाहते हैं, तो आप कुछ इस तरह से कर सकते हैं:
class Foo implements Logger {
private $logger;
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function log($message) {
if ($this->logger) {
$this->logger->log($message);
}
}
}
Foo
अब एक Logger
भी है, लेकिन इसकी कार्यक्षमता setLogger()
माध्यम से इसे पारित किए गए Logger
कार्यान्वयन पर निर्भर करती है। अगर हम अब चाहते हैं कि क्लास Bar
में भी यह लॉगिंग मैकेनिज्म हो, तो हमें Bar
क्लास में लॉजिक के इस टुकड़े की नकल करनी होगी।
कोड को डुप्लिकेट करने के बजाय, एक विशेषता को परिभाषित किया जा सकता है:
trait LoggableTrait {
protected $logger;
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function log($message) {
if ($this->logger) {
$this->logger->log($message);
}
}
}
अब जब हमने तर्क को एक लक्षण में परिभाषित किया है, तो हम तर्क का उपयोग Foo
और Bar
कक्षाओं में तर्क को जोड़ने के लिए कर सकते हैं:
class Foo {
use LoggableTrait;
}
class Bar {
use LoggableTrait;
}
और, उदाहरण के लिए, हम Foo
वर्ग का उपयोग इस तरह कर सकते हैं:
$foo = new Foo();
$foo->setLogger( new FileLogger() );
//note how we use the trait as a 'proxy' to call the Logger's log method on the Foo instance
$foo->log('my beautiful message');
संघर्ष समाधान
एक वर्ग में कई लक्षणों का उपयोग करने की कोशिश करने से परस्पर विरोधी तरीके शामिल हो सकते हैं। आपको ऐसे संघर्षों को मैन्युअल रूप से हल करने की आवश्यकता है।
उदाहरण के लिए, आइए इस पदानुक्रम का निर्माण करें:
trait MeowTrait {
public function say() {
print "Meow \n";
}
}
trait WoofTrait {
public function say() {
print "Woof \n";
}
}
abstract class UnMuteAnimals {
abstract function say();
}
class Dog extends UnMuteAnimals {
use WoofTrait;
}
class Cat extends UnMuteAnimals {
use MeowTrait;
}
अब, निम्न वर्ग बनाने की कोशिश करते हैं:
class TalkingParrot extends UnMuteAnimals {
use MeowTrait, WoofTrait;
}
Php दुभाषिया एक घातक त्रुटि लौटाएगा:
घातक त्रुटि: ट्रेट विधि का कहना है कि लागू नहीं किया गया है, क्योंकि TalkingParrot पर अन्य विशेषता विधियों के साथ टकराव होते हैं
इस संघर्ष को हल करने के लिए, हम यह कर सकते हैं:
- कीवर्ड का उपयोग
insteadof
एक और विशेषता से विधि के बजाय एक विशेषता से विधि का उपयोग करने -
WoofTrait::say as sayAsDog;
तरह एक निर्माण के साथ विधि के लिए एक उपनामWoofTrait::say as sayAsDog;
class TalkingParrotV2 extends UnMuteAnimals {
use MeowTrait, WoofTrait {
MeowTrait::say insteadof WoofTrait;
WoofTrait::say as sayAsDog;
}
}
$talkingParrot = new TalkingParrotV2();
$talkingParrot->say();
$talkingParrot->sayAsDog();
यह कोड निम्नलिखित आउटपुट का उत्पादन करेगा:
मियांउ
Woof
एकाधिक लक्षण उपयोग
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayWorld() {
echo 'World';
}
}
class MyHelloWorld {
use Hello, World;
public function sayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
उपरोक्त उदाहरण आउटपुट होगा:
Hello World!
बदलने की विधि दृश्यता
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// Change visibility of sayHello
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// Alias method with changed visibility
// sayHello visibility not changed
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
इस उदाहरण को चलाना:
(new MyClass1())->sayHello();
// Fatal error: Uncaught Error: Call to protected method MyClass1::sayHello()
(new MyClass2())->myPrivateHello();
// Fatal error: Uncaught Error: Call to private method MyClass2::myPrivateHello()
(new MyClass2())->sayHello();
// Hello World!
तो जागरूक में पिछले उदाहरण में है कि हो सकता है MyClass2
से मूल अन-एलियास विधि trait HelloWorld
सुलभ रहता है-है।
एक लक्षण क्या है?
PHP केवल सिंगल इनहेरिटेंस की अनुमति देता है। दूसरे शब्दों में, एक वर्ग केवल एक दूसरे वर्ग का extend
कर सकता है। लेकिन क्या होगा अगर आपको कोई ऐसी चीज़ शामिल करने की ज़रूरत है जो मूल वर्ग में नहीं है? PHP 5.4 से पहले आपको क्रिएटिव होना होगा, लेकिन 5.4 ट्रेट्स में पेश किया गया था। लक्षण आपको मूल रूप से एक वर्ग के एक हिस्से को "कॉपी और पेस्ट" करने की अनुमति देते हैं
trait Talk {
/** @var string */
public $phrase = 'Well Wilbur...';
public function speak() {
echo $this->phrase;
}
}
class MrEd extends Horse {
use Talk;
public function __construct() {
$this->speak();
}
public function setPhrase($phrase) {
$this->phrase = $phrase;
}
}
इसलिए यहां हमारे पास MrEd
, जो पहले से ही Horse
विस्तार कर रहा है। लेकिन सभी घोड़े Talk
नहीं Talk
, इसलिए हमारे पास इसके लिए एक विशेषता है। आइए ध्यान दें कि यह क्या कर रहा है
सबसे पहले, हम अपनी विशेषता को परिभाषित करते हैं। हम इसे ऑटोलेडिंग और नेम्सस्पेस के साथ उपयोग कर सकते हैं (यह भी देखें कि किसी नाम स्थान में एक वर्ग या फ़ंक्शन को संदर्भित करना )। तब हम इसे कीवर्ड use
साथ अपने MrEd
वर्ग में शामिल करते use
।
आप ध्यान देंगे कि MrEd
Talk
फ़ंक्शंस और वेरिएबल्स को परिभाषित किए बिना उनका उपयोग करने के लिए लेता है। याद रखें कि हमने कॉपी और पेस्ट के बारे में क्या कहा था? ये फ़ंक्शन और चर सभी अब कक्षा के भीतर परिभाषित होते हैं, जैसे कि इस वर्ग ने उन्हें परिभाषित किया था।
लक्षण सबसे अधिक बारीकी से सार वर्गों से संबंधित हैं , जिसमें आप चर और कार्यों को परिभाषित कर सकते हैं। आप सीधे एक ट्रेट (यानी new Trait()
) भी नहीं देख सकते। लक्षण एक वर्ग को संक्षेप में एक सार वर्ग या एक इंटरफ़ेस की तरह एक फ़ंक्शन को परिभाषित करने के लिए मजबूर नहीं कर सकते। लक्षण केवल स्पष्ट परिभाषाओं के लिए हैं (क्योंकि आप जितने चाहें उतने इंटरफेसेस implement
कर सकते हैं, इंटरफेसेस देखें)।
मुझे ट्रेट का उपयोग कब करना चाहिए?
पहली बात, जब आपको एक विशेषता पर विचार करना चाहिए, तो अपने आप को यह महत्वपूर्ण प्रश्न पूछना है
क्या मैं अपने कोड का पुनर्गठन करके एक विशेषता का उपयोग करने से बच सकता हूं?
अधिक बार नहीं, इसका उत्तर हां है । लक्षण एकल विरासत के कारण होने वाले किनारे के मामले हैं। लक्षणों का दुरुपयोग या अति प्रयोग करने का प्रलोभन अधिक हो सकता है। लेकिन विचार करें कि एक विशेषता आपके कोड के लिए एक अन्य स्रोत का परिचय देती है, जिसका अर्थ है कि जटिलता की एक और परत है। यहाँ उदाहरण में, हम केवल 3 वर्गों के साथ काम कर रहे हैं। लेकिन लक्षण का मतलब है कि अब आप उससे कहीं अधिक व्यवहार कर सकते हैं। प्रत्येक विशेषता के लिए, आपकी कक्षा से निपटने के लिए इतना कठिन हो जाता है, क्योंकि अब आपको यह पता लगाने के लिए कि क्या यह परिभाषित करता है (और संभवतः जहां टकराव हुआ था, संघर्ष समाधान देखें) प्रत्येक विशेषता का संदर्भ जाना चाहिए। आदर्श रूप में, आपको अपने कोड में यथासंभव कम लक्षण रखने चाहिए।
वर्गों को साफ रखने का गुण
समय के साथ, हमारी कक्षाएं अधिक से अधिक इंटरफेस लागू कर सकती हैं। जब इन इंटरफेस में कई तरीके होते हैं, तो हमारी कक्षा में कुल तरीकों की संख्या बहुत बड़ी हो जाएगी।
उदाहरण के लिए, मान लें कि हमारे पास दो इंटरफेस हैं और उन्हें लागू करने वाला एक वर्ग है:
interface Printable {
public function print();
//other interface methods...
}
interface Cacheable {
//interface methods
}
class Article implements Cachable, Printable {
//here we must implement all the interface methods
public function print(){ {
/* code to print the article */
}
}
Article
वर्ग के अंदर सभी इंटरफ़ेस विधियों को लागू करने के बजाय, हम इन इंटरफेस को लागू करने के लिए अलग-अलग लक्षणों का उपयोग कर सकते हैं, वर्ग को छोटा रखते हुए और वर्ग से इंटरफ़ेस कार्यान्वयन के कोड को अलग कर सकते हैं।
उदाहरण के लिए, Printable
करने Printable
इंटरफ़ेस को लागू करने के लिए हम इस विशेषता को बना सकते हैं:
trait PrintableArticle {
//implements here the interface methods
public function print() {
/* code to print the article */
}
}
और वर्ग का उपयोग गुण करें:
class Article implements Cachable, Printable {
use PrintableArticle;
use CacheableArticle;
}
प्राथमिक लाभ यह होगा कि हमारे इंटरफ़ेस-कार्यान्वयन के तरीकों को बाकी वर्ग से अलग किया जाएगा, और एक विशेषता में संग्रहीत किया जाएगा जो उस विशेष प्रकार की वस्तु के लिए इंटरफ़ेस को लागू करने की एकमात्र जिम्मेदारी है।
ट्रेट्स का उपयोग करके एक सिंगलटन को लागू करना
डिस्क्लेमर : किसी भी तरह से यह उदाहरण एकल गीतों के उपयोग की वकालत नहीं करता है। सिंगलेट्स का उपयोग बहुत सावधानी से किया जाना है।
PHP में एक सिंगलटन को लागू करने का एक मानक तरीका है:
public class Singleton {
private $instance;
private function __construct() { };
public function getInstance() {
if (!self::$instance) {
// new self() is 'basically' equivalent to new Singleton()
self::$instance = new self();
}
return self::$instance;
}
// Prevent cloning of the instance
protected function __clone() { }
// Prevent serialization of the instance
protected function __sleep() { }
// Prevent deserialization of the instance
protected function __wakeup() { }
}
कोड दोहराव को रोकने के लिए, इस व्यवहार को एक विशेषता में निकालना एक अच्छा विचार है।
trait SingletonTrait {
private $instance;
protected function __construct() { };
public function getInstance() {
if (!self::$instance) {
// new self() will refer to the class that uses the trait
self::$instance = new self();
}
return self::$instance;
}
protected function __clone() { }
protected function __sleep() { }
protected function __wakeup() { }
}
अब एक एकल के रूप में कार्य करने की इच्छा रखने वाला कोई भी वर्ग केवल गुण का उपयोग कर सकता है:
class MyClass {
use SingletonTrait;
}
// Error! Constructor is not publicly accessible
$myClass = new MyClass();
$myClass = MyClass::getInstance();
// All calls below will fail due to method visibility
$myClassCopy = clone $myClass; // Error!
$serializedMyClass = serialize($myClass); // Error!
$myClass = deserialize($serializedMyclass); // Error!
भले ही अब एक सिंगलटन को क्रमबद्ध करना असंभव है, लेकिन फिर भी डीज़रीलाइज़ विधि को बाधित करना उपयोगी है।