2008-12-21 11 views
6

मेरे पास Rock, Paper, और Scissors हैं। ये रॉक, पेपर, कैंची गेम के घटक, या "हाथ" हैं। दो खिलाड़ियों के हाथों को देखते हुए गेम को यह तय करना होगा कि कौन जीतता है। मैं एक दूसरे के लिए विभिन्न हाथ युग्मन के बिना इस श्रृंखला चार्टएक दूसरे के साथ मजबूत वैचारिक बंधन वाले वर्गों से युग्मन को खत्म करना

Rock, Paper, Scissors chart

भंडारण की समस्या कैसे हल करते हैं? लक्ष्य किसी अन्य व्यक्ति को बदलने के बिना खेल के लिए एक नया हाथ जोड़ने के लिए (उदाहरण के लिए जॉन स्कीट) को अनुमति देना है।

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

यह एक डिजाइन 101 समस्या का प्रकार है, लेकिन मैं उत्सुक हूं कि इसके लिए लोग किस समाधान के साथ आ सकते हैं। जाहिर है, यह समस्या आसानी से बहुत अधिक प्रणालियों के साथ स्केल कर सकती है जिसमें कई और घटकों के साथ किसी भी मनमाने ढंग से जटिल संबंध हैं। यही कारण है कि मैं हल करने के लिए एक बहुत ही सरल और ठोस उदाहरण बिछा रहा हूँ। किसी भी प्रतिमान का उपयोग किया जाता है, ओओपी या अन्यथा, स्वागत है।

+0

कभी-कभी, प्रश्न * भी * जेनेरिक हो सकते हैं। –

+0

हां। यही कारण है कि मैंने एक बहुत ही ठोस उदाहरण रखा। आइए बस इस उदाहरण को हल करें, और मुझे कल्पना है कि हम समाधान को सामान्यीकृत करने में सक्षम होंगे। – wilhelmtell

+0

समस्या यह है कि किसी दिए गए उदाहरण के लिए कई "समाधान" हैं, और आप हमेशा सामान्यीकृत नहीं कर सकते हैं। हो सकता है कि यदि आपके पास कई अलग-अलग उदाहरणों का एक सेट था, तो सामान्यीकृत समाधान (वास्तव में केवल एक डिज़ाइन पैटर्न) को देखना उचित होगा – Tom

उत्तर

4

एक गेमस्ट्रेटी क्लास है जो एक विन विधि लागू करती है। जीत विधि हाथों की एक सूची लेती है, और या तो हाथ लौटाती है - यदि कोई विजेता है - या शून्य अगर गेम टाई था। मुझे लगता है कि जीतने की रणनीति वास्तव में हाथ की संपत्ति नहीं है, बल्कि खेल की है। GameStrategy कक्षा में हाथों की एक जोड़ी के विजेता के दृढ़ संकल्प को शामिल करें।

संपादित: संभावित रणनीति

public enum RPSEnum { Rock, Paper, Scissors } 

private RPSEnum FirtRPS = RPSEnum.Rock; 
private RPSEnum LastRPS = RPSEnum.Scissors; 

public Hand Win(Hand firstPlayer, Hand secondPlayer) 
{ 
    if (firstPlayer.Value == FirstRPS 
     && secondPlayer.Value == LastRPS) 
    { 
     return firstPlayer; 
    } 
    else if (secondPlayer.Value == FirstRPS 
      && firstPlayer.Value == LastRPS) 
     return secondPlayer; 
    } 
    else 
    { 
     int compare = (int)firstPlayer.Value - (int)secondPlayer.Value; 
     if (compare > 0) 
     { 
      return firstPlayer; 
     } 
     else if (compare < 0) 
     { 
      return secondPlayer; 
     } 
     else 
     { 
      return null; 
     }  
    } 
} 

एक नया हाथ मूल्य जोड़ने के लिए, बस उचित अनुक्रम में RPSEnum को जोड़ने के मूल्य। यदि यह नया "सबसे निचला" हाथ है, तो फर्स्टआरपीएस अपडेट करें। यदि यह नया "उच्चतम" हाथ है, तो LastRPS अपडेट करें। आपको वास्तविक एल्गोरिदम को बिल्कुल बदलने की आवश्यकता नहीं है।

नोट: यह केवल तीन मानों के लिए होने की तुलना में अधिक जटिल है, लेकिन आवश्यकता अतिरिक्त कोड अपडेट किए बिना अतिरिक्त मूल्यों को जोड़ा जा सकता है।

+0

तो आप रणनीति को कैसे कार्यान्वित करेंगे? क्या आपके पास हाथों के प्रत्येक संभावित मैच के लिए एक प्राप्ति के साथ एक रणनीति पदानुक्रम होगा? हाथों को जोड़ने के रूप में यह पदानुक्रम तेजी से बढ़ेगा। – wilhelmtell

+0

क्या आप मैट्रिक्स की तरह कुछ या रणनीति में स्विच चाहते हैं? यह एक उचित समाधान है। मैं मूल रूप से सोच रहा था कि अगर मैं क्लाइंट कोड को अच्छे लगने के लिए "परिणाम = hand1.fight (hand2)" जैसा कुछ कह सकता हूं। – wilhelmtell

+0

मुझे लगता है कि हाथों का आदेश दिया गया है और एनम का मूल्य ऑर्डर प्रदान करता है। आप सिर्फ enum के मूल्यों की तुलना करेंगे। एकमात्र विशेष मामला आपको जांचना होगा कि पहला हाथ आखिरी हाथ धड़कता है - अन्य सभी को int को काटने और घटाव का उपयोग करके किया जा सकता है। – tvanfosson

2

यदि उनके पास पर्याप्त वैचारिक समानता है, तो आप युग्मन को कम करने से खुद को दस्तक नहीं देना चाहेंगे।

"युग्मन" वास्तव में एक मीट्रिक है कि एक चीज का आंतरिक कार्यान्वयन बदल दिया गया है तो कोड टूटना कितना होगा। यदि इन चीजों का आंतरिक कार्यान्वयन दूसरों के लिए स्वाभाविक रूप से संवेदनशील है, तो यह है; युग्मन कम करना ठीक है, लेकिन सॉफ्टवेयर, सबसे पहले, वास्तविकता को प्रतिबिंबित करना चाहिए।

1

मुझे नहीं लगता कि विभिन्न हाथ अलग-अलग प्रकार हैं: वे एक ही प्रकार के अलग-अलग उदाहरण हैं। उस प्रकार के नाम, संभवतः चित्र, आदि जैसे गुण हैं

आप लोडिंग, डेटा से, हाथों की सूची और एक मैट्रिक्स द्वारा गेम शुरू करते हैं जो हाथ प्रत्येक हाथ को धड़कता है। शायद डेटा को एक तुलना विधि के साथ गेम क्लास में लोड किया जाएगा।

0

इस मामले में, महत्वपूर्ण बात यह है कि अगर आप दो वस्तुओं तुलना, तुम वापस एक परिणाम मिलता है।

तो मूल रूप से, आपको इस तुलना को इस तरह से कम करने की आवश्यकता है कि आप न केवल मिश्रण में एक नया प्रकार का ऑब्जेक्ट जोड़ सकते हैं, बल्कि मिश्रण में तुलना नियम भी जोड़ सकते हैं जो इस ऑब्जेक्ट को संभाल सकता है।

अब, मैं भी कह रही है कि "कभी कभी, सवालों भी सामान्य हो सकता है" आपके प्रश्न पर टिप्पणी, और समस्या है कि यहाँ कोई कितना भी मैं आपको बता कैसे करना है क्या आप के बारे पूछना है, यह है वास्तविक समस्या के बारे में आपको थोड़ा सा मदद नहीं करेगा।

जब तक आप एक रॉक-पेपर-कैंची-एक्स गेम नहीं बना रहे हैं।

व्यक्तिगत रूप से मैं अपने स्वयं के आईओसी कंटेनर के साथ निम्नलिखित कार्य करता हूं।

ServiceContainer.Global.RegisterFactory<IHandType>() 
    .FromDelegate(() => RandomRockScissorPaper()); 
ServiceContainer.Global.RegisterFactory<IHandComparison, DefaultHandComparison>(); 

(या बल्कि, मैं app.config फ़ाइल में ऊपर या इसी तरह इतना है कि यह बाद परियोजना बनाया गया है बदला जा सकता है कॉन्फ़िगर होगा)।

फिर, यदि किसी अन्य प्रकार को जोड़ने के लिए उपयोगकर्ता/ग्राहक/अंत-बिंदु को ओवरराइड करने की आवश्यकता है, तो मैं रजिस्ट्रेशन को निम्नानुसार बढ़ा दूंगा (याद रखें कि उपरोक्त app.config में हैं, इसलिए मैं उन लोगों को प्रतिस्थापित कर दूंगा नीचे):

ServiceContainer.Global.RegisterFactory<IHandType>() 
    .FromDelegate(() => RandomRockScissorPaper()) 
    .ForPolicy("original"); 
ServiceContainer.Global.RegisterFactory<IHandType>() 
    .FromDelegate((IHandType original) => RandomRockScissorPaperBlubb(original)) 
    .WithParameters(
     new Parameter<IHandType>("original").WithPolicy("original")) 
    .ForPolicy("new") 
    .AsDefaultPolicy(); 

यहाँ मैं IHandType को हल करने का एक नया तरीका जोड़ने के लिए, केवल मूल रास्ता रखते हुए नहीं है, लेकिन साथ ही एक नया जोड़कर। इस नए व्यक्ति को पुराने को कॉल करने का नतीजा दिया जाएगा, और फिर आंतरिक रूप से यह तय करना होगा कि यादृच्छिक मौका चौथा प्रकार, या मूल प्रकार (तीन मूल में से एक) को वापस कर देना चाहिए या नहीं।

मैं तो भी मूल तुलना नियम को ओवरराइड होगा: है लागू करने के लिए कैसे

IHandType hand1 = ServiceContainer.Global.Resolve<IHandType>(); 
IHandType hand2 = ServiceContainer.Global.Resolve<IHandType>(); 
IHandComparison comparison = ServiceContainer.Global.Resolve<IHandComparison>(); 
if (comparison.Compare(hand1, hand2) < 0) 
    Console.Out.WriteLine("hand 1 wins"); 
else if (comparison.Compare(hand1, hand2) > 0) 
    Console.Out.WriteLine("hand 1 wins"); 
else 
    Console.Out.WriteLine("equal"); 

यहाँ:

public interface IHandComparison 
{ 
    Int32 Compare(IHandType hand1, IHandType hand2); 
} 
public class DefaultHandComparison : IHandComparison 
{ 
    public Int32 Compare(IHandType hand1, IHandType hand2) 
    { 
     ... normal rules here 
    } 
} 
public class NewHandComparison : IHandComparison 
{ 
    private IHandComparison _Original; 
    public NewHandComparison(IHandComparison original) 
    { 
     _Original = original; 
    } 
    public Int32 Compare(IHandType hand1, IHandType hand2) 
    { 
     if hand1 is blubb or hand2 is blubb then ... 
     else 
      return _Original.Compare(hand1, hand2); 
    } 
} 

सब लिखने के बाद

ServiceContainer.Global.RegisterFactory<IHandComparison, DefaultHandComparison>() 
    .ForPolicy("original"); 
ServiceContainer.Global.RegisterFactory<IHandComparison, NewHandComparison>() 
    .ForPolicy("new") 
    .AsDefaultPolicy() 
    .WithParameters(
     new Parameter<IHandType>("original")); 

यह ऐसे इस्तेमाल किया जाएगा यह मुझे एहसास है कि मेरा ऐप.कॉन्फिग कॉन्फ़िगरेशन प्रतिनिधियों को संभालने में सक्षम नहीं होगा, इसलिए फैक्ट्री ऑब्जेक्ट डब्ल्यू इसकी आवश्यकता हो सकती है, लेकिन यह अभी भी लागू होता है।

आपको दोनों नए हाथों को प्राप्त करने के तरीके के साथ-साथ जीतने वाले हाथों के नियमों को हल करने के तरीके को हल करने में सक्षम होना चाहिए।

0

क्यों decouple? ये तत्व सभी आंतरिक रूप से एक-दूसरे से जुड़े हुए हैं, एक नया हाथ इसे बदल नहीं देगा।

बस रॉक, पेपर और कैंची द्वारा विस्तारित एक बेस हैंड क्लास है। बेस क्लास को एक .beatenby विशेषता दें जो हाथ के अन्य वर्गों में से एक लेता है। यदि आप ऐसी स्थिति से गुजरते हैं जहां एक हाथ से अधिक हाथ से पीटा जा सकता है, तो बस .beatenby विशेषता इसके बजाय एक सरणी लें।

0

कई अन्य उत्तरों ने बहुत ही लचीला और बहुत decoupled समाधान प्रदान किए हैं ... और वे रास्ता होने की अपेक्षा अधिक जटिल हैं। Decoupling का संक्षिप्त उत्तर double dispatch है, जो बहुत कम भाषाएं मूल रूप से समर्थन करते हैं। गोफ विज़िटर पैटर्न एकल प्रेषण का समर्थन करने वाली भाषाओं में डबल प्रेषण अनुकरण करने के लिए मौजूद है (कोई भी ओओ भाषा इसका समर्थन करता है, यहां तक ​​कि फ़ंक्शन पॉइंटर्स के साथ भी सी)।

यहां एक ही भावना में थोड़ा बड़ा उदाहरण दिया गया है। मान लें कि आप HTTP यातायात की निगरानी कर रहे हैं, और आप अनुरोध विधि और प्रतिक्रिया कोड के संयोजन के आधार पर पैटर्न वर्गीकृत करने की कोशिश कर रहे हैं। इसके बजाय इस श्रृंखला को देखने का:

rock/paper 
paper/scissors 
paper/paper 
... 

... आप इस श्रृंखला पर देख रहे हैं:

GET/200 
GET/304 
POST/401 
POST/200 
... 

तो प्रणाली HttpRequest और HttpResponse वस्तुओं था, प्रेषण करने के लिए सबसे आसान तरीका उपयोग करने के लिए किया जाएगा एक भी समारोह जो सभी संभावित विकल्पों के लिए मार्ग बनाता है:

HttpRequest req; 
HttpResponse resp; 
switch ((req.method, resp.code)) { 
    case (GET, 200): return handleGET_OK(req, resp); 
    case (GET, 304); return handleGET_NotModified(req, resp); 
    case (POST, 404): return handlePOST_NotFound(req, resp); 
    ... 
    default: print "Unhandled combination"; break; 
} 

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

संबंधित मुद्दे