যখন বেশ কয়েকটি ক্লাসের একই ডেটা অ্যাক্সেস করার প্রয়োজন হয়, তখন ডেটা কোথায় ঘোষণা করা উচিত?


39

আমার সি ++ তে বেসিক 2 ডি টাওয়ার প্রতিরক্ষা গেম রয়েছে।

প্রতিটি মানচিত্র পৃথক শ্রেণি যা গেমস্টেট থেকে উত্তরাধিকার সূত্রে প্রাপ্ত। মানচিত্রটি গেমের প্রতিটি বস্তুর জন্য যুক্তি এবং অঙ্কন কোডকে প্রতিনিধিত্ব করে এবং মানচিত্রের পথের মতো ডেটা সেট করে। সিউডো-কোডে লজিক বিভাগটি এর মতো দেখতে পারে:

update():
  for each creep in creeps:
    creep.update()
  for each tower in towers:
    tower.update()
  for each missile in missiles:
    missile.update()

অবজেক্টস (ক্রাইপস, টাওয়ার এবং মিসাইল) ভেক্টর-অফ-পয়েন্টারে সংরক্ষণ করা হয়। নতুন ক্ষেপণাস্ত্র তৈরি করতে এবং লক্ষ্যগুলি সনাক্ত করতে টাওয়ারগুলির অবশ্যই ভেক্টর-অফ-ক্রাইপস এবং ভেক্টর অফ মিসাইলগুলিতে অ্যাক্সেস থাকতে হবে।

প্রশ্নটি হল: আমি ভেক্টরগুলি কোথায় ঘোষণা করব? তাদের কী মানচিত্র শ্রেণীর সদস্য হওয়া উচিত এবং টাওয়ার.আপডেট () ফাংশনে যুক্তি হিসাবে পাস করা উচিত? নাকি বিশ্বব্যাপী ঘোষণা? বা অন্য কোনও সমাধান রয়েছে যা আমি পুরোপুরি অনুপস্থিত?

যখন বেশ কয়েকটি ক্লাসের একই ডেটা অ্যাক্সেস করার প্রয়োজন হয়, তখন ডেটা কোথায় ঘোষণা করা উচিত?


1
গ্লোবাল সদস্যদের 'কুৎসিত' হিসাবে বিবেচনা করা হয় তবে দ্রুত এবং উন্নয়ন সহজ করে তোলে যদি এটি একটি ছোট খেলা হয় তবে এটি কোনও সমস্যা নয় (আইএমএইচও)। আপনি একটি বহিরাগত শ্রেণি তৈরি করতে পারেন যা যুক্তি পরিচালনা করে ( কেন টাওয়ারগুলিতে এই ভেক্টরগুলির প্রয়োজন) এবং সমস্ত ভেক্টরগুলিতে অ্যাক্সেস রয়েছে।
জোনাথন কনেল

-1 যদি এটি গেম প্রোগ্রামিংয়ের সাথে সম্পর্কিত হয়, তবে পিজ্জা খাওয়া খুব বেশি। নিজেকে কিছু ভাল সফ্টওয়্যার ডিজাইনের বই
গ্র্যাক করুন

9
@ মাইক: সফ্টওয়্যার ডিজাইন কীভাবে গেম প্রোগ্রামিংয়ের সাথে সম্পর্কিত নয়? এটি প্রোগ্রামিংয়ের অন্যান্য ক্ষেত্রেও প্রযোজ্য কারণ কেবল এটিকে অফ-টপিক তৈরি করে না।
ব্লুরাজা - ড্যানি পিফ্লুঘুফুট

সফ্টওয়্যার ডিজাইন নিদর্শনসমূহের ব্লুরাজা তালিকাগুলি এসও-তে আরও উপযুক্ত। জিডি.এসই গেম প্রোগ্রামিংয়ের জন্য, সফ্টওয়্যার ডিজাইনের জন্য নয়
মাইক সেমডার

উত্তর:


53

আপনার পুরো প্রোগ্রাম জুড়ে যখন আপনার কোনও ক্লাসের একক উদাহরণ প্রয়োজন, আমরা সেই শ্রেণিটিকে একটি পরিষেবা বলি । প্রোগ্রামগুলিতে পরিষেবাগুলি প্রয়োগের বিভিন্ন মানক পদ্ধতি রয়েছে:

  • গ্লোবাল ভেরিয়েবল । এগুলি প্রয়োগ করা সবচেয়ে সহজ, তবে সবচেয়ে খারাপ নকশা। আপনি যদি অনেকগুলি বিশ্বব্যাপী ভেরিয়েবল ব্যবহার করেন তবে আপনি দ্রুত নিজেকে মডিউলগুলি লিখতে পারবেন যা একে অপরের উপর খুব বেশি নির্ভর করে ( শক্ত-যুগল ), যুক্তির প্রবাহকে অনুসরণ করা খুব কঠিন করে তোলে। গ্লোবাল ভেরিয়েবলগুলি মাল্টিথ্রেডিং-বান্ধব নয়। গ্লোবাল ভেরিয়েবলগুলি বস্তুর আজীবন ট্র্যাকিংকে আরও কঠিন করে তোলে এবং নেমস্পেসকে বিশৃঙ্খলা করে। এগুলি তবে সর্বাধিক পারফরম্যান্ট বিকল্প, তাই এমন সময় রয়েছে যখন তারা ব্যবহার করতে পারে এবং ব্যবহার করা উচিত, তবে এগুলি অল্প পরিমাণে ব্যবহার করুন।
  • সিলেটলেটস । প্রায় 10-15 বছর আগে, সিলেটলেটগুলি জানার জন্য বড় ডিজাইন-প্যাটার্ন ছিল। যাইহোক, আজকাল তাদের দিকে তাকাতে হয়। এগুলি মাল্টি-থ্রেডে অনেক সহজ, তবে আপনাকে অবশ্যই তাদের ব্যবহার একবারে একটি থ্রেডের মধ্যে সীমাবদ্ধ করতে হবে যা আপনি যা চান তা সর্বদা নয়। লাইফটাইম ট্র্যাক করা বৈশ্বিক ভেরিয়েবলের মতোই কঠিন।
    একটি সাধারণ সিঙ্গলটন ক্লাস এর মতো দেখতে পাবেন:

    class MyClass
    {
    private:
        static MyClass* _instance;
        MyClass() {} //private constructor
    
    public:
        static MyClass* getInstance();
        void method();
    };
    
    ...
    
    MyClass* MyClass::_instance = NULL;
    MyClass* MyClass::getInstance()
    {
        if(_instance == NULL)
            _instance = new MyClass(); //Not thread-safe version
        return _instance;
    
        //Note that _instance is *never* deleted - 
        //it exists for the entire lifetime of the program!
    }
    
  • নির্ভরতা ইনজেকশন (ডিআই) । এটি কেবল কনস্ট্রাক্টর প্যারামিটার হিসাবে পরিষেবাটি পাস করার অর্থ। কোনও ক্লাসে পাস করার জন্য একটি পরিষেবা ইতিমধ্যে বিদ্যমান থাকতে হবে, সুতরাং দুটি পরিষেবা একে অপরের উপর নির্ভর করার উপায় নেই; 98% ক্ষেত্রে, আপনি এটি চান (এবং অন্যান্য 2% এর জন্য, আপনি সর্বদা একটি setWhatever()পদ্ধতি তৈরি করতে পারেন এবং পরে পরিষেবাতে পাস করতে পারেন ) । এ কারণে, ডিআই-র অন্যান্য বিকল্পগুলির মতো একই সংযোগের সমস্যা নেই। এটি মাল্টিথ্রেডিংয়ের সাথে ব্যবহার করা যেতে পারে, কারণ প্রতিটি থ্রেডে প্রতিটি পরিষেবায় কেবল নিজের নিজস্ব দৃষ্টান্ত থাকতে পারে (এবং কেবল এটির ভাগ করে নিতে হবে)। এটি কোডকে ইউনিট-টেস্টেবল করে তোলে, যদি আপনি সে সম্পর্কে যত্নবান হন।

    নির্ভরতা ইনজেকশন সহ সমস্যাটি এটি আরও বেশি স্মৃতি গ্রহণ করে; এখন কোনও শ্রেণীর প্রতিটি উদাহরণের জন্য এটি ব্যবহার করা প্রতিটি পরিষেবাদির উল্লেখ প্রয়োজন। এছাড়াও, যখন আপনার অনেকগুলি পরিষেবা থাকে এটি ব্যবহার করতে বিরক্ত হয়; এমন ফ্রেমওয়ার্ক রয়েছে যা অন্যান্য ভাষায় এই সমস্যাটি প্রশমিত করে, তবে সি ++ এর প্রতিবিম্বের অভাবের কারণে সি ++ এ ডিআই ফ্রেমওয়ার্কগুলি কেবল ম্যানুয়ালি না করেই আরও বেশি কাজ করার প্রবণতা রয়েছে।

    //Example of dependency injection
    class Tower
    {
    private:
        MissileCreationService* _missileCreator;
        CreepLocatorService* _creepLocator;
    public:
        Tower(MissileCreationService*, CreepLocatorService*);
    }
    
    //In order to create a tower, the creating-class must also have instances of
    // MissileCreationService and CreepLocatorService; thus, if we want to 
    // add a new service to the Tower constructor, we must add it to the
    // constructor of every class which creates a Tower as well!
    //This is not a problem in languages like C# and Java, where you can use
    // a framework to create an instance and inject automatically.
    

    দেখুন এই পৃষ্ঠার আরেকটি উদাহরণ জন্য (Ninject, একটি সি # দ্বি ফ্রেমওয়ার্ক জন্য নথিপত্র থেকে)।

    নির্ভরতা ইনজেকশন হ'ল এই সমস্যার স্বাভাবিক সমাধান এবং এটিই উত্তর যা আপনি স্ট্যাকওভারফ্লো.কম এ এই জাতীয় প্রশ্নগুলিতে সর্বাধিক উত্সাহিত দেখতে পাবেন। ডিআই হ'ল এক ধরণের ইনভার্শন অফ কন্ট্রোল (আইওসি)।

  • পরিষেবা লোকেটার । মূলত, কেবল একটি শ্রেণি যা প্রতিটি পরিষেবার একটি উদাহরণ রাখে। আপনি এটি প্রতিবিম্ব ব্যবহার করে করতে পারেন বা আপনি যখনই কোনও নতুন পরিষেবা তৈরি করতে চান প্রতিবার এটিতে একটি নতুন উদাহরণ যুক্ত করতে পারেন। আপনার এখনও আগের মতো একই সমস্যা রয়েছে - ক্লাসগুলি কীভাবে এই লোকেটারটি অ্যাক্সেস করতে পারে? - যা উপরের যে কোনও উপায়ে সমাধান করা যেতে পারে, তবে এখন আপনার ServiceLocatorকয়েক ডজন পরিষেবাদি না করে কেবল আপনার ক্লাসের জন্য এটি করা দরকার । আপনি যদি এই ধরণের জিনিসটির বিষয়ে চিন্তা করেন তবে এই পদ্ধতিটিও ইউনিট-টেস্টেবল।

    ইনভার্সন অফ কন্ট্রোল (আইওসি) এর অন্য রূপ হ'ল সার্ভিস লোকেটারগুলি। সাধারণত, ফ্রেমওয়ার্কগুলি যা স্বয়ংক্রিয়ভাবে নির্ভরতা ইনজেকশনটি করে সেগুলিতে একটি পরিষেবা লোকেটারও থাকবে।

    এক্সএনএ (মাইক্রোসফ্টের সি # গেম প্রোগ্রামিং ফ্রেমওয়ার্ক) এর মধ্যে একটি সার্ভিস লোকেটার অন্তর্ভুক্ত রয়েছে; এটি সম্পর্কে আরও জানতে, এই উত্তরটি দেখুন


যাইহোক, আইএমএইচও টাওয়ারগুলি লতানো সম্পর্কে জানা উচিত নয়। আপনি যদি প্রতিটি টাওয়ারের জন্য ক্রাইপগুলির তালিকাটি কেবল ফাঁক করে দেওয়ার পরিকল্পনা না করেন তবে আপনি সম্ভবত কিছুটা অযৌক্তিক স্পেস বিভাজন বাস্তবায়ন করতে চাইবেন ; এবং এই ধরণের যুক্তি টাওয়ার ক্লাসের অন্তর্ভুক্ত নয়।


মন্তব্যগুলি বর্ধিত আলোচনার জন্য নয়; এই কথোপকথন চ্যাটে সরানো হয়েছে ।
জোশ

আমি যা পড়েছি তার মধ্যে অন্যতম সেরা, পরিষ্কার উত্তর। সাবাশ. আমি ভেবেছিলাম কোনও পরিষেবা সর্বদা ভাগ করা উচিত।
নিকস

5

আমি ব্যক্তিগতভাবে এখানে পলিমারফিজম ব্যবহার করব। কেন একটি missileভেক্টর, একটি towerভেক্টর এবং একটি ভেক্টর creepরয়েছে..তারা যখন সবাই একই ফাংশন বলে; update? কিছু বেস ক্লাসে পয়েন্টারগুলির ভেক্টর Entityবা GameObjectকেন নেই?

আমি ডিজাইনের একটি ভাল উপায় খুঁজে পেয়েছি 'মালিকানার দিক থেকে এটি কী বোঝায়?' স্পষ্টতই একটি টাওয়ার নিজেকে আপডেট করার একটি উপায়ের মালিক, তবে কোনও মানচিত্র কী এতে সমস্ত বস্তুর মালিক? আপনি যদি বিশ্বব্যাপী যান, আপনি কি বলছেন যে টাওয়ার এবং ক্রিপসের কোনও কিছুই মালিক নয়? গ্লোবাল সাধারণত একটি খারাপ সমাধান - এটি খারাপ ডিজাইনের ধরণগুলি প্রচার করে, তবুও এটির সাথে কাজ করা আরও সহজ। ওজন বিবেচনা করুন 'আমি কি এটি শেষ করতে চাই?' এবং 'আমি কি এমন কিছু চাই যা আমি পুনরায় ব্যবহার করতে পারি?'

এর চারপাশের এক উপায় হ'ল মেসেজিং সিস্টেমের কিছু ফর্ম। towerএকটি বার্তা পাঠাতে পারে map(তার মালিকের কাছে তা অ্যাক্সেস আছে, সম্ভবত একটি রেফারেন্স?) যে এটি একটি আঘাত creep, এবং mapতারপর বলে creepএটি আঘাত হয়েছে। এটি খুব পরিষ্কার এবং ডেটা আলাদা করে।

আর একটি উপায় হ'ল ম্যাপটি যা চায় তা কেবল নিজেরাই অনুসন্ধান করা। তবে এখানে আপডেট অর্ডার নিয়ে সমস্যা থাকতে পারে।


1
পলিমারফিজম সম্পর্কে আপনার পরামর্শটি আসলেই প্রাসঙ্গিক নয়। আমি সেগুলি পৃথক ভেক্টরগুলিতে সংরক্ষণ করেছি যাতে আমি প্রতিটি ধরণের উপর পৃথকভাবে পুনরাবৃত্তি করতে পারি, যেমন অঙ্কন কোডে (যেখানে আমি প্রথমে আঁকা নির্দিষ্ট কিছু জিনিস চাইব) বা সংঘর্ষের কোডে।
রসালো

আমার উদ্দেশ্যে মানচিত্রটি সত্তাগুলির মালিক, কারণ এখানে মানচিত্রটি 'স্তর' এর সাথে সাদৃশ্যযুক্ত। আমি বার্তা সম্পর্কে আপনার ধারণা বিবেচনা করব, ধন্যবাদ।
রসালো

1
গেমের পারফরম্যান্সের ক্ষেত্রে। সুতরাং একই অবজেক্ট সময়ের ভেক্টরগুলির রেফারেন্সের ভাল লোকেশন থাকে। এছাড়াও, ভার্চুয়াল পয়েন্টারগুলির সাথে বহুতল বস্তুগুলির দুর্দান্ত কার্যকারিতা রয়েছে কারণ সেগুলি আপডেট লুপের সাথে inোকানো যায় না।
জ্যান লিংস

0

এটি এমন একটি ক্ষেত্রে যেখানে কঠোর অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং (ওওপি) ভেঙে যায়।

ওওপির নীতি অনুসারে আপনার ক্লাস ব্যবহার করে সম্পর্কিত আচরণের সাথে ডেটা গ্রুপ করা উচিত। তবে আপনার এমন আচরণ (টার্গেটিং) রয়েছে যা এমন ডেটার দরকার যা একে অপরের সাথে সম্পর্কিত নয় (টাওয়ার এবং লতা)। এই পরিস্থিতিতে, অনেক প্রোগ্রামাররা প্রয়োজনীয় তথ্যের অংশের সাথে আচরণটি সংযুক্ত করার চেষ্টা করবে (উদাহরণস্বরূপ টাওয়ারগুলি লক্ষ্যবস্তু পরিচালনা করে, তবে ক্রিপগুলি সম্পর্কে জানে না), তবে আরও একটি বিকল্প রয়েছে: ডেটাগুলির সাথে আচরণটি গোষ্ঠীভুক্ত করবেন না।

টার্গেটিং আচরণটি টাওয়ার বর্গের একটি পদ্ধতি তৈরির পরিবর্তে এটিকে একটি ফাংশন করুন যা টাওয়ার এবং ক্রাইপকে আর্গুমেন্ট হিসাবে গ্রহণ করে। এটির জন্য টাওয়ার এবং ক্রেপ ক্লাসগুলিতে প্রকাশিত সদস্যদের আরও বেশি তৈরি করার প্রয়োজন হতে পারে এবং এটি ঠিক আছে। ডেটা লুকানো দরকারী, তবে এটি একটি উপায়, নিজের মধ্যেই শেষ নয় এবং আপনার এটির গোলাম হওয়া উচিত নয়। তদ্ব্যতীত, ব্যক্তিগত সদস্যরা ডেটাতে অ্যাক্সেস নিয়ন্ত্রণের একমাত্র উপায় নয় - যদি ডেটা কোনও ফাংশনে প্রবেশ না করে এবং এটি বিশ্বব্যাপী না হয়, তবে কার্যকরভাবে সেই ফাংশন থেকে এটি লুকানো থাকে। যদি এই কৌশলটি ব্যবহার করা আপনাকে বৈশ্বিক ডেটা এড়াতে দেয় তবে আপনি সম্ভবত এনক্যাপসুলেশনটি উন্নত করতে পারেন ।

এই পদ্ধতির একটি চূড়ান্ত উদাহরণ হ'ল সত্তা সিস্টেমের আর্কিটেকচার।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.