ठीक है, ऐसा लगता है कि आपके शब्दार्थ डोमेन का IS-A संबंध है, लेकिन आप इसे मॉडल करने के लिए उपप्रकार / वंशानुक्रम का उपयोग करने से थोड़ा सावधान हैं - विशेष रूप से रनटाइम प्रकार प्रतिबिंब के कारण। हालांकि मुझे लगता है कि आप गलत चीज़ से डर गए हैं- सबटाइपिंग वास्तव में खतरों के साथ आती है, लेकिन यह तथ्य कि आप रनटाइम पर किसी ऑब्जेक्ट को क्वेरी कर रहे हैं समस्या नहीं है। आप देखेंगे कि मेरा क्या मतलब है।
ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग आईएस-ए रिश्तों की धारणा पर बहुत अधिक झुक गई है, इसने यकीनन इस पर बहुत अधिक झुकाव किया है, जिससे दो प्रसिद्ध महत्वपूर्ण अवधारणाएं पैदा हुई हैं:
लेकिन मुझे लगता है कि आईएस-ए रिश्तों को देखने के लिए एक और अधिक कार्यात्मक-प्रोग्रामिंग-आधारित तरीका है जो शायद इन कठिनाइयों को नहीं करता है। सबसे पहले, हम अपने कार्यक्रम में घोड़ों और गेंडा को मॉडल करना चाहते हैं, इसलिए हम Horseएक Unicornप्रकार और एक प्रकार का होने जा रहे हैं । इन प्रकारों के मूल्य क्या हैं? खैर, मैं यह कहूँगा:
- इन प्रकारों के मूल्य घोड़ों और इकसिंगों (क्रमशः) के प्रतिनिधित्व या विवरण हैं;
- वे कर रहे हैं schematized , वे बहुत सख्त नियमों के अनुसार साथ बनाया कर रहे हैं प्रतिनिधित्व या वर्णन-रूप में नज़र आते नहीं मुक्त रूप।
यह स्पष्ट लग सकता है, लेकिन मुझे लगता है कि लोगों द्वारा सर्कल-एलिप्से समस्या जैसे मुद्दों में से एक को ध्यान में रखकर उन बिंदुओं पर ध्यान नहीं दिया गया है। हर वृत्त एक दीर्घवृत्त है, लेकिन इसका मतलब यह नहीं है कि किसी चक्र का प्रत्येक योजनाबद्ध विवरण स्वचालित रूप से एक अलग स्कीमा के अनुसार दीर्घवृत्त का एक योजनाबद्ध विवरण है। दूसरे शब्दों में, सिर्फ इसलिए कि एक वृत्त एक दीर्घवृत्त है इसका मतलब यह नहीं है कि एक एक Circleहै Ellipse, इसलिए बोलने के लिए। लेकिन इसका मतलब यह है कि:
- एक कुल फ़ंक्शन होता है जो किसी भी
Circle(योजनाबद्ध सर्कल विवरण) को एक Ellipse(विभिन्न प्रकार के विवरण) में परिवर्तित करता है जो एक ही सर्कल का वर्णन करता है;
- एक आंशिक फ़ंक्शन होता है
Ellipse, जो एक सर्कल का वर्णन करता है, और इसी रिटर्न देता है Circle।
इसलिए, कार्यात्मक प्रोग्रामिंग शब्दों में, आपके Unicornप्रकार को बिल्कुल उप-प्रकार होने की आवश्यकता नहीं है Horse, आपको बस इन जैसे संचालन की आवश्यकता है:
-- Convert any unicorn-description of into a horse-description that
-- describes the same unicorns.
toHorse :: Unicorn -> Horse
-- If the horse described by the given horse-description is a unicorn,
-- then return a unicorn-description of that unicorn, otherwise return
-- nothing.
toUnicorn :: Horse -> Maybe Unicorn
और इसके toUnicornठीक विपरीत होना चाहिए toHorse:
toUnicorn (toHorse x) = Just x
हास्केल का Maybeप्रकार वह है जिसे अन्य भाषाएं "विकल्प" प्रकार कहती हैं। उदाहरण के लिए, जावा 8 Optional<Unicorn>प्रकार या तो Unicornकुछ भी नहीं है। ध्यान दें कि आपके दो विकल्प-एक अपवाद को फेंकने या "डिफ़ॉल्ट या जादू मूल्य" वापस करने के लिए - विकल्प के प्रकारों के समान।
इसलिए मूल रूप से जो मैंने यहां किया है वह अवधारणा आईएस-ए संबंध को प्रकारों और कार्यों के संदर्भ में फिर से संगठित करता है, बिना उपप्रकार या विरासत के उपयोग के। इससे मुझे क्या लेना देना है:
- आपके मॉडल को एक
Horseप्रकार का होना चाहिए;
- इस
Horseप्रकार को स्पष्ट रूप से निर्धारित करने के लिए पर्याप्त जानकारी को एन्कोड करने की आवश्यकता है कि क्या कोई मूल्य एक गेंडा का वर्णन करता है;
Horseप्रकार के कुछ संचालन को उस जानकारी को उजागर करने की आवश्यकता होती है ताकि प्रकार के ग्राहक यह देख सकें कि क्या दिया Horseएक गेंडा है;
- प्रकार के ग्राहकों को
Horseइन बाद के ऑपरेशनों का उपयोग रनवे पर यूनिकॉर्न और घोड़ों के बीच भेदभाव करने के लिए करना होगा।
तो यह मौलिक रूप से "प्रत्येक से पूछें Horseकि क्या यह एक गेंडा" मॉडल है। आप उस मॉडल से सावधान हैं, लेकिन मुझे गलत लगता है। यदि मैं आपको s की एक सूची देता हूं Horse, तो सभी प्रकार की गारंटी यह है कि सूची में बताई गई चीजें घोड़े हैं - इसलिए आप अनिवार्य रूप से, यह बताने के लिए रनटाइम पर कुछ करने की जरूरत है कि उनमें से कौन सी इकाइयां हैं। इसलिए ऐसा कुछ भी नहीं हो रहा है, मुझे लगता है - आपको उन कार्यों को लागू करने की आवश्यकता है जो आपके लिए ऐसा करेंगे।
ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में, ऐसा करने का परिचित तरीका निम्नलिखित है:
- एक
Horseप्रकार है;
- के
Unicornउपप्रकार के रूप में है Horse;
- क्लाइंट-सुलभ ऑपरेशन के रूप में रनटाइम टाइप रिफ्लेक्शन का उपयोग करें जो यह बताता है कि क्या कोई दिया गया
Horseहै Unicorn।
इसकी एक बड़ी कमजोरी है, जब आप इसे "चीज़ बनाम विवरण" कोण से देखते हैं जो मैंने ऊपर प्रस्तुत किया है:
- क्या होगा अगर आपके पास एक
Horseउदाहरण है जो एक गेंडा का वर्णन करता है लेकिन एक Unicornउदाहरण नहीं है ?
शुरुआत में वापस जाना, यह वही है जो मुझे लगता है कि इस आईएस-ए रिश्ते को मॉडलिंग करने के लिए घटाव और डाउनकास्ट का उपयोग करने के बारे में वास्तव में डरावना हिस्सा है - इस तथ्य से नहीं कि आपको एक रनटाइम चेक करना है। टाइपोग्राफी को थोड़ा गाली देना, Horseयह पूछना कि क्या यह एक Unicornउदाहरण है, यह पूछने का पर्याय नहीं है Horseकि क्या यह एक गेंडा है (चाहे यह Horseएक घोड़े का प्रतिलेखन है जो एक गेंडा भी है)। नहीं जब तक कि आपका प्रोग्राम कोड बनाने के लिए बहुत बड़ी लंबाई तक नहीं गया है जो कि निर्माण करता है Horsesताकि हर बार एक ग्राहक Horseजो एक गेंडा का वर्णन करने के लिए निर्माण करने की कोशिश करता है , तो Unicornक्लास का त्वरित मूल्यांकन किया जाता है। मेरे अनुभव में, शायद ही कभी प्रोग्रामर चीजों को ध्यान से करते हैं।
तो मैं उस दृष्टिकोण के साथ जाऊँगा जहाँ एक स्पष्ट, गैर-डाउनकास्ट ऑपरेशन है जो Horseएस को Unicornएस में परिवर्तित करता है । यह या तो Horseप्रकार की एक विधि हो सकती है :
interface Horse {
// ...
Optional<Unicorn> toUnicorn();
}
... या यह एक बाहरी वस्तु हो सकती है (आपके "घोड़े पर अलग वस्तु जो आपको बताती है कि घोड़ा एक गेंडा है या नहीं"):
class HorseToUnicornCoercion {
Optional<Unicorn> convert(Horse horse) {
// ...
}
}
इनमें से चुनाव इस बात का है कि आपका कार्यक्रम कैसे आयोजित किया जाता है - दोनों ही मामलों में, आपके पास Horse -> Maybe Unicornऊपर से मेरे ऑपरेशन के बराबर है , आप इसे अलग-अलग तरीकों से पैकेजिंग कर रहे हैं (यह माना जाएगा कि किस Horseप्रकार के ऑपरेशन के लिए आवश्यक रूप से लहर के प्रभाव होंगे अपने ग्राहकों को उजागर करने के लिए)।