क्या कोई विशिष्ट डिज़ाइन रणनीति है जिसे अपरिवर्तनीय वस्तुओं का उपयोग करते समय अधिकांश चिकन-एंड-एग समस्याओं को हल करने के लिए लागू किया जा सकता है?


17

एक OOP पृष्ठभूमि (जावा) से आ रहा है, मैं अपने दम पर स्काला सीख रहा हूं। जबकि मैं आसानी से व्यक्तिगत रूप से अपरिवर्तनीय वस्तुओं का उपयोग करने के फायदे देख सकता हूं, मुझे यह देखने में कठिन समय हो रहा है कि कोई भी इस तरह से पूरे एप्लिकेशन को कैसे डिज़ाइन कर सकता है। मैं एक उदाहरण दूंगा:

कहें कि मेरे पास ऐसी वस्तुएँ हैं जो "सामग्री" और उनके गुणों का प्रतिनिधित्व करती हैं (मैं एक गेम डिजाइन कर रहा हूं, इसलिए मुझे वास्तव में वह समस्या है), जैसे पानी और बर्फ। मेरे पास एक "प्रबंधक" होगा जो ऐसी सभी सामग्री के उदाहरणों का मालिक है। एक संपत्ति ठंड और पिघलने बिंदु होगी, और क्या सामग्री जम जाती है या पिघल जाती है।

[संपादित करें] सभी सामग्री उदाहरण "सिंगलटन" हैं, एक जावा एनम की तरह।

मैं "पानी" कहना चाहता हूं कि यह "सी" पर "बर्फ" और "बर्फ" के लिए जमा हो जाता है यह कहने के लिए कि यह 1 सी पर "पानी" के लिए पिघला देता है। लेकिन अगर पानी और बर्फ अपरिवर्तनीय हैं, तो वे एक दूसरे को निर्माता मापदंडों के रूप में संदर्भ नहीं दे सकते हैं, क्योंकि उनमें से एक को पहले बनाना होगा, और उस एक को निर्माता पैरामीटर के रूप में नहीं-अभी तक मौजूदा अन्य का संदर्भ नहीं मिल सकता है। मैं उन दोनों को प्रबंधक को एक संदर्भ देकर इसे हल कर सकता था ताकि वे इसे अन्य सामग्री उदाहरण को खोजने के लिए क्वेरी कर सकें जो उन्हें हर बार उनकी ठंड / पिघलने वाली संपत्तियों के लिए कहा जा रहा है, लेकिन फिर मुझे प्रबंधक के बीच वही समस्या आती है और सामग्री, कि उन्हें एक दूसरे के लिए एक संदर्भ की आवश्यकता है, लेकिन यह केवल उनमें से एक के लिए कंस्ट्रक्टर में प्रदान किया जा सकता है, इसलिए या तो प्रबंधक या सामग्री अपरिवर्तनीय नहीं हो सकती है।

क्या इस समस्या के आसपास उनका कोई रास्ता नहीं है, या क्या मुझे "कार्यात्मक" प्रोग्रामिंग तकनीकों, या इसे हल करने के लिए कुछ अन्य पैटर्न का उपयोग करने की आवश्यकता है?


2
मेरे लिए, जिस तरह से आप राज्य करते हैं, वहां न तो पानी है और न ही बर्फ। बस h2oसामग्री है
gnat

1
मुझे पता है कि यह अधिक "वैज्ञानिक अर्थ" बना देगा, लेकिन एक खेल में इसे संदर्भ के आधार पर "चर" गुणों वाली एक सामग्री के बजाय "निश्चित" गुणों के साथ दो अलग-अलग सामग्रियों के रूप में मॉडल करना आसान है।
सेबेस्टियन डायट

सिंगलटन एक गूंगा विचार है।
17-16 बजे डेडएमजी सेप

@ डीडीएमजी वेल, ओके। वे असली जावा सिंगलेट्स नहीं हैं। मेरा तात्पर्य यह है कि प्रत्येक के एक से अधिक उदाहरण बनाने का कोई मतलब नहीं है, क्योंकि वे अपरिवर्तनीय हैं और एक दूसरे के बराबर होंगे। वास्तव में, मेरे पास कोई वास्तविक स्थिर उदाहरण नहीं होगा। मेरे "रूट" ऑब्जेक्ट OSGi सेवाएँ हैं।
सेबेस्टियन डायट

इस अन्य प्रश्न का उत्तर मेरे संदेह की पुष्टि करता प्रतीत होता है कि चीजें वास्तव में अपरिवर्तनीयों के साथ जल्दी से जटिल हो जाती हैं: प्रोग्रामर.स्टैकएक्सचेंज.
com/questions/68058/

जवाबों:


2

इसका हल थोड़ा धोखा देना है। विशेष रूप से:

  • A बनाएँ, लेकिन B के लिए इसका संदर्भ छोड़ दें।

  • बी बनाएं, और यह ए की ओर इशारा करता है।

  • B से इंगित करने के लिए A को अपडेट करें और इसके बाद A या B को अपडेट न करें।

यह या तो स्पष्ट रूप से किया जा सकता है (उदाहरण C ++ में):

struct List {
    int n;
    List *next;

    List(int n, List *next)
        : n(n), next(next);
};

// Return a list containing [0,1,0,1,...].
List *binary(void)
{
    List *a = new List(0, NULL);
    List *b = new List(1, a);
    a->next = b; // Evil, but necessary.
    return a;
}

या अंतर्निहित (हास्केल में उदाहरण):

binary :: [Int]
binary = a where
    a = 0 : b
    b = 1 : a

हास्केल उदाहरण परस्पर निर्भर अपरिवर्तनीय मूल्यों के भ्रम को प्राप्त करने के लिए आलसी मूल्यांकन का उपयोग करता है। मान के रूप में बाहर शुरू:

a = 0 : <thunk>
b = 1 : a

aऔर bदोनों स्वतंत्र रूप से मान्य सामान्य रूप हैं । प्रत्येक चर का निर्माण अन्य चर के अंतिम मूल्य की आवश्यकता के बिना किया जा सकता है। जब थंक का मूल्यांकन किया जाता है, तो यह उसी डेटा bबिंदुओं को इंगित करेगा।

इस प्रकार, यदि आप दो अपरिवर्तनीय मूल्यों को एक दूसरे को इंगित करना चाहते हैं, तो आपको दूसरा निर्माण करने के बाद पहले को अपडेट करना होगा, या ऐसा करने के लिए उच्च-स्तरीय तंत्र का उपयोग करना होगा।


आपके विशेष उदाहरण में, मैं इसे हास्केल में व्यक्त कर सकता हूं:

data Material = Water {temperature :: Double}
              | Ice   {temperature :: Double}

setTemperature :: Double -> Material -> Material
setTemperature newTemp (Water _) | newTemp <= 0.0 = Ice newTemp
                                 | otherwise      = Water newTemp
setTemperature newTemp (Ice _)   | newTemp >= 1.0 = Water newTemp
                                 | otherwise      = Ice newTemp

हालाँकि, मैं इस मुद्दे को साइड-स्टेप कर रहा हूँ। मुझे लगता है कि ऑब्जेक्ट-ओरिएंटेड दृष्टिकोण में, जहां एक setTemperatureविधि प्रत्येक Materialकंस्ट्रक्टर के परिणाम से जुड़ी होती है , आपको कंस्ट्रक्टर को एक-दूसरे के पास होना चाहिए। यदि कंस्ट्रक्टर्स को अपरिवर्तनीय मूल्यों के रूप में माना जाता है, तो आप ऊपर उल्लिखित दृष्टिकोण का उपयोग कर सकते हैं।


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

6

अपने उदाहरण में, आप किसी ऑब्जेक्ट में परिवर्तन लागू कर रहे हैं, इसलिए मैं एक ऐसी ApplyTransform()विधि की तरह कुछ का उपयोग करूंगा BlockBaseजो वर्तमान ऑब्जेक्ट को बदलने की कोशिश करने के बजाय वापस आती है ।

उदाहरण के लिए, कुछ हीट को लागू करके एक आइसब्लॉक को वॉटरब्लॉक में बदलने के लिए, मैं कुछ ऐसा कहूंगा

BlockBase currentBlock = new IceBlock();
currentBlock = currentBlock.ApplyTemperature(1); 
// currentBlock is now a WaterBlock 

और IceBlock.ApplyTemperature()विधि कुछ इस तरह दिखाई देगी:

public class IceBlock() : BlockBase
{
    public BlockBase ApplyTemperature(int temp)
    {
        return (temp > 0 ? new WaterBlock((BlockBase)this) : this);
    }
}

यह एक अच्छा जवाब है, लेकिन दुर्भाग्य से केवल इसलिए कि मैं यह उल्लेख करने में विफल रहा कि मेरे "सामग्री", वास्तव में मेरे "ब्लॉक", सभी सिंगलटन हैं, इसलिए नए वाटरब्लॉक () सिर्फ एक विकल्प नहीं है। यह अपरिवर्तनीय का मुख्य लाभ है, आप उन्हें असीम रूप से पुन: उपयोग कर सकते हैं। राम में 500,000 ब्लॉक होने के बजाय, मेरे पास 100 ब्लॉक के 500,000 संदर्भ हैं। बहुत सस्ता!
सेबेस्टियन डायट

तो BlockList.WaterBlockनया ब्लॉक बनाने के बजाय लौटने के बारे में क्या ?
राहेल

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

@ एसबेस्टियन मैं सोच रहा हूं कि BlockListसिर्फ एक staticवर्ग है जो प्रत्येक ब्लॉक के एकल उदाहरणों के लिए जिम्मेदार है, इसलिए आपको एक उदाहरण बनाने की आवश्यकता नहीं है BlockList(मैं C # का उपयोग कर रहा हूं)
राहेल

@ सेबेस्टियन: यदि आप सिंगेलटन का उपयोग करते हैं, तो आप कीमत चुकाते हैं।
डेडजैम

6

चक्र को तोड़ने का एक और तरीका सामग्री और प्रसारण की चिंताओं को अलग करना है, कुछ बनी भाषा में:

water = new Block("water");
ice = new Block("ice");

transitions = new Transitions([
    new transitions.temperature.Below(0.0, water, ice),
    new transitions.temperature.Above(0.0, ice, water),
]);

हुह, मेरे लिए शुरू में यह पढ़ना कठिन था, लेकिन मुझे लगता है कि यह अनिवार्य रूप से वही दृष्टिकोण है जिसकी मैंने वकालत की थी।
ऐदन कल्ली

1

यदि आप एक कार्यात्मक भाषा का उपयोग करने जा रहे हैं, और आप अपरिवर्तनीयता के लाभों को महसूस करना चाहते हैं, तो आपको उस समस्या को ध्यान में रखना चाहिए। आप एक वस्तु प्रकार "बर्फ" या "पानी" को परिभाषित करने का प्रयास कर रहे हैं, जो तापमान की एक सीमा का समर्थन कर सकता है - अपरिवर्तनीयता का समर्थन करने के लिए, आपको तब हर बार एक नई वस्तु बनाने की आवश्यकता होगी, जो तापमान में परिवर्तन हो, जो बेकार है। इसलिए ब्लॉक-प्रकार और तापमान की अवधारणाओं को अधिक स्वतंत्र बनाने की कोशिश करें। मुझे नहीं पता कि स्काला (यह मेरी सीखने की सूची :-)) पर है, लेकिन जोसे एडम्स के हास्केल में उत्तर से उधार ले रहा हूं, मैं कुछ सुझाव देता हूं:

data Material = Water | Ice

blockForTemperature :: Double -> Material
blockForTemperature x = 
  if x < 0 then Ice else Water

या हो सकता है:

transitionForTemperature :: Material -> Double -> Material
transitionForTemperature oldMaterial newTemp = 
  case (oldMaterial, newTemp) of
    (Ice, _) | newTemp > 0 -> Water
    (Water, _) | newTemp <= 0 -> Ice

(ध्यान दें: मैंने इसे चलाने की कोशिश नहीं की है, और मेरी हास्केल थोड़ी सी जंग खा रही है।) अब, संक्रमण तर्क को सामग्री के प्रकार से अलग किया जाता है, इसलिए यह अधिक मेमोरी बर्बाद नहीं करता है, और (मेरी राय में) यह काफी है थोड़ा और अधिक कार्यात्मक रूप से उन्मुख।


मैं वास्तव में "कार्यात्मक भाषा" का उपयोग करने की कोशिश नहीं कर रहा हूं क्योंकि मैं इसे प्राप्त नहीं करता हूं! केवल एक चीज जिसे मैं सामान्य रूप से किसी भी गैर-तुच्छ कार्यात्मक प्रोग्रामिंग उदाहरण से बनाए रखता हूं वह है: "अरे, मैं जो मैं अधिक चतुर था!" यह सिर्फ मेरे परे है कि यह किसी के लिए कैसे समझ में आता है। अपने छात्रों के दिनों से, मुझे याद है कि प्रोलॉग (तर्क-आधारित), ओकाम (सब कुछ-रन-इन-समानांतर-बाय-डिफ़ॉल्ट) और यहां तक ​​कि कोडांतरक भी समझ में आता है, लेकिन लिस्प केवल पागल सामान था। लेकिन मैं आपको उस कोड को स्थानांतरित करने के बारे में बताता हूं जो "राज्य" के बाहर "राज्य परिवर्तन" का कारण बनता है।
सेबस्टियन डायॉट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.