रेल्स: लॉ ऑफ़ डेमेटर कन्फ्यूजन


13

मैं एक पुस्तक पढ़ रहा हूं जिसका नाम है रेल्स एंटीपैटर्न और वे कानून के कानून को तोड़ने से बचने के लिए प्रतिनिधिमंडल का उपयोग करने के बारे में बात करते हैं। यहाँ उनका प्रमुख उदाहरण दिया गया है:

उनका मानना ​​है कि कंट्रोलर में कुछ इस तरह से कॉल करना बुरा है (और मैं सहमत हूं)

@street = @invoice.customer.address.street

उनका प्रस्तावित समाधान निम्नलिखित करना है:

class Customer

    has_one :address
    belongs_to :invoice

    def street
        address.street
    end
end

class Invoice

    has_one :customer

    def customer_street
        customer.street
    end
end

@street = @invoice.customer_street

वे यह कह रहे हैं कि चूंकि आप केवल एक डॉट का उपयोग करते हैं, इसलिए आप यहां कानून के कानून को नहीं तोड़ रहे हैं। मुझे लगता है कि यह गलत है, क्योंकि आप अभी भी चालान की सड़क पाने के लिए ग्राहक से गुजर रहे हैं। मुझे मुख्य रूप से एक ब्लॉग पोस्ट से मुझे यह विचार मिला:

http://www.dan-manges.com/blog/37

ब्लॉग पोस्ट में प्रमुख उदाहरण है

class Wallet
  attr_accessor :cash
end
class Customer
  has_one :wallet

  # attribute delegation
  def cash
    @wallet.cash
  end
end

class Paperboy
  def collect_money(customer, due_amount)
    if customer.cash < due_ammount
      raise InsufficientFundsError
    else
      customer.cash -= due_amount
      @collected_amount += due_amount
    end
  end
end

ब्लॉग पोस्ट में कहा गया है कि हालांकि customer.cashइसके बजाय केवल एक डॉट है customer.wallet.cash, यह कोड अभी भी कानून के कानून का उल्लंघन करता है।

अब पेपरबॉय कलेक्ट_मनी पद्धति में, हमारे पास दो डॉट्स नहीं हैं, हमारे पास "customer.cash" में एक है। क्या इस प्रतिनिधिमंडल ने हमारी समस्या हल कर दी है? हर्गिज नहीं। यदि हम व्यवहार को देखें, तो कैशबैक प्राप्त करने के लिए एक पेपरबॉय अभी भी सीधे ग्राहक के बटुए में पहुंच रहा है।

संपादित करें

मैं पूरी तरह से समझता हूं और सहमत हूं कि यह अभी भी उल्लंघन है और मुझे Walletआहरण में एक विधि बनाने की आवश्यकता है जो मेरे लिए भुगतान को संभालती है और मुझे उस पद्धति को Customerकक्षा के अंदर कॉल करना चाहिए । मुझे जो नहीं मिलता है, वह है कि इस प्रक्रिया के अनुसार, मेरा पहला उदाहरण अभी भी कानून के कानून का उल्लंघन करता है क्योंकि सड़क पाने के लिए Invoiceअभी भी सीधे पहुंच रहा है Customer

क्या कोई मुझे भ्रम को दूर करने में मदद कर सकता है। मैं पिछले 2 दिनों से इस विषय को अंदर आने देने की कोशिश कर रहा हूं, लेकिन यह अभी भी भ्रामक है।


2
इसी तरह का प्रश्न यहाँ
थोरस्टन मुलर

मुझे नहीं लगता कि ब्लॉग से दूसरा उदाहरण (पेपरबॉय) कानून के कानून का उल्लंघन करता है। यह खराब डिजाइन हो सकता है (आप मान रहे हैं कि ग्राहक नकदी के साथ भुगतान करेगा), लेकिन यह कानून का उल्लंघन नहीं है। इस कानून को तोड़ने से सभी डिज़ाइन त्रुटियां नहीं होती हैं। लेखक भ्रमित IMO है।
एंड्रेस एफ।

जवाबों:


24

आपका पहला उदाहरण Demeter कानून का उल्लंघन नहीं करता है। हां, कोड के साथ जैसा कि यह खड़ा है, कहती @invoice.customer_streetहै कि एक ही मूल्य प्राप्त करने के लिए होता है कि एक काल्पनिक @invoice.customer.address.streetहोगा, लेकिन ट्रैवर्सल के प्रत्येक चरण में, लौटाए गए मूल्य का निर्धारण ऑब्जेक्ट द्वारा पूछा जा रहा है - यह नहीं है कि "पेपरबॉय में पहुंचता है" ग्राहक का बटुआ ", यह है कि" पेपरबॉय ग्राहक से नकदी मांगता है, और ग्राहक अपने बटुए से नकदी प्राप्त करने के लिए होता है "।

जब आप कहते हैं @invoice.customer.address.street, आप ग्राहक और पते के आंतरिक का ज्ञान ग्रहण कर रहे हैं - यह बुरी बात है। जब आप कहते हैं @invoice.customer_street, आप पूछ रहे हैं invoice, "अरे, मैं ग्राहक की सड़क पसंद करूंगा, आप तय करें कि आप इसे कैसे प्राप्त करते हैं "। ग्राहक फिर अपने पते पर कहता है, "अरे मैं आपकी सड़क पसंद करूंगा, आप तय करें कि आपको यह कैसे मिलेगा "।

Demeter के जोर है नहीं 'क्या तुमने कभी पता नहीं कर सकते हैं मानों आप, यह बजाय है वस्तुओं से दूर आप से "ग्राफ में दूर' अपने आप 'आदेश मान प्राप्त करने में अब तक वस्तु ग्राफ के साथ पार करने के लिए नहीं हैं।

मैं मानता हूं कि यह एक सूक्ष्म अंतर की तरह लग सकता है, लेकिन इस पर विचार करें: Demeter- आज्ञाकारी कोड में, परिवर्तनों के आंतरिक प्रतिनिधित्व के लिए कोड को कितना बदलना होगा address? गैर-डिमॉटर-अनुरूप कोड के बारे में क्या?


यह ठीक उसी तरह का स्पष्टीकरण है जिसकी मुझे तलाश थी! धन्यवाद।
user2158382

बहुत अच्छी व्याख्या। मेरे पास एक प्रश्न है: 1) यदि चालान ऑब्जेक्ट चालान के ग्राहक को एक ग्राहक वस्तु लौटना चाहता है, तो इसका मतलब यह नहीं है कि यह वही ग्राहक वस्तु है जो इसे आंतरिक रूप से रखती है। यह केवल क्लाइंट पर बनाई गई एक वस्तु हो सकती है, ग्राहक को वापस लौटाने के उद्देश्य से इसमें कई मूल्यों के साथ एक अच्छा पैक डेटा सेट। आपके द्वारा प्रस्तुत तर्क का उपयोग करते हुए, आप कह रहे हैं कि इनवॉइस में एक फ़ील्ड नहीं हो सकता है जो एक से अधिक डेटा का प्रतिनिधित्व करता है। या क्या मैं कुछ न कुछ भूल रहा हूं।
zumalifeguard

2

पहला उदाहरण और दूसरा वास्तव में बहुत समान नहीं हैं। जबकि पहला "एक डॉट" के सामान्य नियमों के बारे में बात कर रहा है, दूसरा ओओ डिजाइन में अन्य चीजों के बारे में अधिक बात करता है, विशेष रूप से " बताओ मत करो "

प्रतिनिधिमंडल कानून के उल्लंघन से बचने के लिए एक प्रभावी तकनीक है, लेकिन केवल व्यवहार के लिए, विशेषताओं के लिए नहीं। - दूसरे उदाहरण से, डैन का ब्लॉग

फिर, " केवल व्यवहार के लिए, विशेषताओं के लिए नहीं "

यदि आप विशेषताएँ माँगते हैं, तो आप पूछने वाले हैं । "अरे यार, तुम्हारे पास जेब में कितने पैसे हैं? मुझे दिखाओ, मैं मूल्यांकन करूंगा कि क्या तुम यह भुगतान कर सकते हो।" यह गलत है, कोई भी शॉपिंग क्लर्क इस तरह का व्यवहार नहीं करेगा। इसके बजाय, वे कहेंगे, "कृपया भुगतान करें"

customer.pay(due_amount)

यदि वह भुगतान करना चाहिए और यदि वह भुगतान कर सकता है तो मूल्यांकन करना ग्राहक का अपना कर्तव्य होगा। और ग्राहक को भुगतान करने के लिए कहने के बाद क्लर्क का कार्य समाप्त हो जाता है।

तो, क्या दूसरा उदाहरण साबित करता है कि पहला गलत है?

मेरी राय में। नहीं , जब तक:

1. आप इसे आत्म-बाधा के साथ करते हैं।

जब आप @invoiceप्रतिनिधि द्वारा ग्राहक की सभी विशेषताओं का उपयोग कर सकते हैं , तो आपको शायद ही कभी सामान्य मामलों में इसकी आवश्यकता होगी।

किसी ऐसे पृष्ठ के बारे में सोचें जो किसी Rails ऐप में चालान दिखा रहा है। ग्राहक का विवरण दिखाने के लिए शीर्ष पर एक अनुभाग होगा। तो, इनवॉइस टेम्पलेट में, क्या आप इस तरह से कोड करेंगे?

#customer-info
  = @invoice.customer_name
  = @invoice.customer_address
  ....

यह गलत और अक्षम है। बेहतर तरीका है

#customer-info
  = render partial: 'invoice_header_customer', 
           locals: {customer: @invoice.customer}

फिर ग्राहक को सभी विशेषताओं को संसाधित करने के लिए आंशिक रूप से ग्राहक के होने दें।

तो आम तौर पर आप की जरूरत नहीं है कि लेकिन आपके पास हाल के सभी चालान दिखाने वाले एक सूची पृष्ठ हो सकते हैं, liग्राहक के नाम को प्रदर्शित करने वाले प्रत्येक में एक ब्रीफिंग फ़ील्ड है । इस मामले में, आपको ग्राहक की विशेषता दिखाने की आवश्यकता है, और यह टेम्पलेट को कोड करने के लिए पूरी तरह से वैध है

= @invoice.customer_name

2. इस विधि कॉल के आधार पर आगे कोई कार्रवाई नहीं है।

सूची पृष्ठ के उपरोक्त मामले में, चालान ने ग्राहक का नाम विशेषता पूछा, लेकिन यह वास्तविक उद्देश्य " मुझे अपना नाम दिखाएं " है, इसलिए यह मूल रूप से अभी भी एक व्यवहार है, लेकिन विशेषता नहीं है । इस विशेषता के आधार पर आगे मूल्यांकन और कार्रवाई नहीं होती है, जैसे कि यदि आपका नाम "माइक" है, तो मैं आपको पसंद करूंगा और आपको 30 दिनों का और क्रेडिट दूंगा। नहीं, इनवॉइस केवल "मुझे अपना नाम दिखाएँ", और नहीं। इसलिए यह उदाहरण 2 में "बताओ मत पूछो" नियम के अनुसार पूरी तरह से स्वीकार्य है।


0

दूसरे लेख में आगे पढ़ें और मुझे लगता है कि यह विचार स्पष्ट हो जाएगा। विचार बस ग्राहक के पास भुगतान करने की क्षमता है और पूरी तरह से छिपाने के लिए जहां मामला रखा गया है। क्या यह एक क्षेत्र है, एक बटुए का सदस्य है, या कुछ और है? फोन करने वाले को पता नहीं होता है, उसे जानने की जरूरत नहीं होती है और अगर उस कार्यान्वयन विवरण में बदलाव नहीं होता है तो वह बदल नहीं सकता है।

class Wallet
  attr_accessor :cash
  def withdraw(amount)
     raise InsufficientFundsError if amount > cash
     cash -= amount
     amount
  end
end
class Customer
  has_one :wallet
  # behavior delegation
  def pay(amount)
    @wallet.withdraw(amount)
  end
end
class Paperboy
  def collect_money(customer, due_amount)
    @collected_amount += customer.pay(due_amount)
  end
end

इसलिए मुझे लगता है कि आपका दूसरा संदर्भ एक अधिक उपयोगी सिफारिश दे रहा है।

"एक बिंदु" केवल विचार, एक आंशिक सफलता है, इसमें वह कुछ गहरा विवरण छिपाता है, लेकिन फिर भी अलग-अलग घटकों के बीच युग्मन को बढ़ाता है।


क्षमा करें, शायद मैं स्पष्ट नहीं था, लेकिन मैं दूसरा उदाहरण पूरी तरह से समझता हूं और मैं समझता हूं कि आपके द्वारा पोस्ट किए गए अमूर्त को बनाने की आवश्यकता है, लेकिन जो मैं नहीं समझता वह मेरा पहला उदाहरण है। ब्लॉग पोस्ट के अनुसार, मेरा पहला उदाहरण गलत है
user2158382

0

डैन जैसी आवाज़ ने इस लेख से उनके उदाहरण को बाहर निकाल दिया: द पेपरबॉय, द वॉलेट और द लॉ ऑफ़ डेमेटर

कानून की विधि किसी वस्तु का एक तरीका केवल निम्न प्रकार की वस्तुओं के तरीकों को लागू करना चाहिए:

  1. अपने आप
  2. इसके पैरामीटर
  3. किसी भी वस्तु को बनाता / उत्पन्न करता है
  4. इसके प्रत्यक्ष घटक ऑब्जेक्ट

कब और कैसे कानून के कानून को लागू करने के लिए

तो अब आपको कानून की अच्छी समझ है और यह लाभ है, लेकिन हमने अभी तक चर्चा नहीं की है कि मौजूदा कोड में उन स्थानों की पहचान कैसे करें जहां हम इसे लागू कर सकते हैं (और बस उतना ही महत्वपूर्ण है, जहां इसे लागू नहीं करना है ...)

  1. जंजीर 'प्राप्त' कथन - विधि का कानून लागू करने वाला पहला, सबसे स्पष्ट स्थान बार-बार get() बयान देने वाले कोड का स्थान है ,

    value = object.getX().getY().getTheValue();

    जैसे कि जब इस उदाहरण के लिए हमारे विहित व्यक्ति को पुलिस द्वारा खींच लिया गया, तो हम देख सकते हैं:

    license = person.getWallet().getDriversLicense();

  2. बहुत सारी 'अस्थायी' वस्तुएं - उपरोक्त लाइसेंस उदाहरण कोई बेहतर नहीं होगा यदि कोड जैसा दिखता है,

    Wallet tempWallet = person.getWallet(); license = tempWallet.getDriversLicense();

    यह बराबर है, लेकिन पता लगाने के लिए कठिन है।

  3. कई वर्गों का आयात - जावा परियोजना पर मैं काम करता हूं, हमारा एक नियम है कि हम केवल उन कक्षाओं का आयात करते हैं जिनका हम वास्तव में उपयोग करते हैं; आप कभी ऐसा कुछ नहीं देखते

    import java.awt.*;

    हमारे स्रोत कोड में। जगह में इस नियम के साथ, एक ही पैकेज से आने वाले सभी दर्जन या तो आयात बयानों को देखना असामान्य नहीं है। यदि यह आपके कोड में हो रहा है, तो उल्लंघन के अस्पष्ट उदाहरणों को देखने के लिए यह एक अच्छी जगह हो सकती है। यदि आपको इसे आयात करने की आवश्यकता है, तो आप इसे युग्मित कर रहे हैं। यदि यह बदल जाता है, तो आपको भी करना पड़ सकता है। कक्षाओं को स्पष्ट रूप से आयात करके, आप यह देखना शुरू कर देंगे कि वास्तव में आपकी कक्षाएं कितनी युग्मित हैं।

मैं समझता हूं कि आपका उदाहरण रूबी में है, लेकिन यह सभी ओओपी भाषाओं में लागू होना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.