अन्य उत्तरों ने इंटरफेस और लक्षणों के बीच अंतर को समझाने का एक बड़ा काम किया। मैं एक उपयोगी वास्तविक दुनिया उदाहरण पर ध्यान केंद्रित करूंगा, विशेष रूप से एक जो प्रदर्शित करता है कि लक्षण उदाहरण चर का उपयोग कर सकते हैं - आपको न्यूनतम बॉयलरप्लेट कोड के साथ एक वर्ग में व्यवहार जोड़ने की अनुमति देता है।
फिर से, जैसा कि दूसरों द्वारा उल्लेख किया गया है, इंटरफेस के साथ जोड़ी को अच्छी तरह से विशेषता देता है, जिससे इंटरफ़ेस को व्यवहार अनुबंध और कार्यान्वयन को पूरा करने के लिए विशेषता की अनुमति मिलती है।
किसी वर्ग में ईवेंट प्रकाशित / सदस्यता क्षमता जोड़ना कुछ कोड आधारों में एक सामान्य परिदृश्य हो सकता है। 3 सामान्य उपाय हैं:
- इवेंट पब / सब कोड के साथ एक बेस क्लास को परिभाषित करें, और फिर जो क्लासेस ईवेंट की पेशकश करना चाहते हैं, वे क्षमताओं को प्राप्त करने के लिए इसे बढ़ा सकते हैं।
- इवेंट पब / सब कोड के साथ एक वर्ग को परिभाषित करें, और फिर अन्य वर्ग जो घटनाओं की पेशकश करना चाहते हैं, रचना के माध्यम से इसका उपयोग कर सकते हैं, तैयार की गई वस्तु को लपेटने के लिए अपने स्वयं के तरीकों को परिभाषित करते हुए, विधि कॉल को प्रॉक्सी करते हैं।
- इवेंट पब / सब कोड के साथ एक विशेषता को परिभाषित करें, और फिर अन्य वर्ग जो घटनाओं की पेशकश करना चाहते हैं
use
, वह गुण प्राप्त कर सकते हैं , उर्फ़ इसे आयात करते हैं, क्षमताओं को प्राप्त करने के लिए।
प्रत्येक कार्य कितनी अच्छी तरह से होता है?
# 1 अच्छा काम नहीं करता है। यह तब तक होता है, जब तक आपको एहसास नहीं होता कि आप आधार वर्ग का विस्तार नहीं कर सकते क्योंकि आप पहले से ही कुछ और कर रहे हैं। मैं इसका एक उदाहरण नहीं दिखाऊंगा क्योंकि यह स्पष्ट होना चाहिए कि इस तरह विरासत का उपयोग करना कितना सीमित है।
# 2 & # 3 दोनों अच्छा काम करते हैं। मैं एक उदाहरण दिखाऊंगा जो कुछ मतभेदों को उजागर करता है।
सबसे पहले, कुछ कोड जो दोनों उदाहरणों के बीच समान होंगे:
एक इंटरफ़ेस
interface Observable {
function addEventListener($eventName, callable $listener);
function removeEventListener($eventName, callable $listener);
function removeAllEventListeners($eventName);
}
और उपयोग प्रदर्शित करने के लिए कुछ कोड:
$auction = new Auction();
// Add a listener, so we know when we get a bid.
$auction->addEventListener('bid', function($bidderName, $bidAmount){
echo "Got a bid of $bidAmount from $bidderName\n";
});
// Mock some bids.
foreach (['Moe', 'Curly', 'Larry'] as $name) {
$auction->addBid($name, rand());
}
ठीक है, अब दिखाते हैं कि किस तरह का कार्यान्वयन Auction
लक्षण का उपयोग करते समय कक्षा भिन्न होगा।
सबसे पहले, यहां बताया गया है कि # 2 (रचना का उपयोग करके) कैसा दिखेगा:
class EventEmitter {
private $eventListenersByName = [];
function addEventListener($eventName, callable $listener) {
$this->eventListenersByName[$eventName][] = $listener;
}
function removeEventListener($eventName, callable $listener) {
$this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
return $existingListener === $listener;
});
}
function removeAllEventListeners($eventName) {
$this->eventListenersByName[$eventName] = [];
}
function triggerEvent($eventName, array $eventArgs) {
foreach ($this->eventListenersByName[$eventName] as $listener) {
call_user_func_array($listener, $eventArgs);
}
}
}
class Auction implements Observable {
private $eventEmitter;
public function __construct() {
$this->eventEmitter = new EventEmitter();
}
function addBid($bidderName, $bidAmount) {
$this->eventEmitter->triggerEvent('bid', [$bidderName, $bidAmount]);
}
function addEventListener($eventName, callable $listener) {
$this->eventEmitter->addEventListener($eventName, $listener);
}
function removeEventListener($eventName, callable $listener) {
$this->eventEmitter->removeEventListener($eventName, $listener);
}
function removeAllEventListeners($eventName) {
$this->eventEmitter->removeAllEventListeners($eventName);
}
}
यहां बताया गया है कि # 3 (लक्षण) कैसा दिखेगा:
trait EventEmitterTrait {
private $eventListenersByName = [];
function addEventListener($eventName, callable $listener) {
$this->eventListenersByName[$eventName][] = $listener;
}
function removeEventListener($eventName, callable $listener) {
$this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
return $existingListener === $listener;
});
}
function removeAllEventListeners($eventName) {
$this->eventListenersByName[$eventName] = [];
}
protected function triggerEvent($eventName, array $eventArgs) {
foreach ($this->eventListenersByName[$eventName] as $listener) {
call_user_func_array($listener, $eventArgs);
}
}
}
class Auction implements Observable {
use EventEmitterTrait;
function addBid($bidderName, $bidAmount) {
$this->triggerEvent('bid', [$bidderName, $bidAmount]);
}
}
ध्यान दें कि अंदर का कोड EventEmitterTrait
वैसा ही है जैसा कि EventEmitter
वर्ग के अंदर क्या है सिवाय विशेषता के कि triggerEvent()
विधि संरक्षित बताए। इसलिए, आपको केवल एक फर्क देखने की जरूरत है वह है Auction
कक्षा का कार्यान्वयन ।
और अंतर बड़ा है। रचना का उपयोग करते समय, हम एक महान समाधान प्राप्त करते हैं, जिससे हमें अपनी पसंद के EventEmitter
अनुसार कई वर्गों द्वारा पुन: उपयोग करने की अनुमति मिलती है । लेकिन, मुख्य दोष यह है कि हमारे पास बहुत सारे बॉयलरप्लेट कोड हैं जिन्हें हमें लिखने और बनाए रखने की आवश्यकता है क्योंकि Observable
इंटरफ़ेस में परिभाषित प्रत्येक विधि के लिए , हमें इसे लागू करने और उबाऊ बॉयलरप्लेट कोड लिखने की आवश्यकता है जो कि केवल इसी पद्धति पर तर्कों के लिए आगे हमारी रचना की EventEmitter
वस्तु। इस उदाहरण में विशेषता का उपयोग करने से हमें बॉयलरप्लेट कोड को कम करने और रखरखाव में सुधार करने में मदद करने से बचने में मदद मिलती है ।
हालाँकि, कई बार ऐसा भी हो सकता है कि आप अपनी Auction
कक्षा को पूर्ण रूप से लागू नहीं करना चाहते हैंObservable
इंटरफ़ेस - शायद आप केवल 1 या 2 विधियों को उजागर करना चाहते हैं, या शायद बिल्कुल भी नहीं, ताकि आप अपनी स्वयं की विधि हस्ताक्षर परिभाषित कर सकें। ऐसे मामले में, आप अभी भी रचना विधि पसंद कर सकते हैं।
लेकिन, अधिकांश परिदृश्यों में विशेषता बहुत सम्मोहक है, खासकर यदि इंटरफ़ेस में बहुत सारी विधियाँ हैं, जो आपको बहुत सारे बॉयलरप्लेट लिखने का कारण बनता है।
* आप वास्तव में दोनों तरह से कर सकते हैं - EventEmitter
यदि आप कभी भी इसका उपयोग करना चाहते हैं तो वर्ग को परिभाषित करें , और EventEmitterTrait
गुण को भी परिभाषित करें, गुण के EventEmitter
अंदर वर्ग कार्यान्वयन का उपयोग करते हुए :)