জাভা সিরিয়ালাইজেশন: রিডবজেক্ট () বনাম রিড রিসলভ ()


127

কার্যকর জাভা এবং অন্যান্য উত্স বইটি সিরিয়ালাইজযোগ্য জাভা ক্লাসগুলির সাথে কাজ করার সময় রিডবজেক্ট () পদ্ধতিটি কখন এবং কখন ব্যবহার করতে হবে সে সম্পর্কে একটি দুর্দান্ত ব্যাখ্যা প্রদান করে। অন্যদিকে রিডরসলভ () পদ্ধতিটি কিছুটা রহস্য থেকে যায়। মূলত আমি যে সমস্ত নথি পেয়েছি সেগুলির মধ্যে দুটির মধ্যে কেবল একটির উল্লেখ আছে বা উভয়ই কেবল স্বতন্ত্রভাবে উল্লেখ করেছি।

উত্তর না থাকা প্রশ্নগুলি হ'ল:

  • দুটি পদ্ধতির মধ্যে পার্থক্য কী?
  • কখন কোন পদ্ধতি প্রয়োগ করা উচিত?
  • কীভাবে রিসার্ভ () ব্যবহার করা উচিত, বিশেষত কী ফিরিয়ে দেওয়ার ক্ষেত্রে?

আমি আশা করি আপনি এই বিষয়ে কিছুটা আলোকপাত করতে পারেন।


ওরাকলের String.CaseInsensitiveComparator.readResolve()
জেডিকে

উত্তর:


137

readResolveস্ট্রিম থেকে পঠিত বস্তুর প্রতিস্থাপনের জন্য ব্যবহৃত হয় । আমি এর জন্য কেবলমাত্র একমাত্র ব্যবহারই হ'ল সিঙ্গেলন প্রয়োগ করে; যখন কোনও অবজেক্ট পড়া হয়, তখন এটি সিঙ্গলটন দৃষ্টান্তের সাথে প্রতিস্থাপন করুন। এটি নিশ্চিত করে যে কেউ সিঙ্গলটোনকে সিরিয়ালাইজেশন এবং ডিসিজায়ালাইজেশন করে অন্য কোনও উদাহরণ তৈরি করতে পারে না।


3
দূষিত কোড (বা এমনকি ডেটা) এর কাছাকাছি আসার জন্য বেশ কয়েকটি উপায় রয়েছে।
টম হাটিন -

6
জোশ ব্লচ সেই শর্তগুলির বিষয়ে কথা বলেছেন যার অধীনে এটি কার্যকর জাভা 2 য় সংস্করণে ভেঙে যায়। আইটেম। 77. তিনি এই কথাবার্তার মধ্যে তিনি উল্লেখ করেছেন কয়েক বছর আগে গুগল আইও-তে (আলোচনার শেষের দিকে কিছু সময়): youtube.com/watch?v=pi_I7oD_uGI
Calvinkrishy

17
আমি এই উত্তরটি কিছুটা অপ্রতুল পাই, কারণ এতে transientক্ষেত্রের উল্লেখ নেই । readResolveবিষয়টি পড়ার পরে সমাধান করার জন্য ব্যবহৃত হয়। একটি উদাহরণ ব্যবহার সম্ভবত একটি বস্তু এমন কিছু ক্যাশে ধারণ করে যা বিদ্যমান ডেটা থেকে পুনরায় তৈরি করা যেতে পারে এবং ক্রমিকায়িত করার প্রয়োজন হয় না; ক্যাশেড ডেটা ঘোষিত হতে পারে transientএবং readResolve()ডিসিশ্রাইজেশনের পরে এটি পুনর্নির্মাণ করতে পারে । এই পদ্ধতির জন্য এই জাতীয় জিনিস।
জেসন সি 2

2
@ জেসনসি আপনার মন্তব্য যে "এই পদ্ধতির জন্য [ক্ষণস্থায়ী হ্যান্ডলিং] এর মতো বিষয়গুলি " বিভ্রান্তিকর। জাভা ডকটির জন্য দেখুন Serializable: এতে বলা হয়েছে যে "ক্লাসগুলির যখন স্ট্রিম থেকে কোনও উদাহরণ পড়তে হবে তখন প্রতিস্থাপনের জন্য এই বিভাগটির readResolveবিশেষ পদ্ধতি প্রয়োগ করা উচিত ..."।
ওফার

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

29

আইটেম 90, কার্যকর জাভা, 3 য় এড কভার readResolveএবং writeReplaceসিরিয়াল প্রক্সিগুলির জন্য - তাদের প্রধান ব্যবহার। উদাহরণগুলি লিখিত হয় না readObjectএবং writeObjectপদ্ধতিগুলি না কারণ তারা ক্ষেত্রগুলি পড়তে এবং লেখার জন্য ডিফল্ট সিরিয়ালাইজেশন ব্যবহার করে।

readResolvereadObjectফিরে আসার পরে বলা হয় (বিপরীতে writeReplaceআগে বলা হয় writeObjectএবং সম্ভবত অন্য কোনও বস্তুর উপরে)। পদ্ধতিটি যে thisবস্তুটি প্রত্যাবর্তন করে তা ব্যবহারকারীর কাছে প্রত্যাবর্তিত বস্তুর প্রতিস্থাপন ObjectInputStream.readObjectকরে এবং স্ট্রিমের অবজেক্টের আরও কোনও পিছনে রেফারেন্স দেয়। উভয় readResolveএবং writeReplaceএকই বা বিভিন্ন ধরণের অবজেক্টগুলি ফিরিয়ে দিতে পারে। একই ধরণের প্রত্যাবর্তন কিছু ক্ষেত্রে ক্ষেত্রে দরকারী যেখানে ক্ষেত্রগুলি অবশ্যই আবশ্যক finalএবং পিছনে সামঞ্জস্যতা প্রয়োজন বা মানগুলি অবশ্যই অনুলিপি করা উচিত এবং / অথবা যাচাই করা উচিত।

ব্যবহারের readResolveSingleton সম্পত্তি জোরদার করে না।


9

রিড রেজলভ রিডঅবজেক্ট পদ্ধতির মাধ্যমে ক্রমিকিত ডেটা পরিবর্তন করতে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, xstream API এই বৈশিষ্ট্যটি এমন কিছু বৈশিষ্ট্য আরম্ভ করার জন্য ব্যবহার করে যা ডিএমআরালাইজড করার জন্য এক্সএমএলে ছিল না।

http://x-stream.github.io/faq.html#Serialization


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

5
গৃহীত উত্তরে বলা হয়েছে যে কোনও বস্তুর প্রতিস্থাপনের জন্য রিডরসলভ ব্যবহার করা হয়। এই উত্তরটি দরকারী অতিরিক্ত তথ্য সরবরাহ করে যা এটি ডিসেরিয়ালাইজেশনের সময় কোনও বস্তু সংশোধন করতে ব্যবহৃত হতে পারে। এক্সস্ট্রিমটিকে উদাহরণ হিসাবে দেওয়া হয়েছিল, কেবলমাত্র সম্ভাব্য গ্রন্থাগার হিসাবে এটি ঘটে না।
Enwired

5

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


পড়ুন সমাধান () আমার কাছে স্পষ্ট ছিল তবে এখনও আমার মনে কিছু অব্যক্ত প্রশ্ন রয়েছে তবে আপনার উত্তরটি আমার মনকে কেবল পড়বে, ধন্যবাদ
রজনী গঙ্গোয়ার

5

রিডওবজেক্ট () অবজেক্টইনপ্রীষ্ট্রিম শ্রেণিতে একটি বিদ্যমান পদ্ধতি whileদিকে ডিসরিয়ালাইজেশন করার সময় পড়ার অবজেক্ট রিডঅবজেক্ট পদ্ধতি অভ্যন্তরীণভাবে পরীক্ষা করুন যে পাঠ্য পুনর্বিবেচনা পদ্ধতিটি রয়েছে এমন শ্রেণীর অবজেক্টটি রিসার্ভ পদ্ধতিতে রয়েছে কিনা তা যদি পুনরায় সমাধানের পদ্ধতিটি বিদ্যমান থাকে তবে এটি পুনরায় সমাধানের পদ্ধতিটি আহ্বান করবে এবং একইটি ফিরে আসবে same দৃষ্টান্ত.

তাই রিড রিলভ পদ্ধতি লেখার অভিপ্রায়টি খাঁটি সিঙ্গলটন ডিজাইন প্যাটার্ন অর্জনের জন্য একটি ভাল অনুশীলন যেখানে সিরিয়ালাইজেশন / ডিসস্রায়ালাইজেশন করে কেউ অন্য উদাহরণ পেতে পারে না।



2

সিরিয়ালাইজেশন যখন কোনও বস্তুকে রূপান্তর করতে ব্যবহার করা হয় যাতে এটি ফাইলে সংরক্ষণ করা যায়, আমরা একটি পদ্ধতি, রিডারলভ () পড়ুন ট্রিগার করতে পারি। পদ্ধতিটি ব্যক্তিগত এবং একই শ্রেণিতে রাখা হয় যার অবনতিকরণের সময় অবজেক্টটি পুনরুদ্ধার করা হচ্ছে। এটি নিশ্চিত করে যে deserialization পরে, কোন বস্তুটি প্রত্যাবর্তিত হয় সিরিয়ালাইজড হিসাবে একই ছিল। এটাই,instanceSer.hashCode() == instanceDeSer.hashCode()

readResolve () পদ্ধতিটি কোনও স্থির পদ্ধতি নয়। in.readObject()ডিসিরিয়ালাইজেশন করার পরে বলা হয় কেবল এটি নিশ্চিত করে যে প্রত্যাবর্তিত বস্তুটি নীচের সময় যেমন সিরিয়ালীকৃত হয়েছিল একইরকমout.writeObject(instanceSer)

..
    ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file1.ser"));
    out.writeObject(instanceSer);
    out.close();

এইভাবে, এটি সিঙ্গলটন ডিজাইন প্যাটার্ন বাস্তবায়নে সহায়তা করে, কারণ প্রতিবার একই উদাহরণ ফিরে আসে।

public static ABCSingleton getInstance(){
    return ABCSingleton.instance; //instance is static 
}

1

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

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

  1. সিরিয়ালযুক্ত অবজেক্টের যদি অপরিবর্তনীয় ক্ষেত্র থাকে যা কাস্টম সিরিয়ালাইজেশন প্রয়োজন, এর মূল সমাধানটি writeObject/readObjectঅপর্যাপ্ত, কারণ লিখিত স্ট্রিমের অংশটি পড়ার আগে ডিসরিয়ালাইজড অবজেক্ট তৈরি করা হয় writeObject। একটি লিঙ্কযুক্ত তালিকার এই সর্বনিম্ন বাস্তবায়ন নিন:

    public class List<E> extends Serializable {
        public final E head;
        public final List<E> tail;
    
        public List(E head, List<E> tail) {
            if (head==null)
                throw new IllegalArgumentException("null as a list element");
            this.head = head;
            this.tail = tail;
        }
    
        //methods follow...
    }

এই কাঠামোটি headপ্রতিটি লিঙ্কের ক্ষেত্রটি পুনরাবৃত্তভাবে লিখে একটি nullমান অনুসরণ করে ক্রমিক করা যেতে পারে । এই ধরণের ফর্ম্যাটটি deserial করা তবে অসম্ভব হয়ে ওঠে: readObjectসদস্য ক্ষেত্রের মান (এখন নির্ধারিত null) পরিবর্তন করতে পারে না । এখানে writeReplace/ readResolveজুটি আসুন :

private Object writeReplace() {
    return new Serializable() {
        private transient List<E> contents = List.this;

        private void writeObject(ObjectOutputStream oos) {
            List<E> list = contents;
            while (list!=null) {
                oos.writeObject(list.head);
                list = list.tail;
            }
            oos.writeObject(null);
        }

        private void readObject(ObjectInputStream ois) {
            List<E> tail = null;
            E head = ois.readObject();
            if (head!=null) {
                readObject(ois); //read the tail and assign it to this.contents
                this.contents = new List<>(head, this.contents)
            }                     
        }


        private Object readResolve() {
            return this.contents;
        }
    }
}

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

  1. আমরা যতটা লিখেছি তার চেয়ে আমরা আলাদা ক্লাসের কোনও অবজেক্টকে ডিসরিয়ালাইজ করতে পারি ObjectOutputStreamjava.util.Listতালিকার প্রয়োগের মতো মতামতগুলির ক্ষেত্রে এটি হবে যা দীর্ঘ থেকে কোনও স্লাইস প্রকাশ করে ArrayList। স্পষ্টতই, পুরো ব্যাকিং লিস্টকে সিরিয়ালাইজ করা একটি খারাপ ধারণা এবং আমাদের কেবল দেখা স্লাইস থেকে উপাদানগুলি লেখা উচিত। কেন তবে এটিকে থামান এবং ডিসিরিয়ালাইজেশন করার পরে অব্যর্থ স্তরের দিকনির্দেশনা রাখেন? আমরা কেবল স্ট্রিমের উপাদানগুলিকে একটিতে পড়তে ArrayListপারি এবং এটি আমাদের ভিউ ক্লাসে মোড়কের পরিবর্তে সরাসরি ফিরিয়ে দিতে পারি।

  2. বিকল্পভাবে, সিরিয়ালাইজেশনে উত্সর্গীকৃত অনুরূপ প্রতিনিধি শ্রেণি নকশা পছন্দ হতে পারে। একটি ভাল উদাহরণ হ'ল আমাদের সিরিয়ালাইজেশন কোডটি পুনরায় ব্যবহার করা। উদাহরণস্বরূপ, যদি আমাদের একটি বিল্ডার ক্লাস থাকে (স্ট্রিংয়ের জন্য স্ট্রিংবিল্ডারের অনুরূপ), আমরা সিরিয়ালাইজেশন ডেলিগেট লিখতে পারি যা কোনও সংগ্রহকে প্রবাহে খালি বিল্ডার লিখে সিরিয়ালাইজ করে, তারপরে সংগ্রহের আকার এবং উপাদানগুলির সংকলনের দ্বারা পুনরুদ্ধারকারী দ্বারা ফিরে আসে। ডেসারিয়ালাইজেশনটিতে বিল্ডারকে পড়া, পরবর্তীকালে সমস্ত পাঠক উপাদান যুক্ত করা এবং build()প্রতিনিধিদের কাছ থেকে ফাইনালের ফলাফল ফিরিয়ে দেওয়া অন্তর্ভুক্ত থাকে readResolve। সেক্ষেত্রে আমাদের কেবল সংগ্রহক্রমের শ্রেণিবিন্যাসের মূল শ্রেণিতেই সিরিয়ালকরণ বাস্তবায়ন করতে হবে এবং বর্তমান বা ভবিষ্যতের বাস্তবায়নগুলি থেকে কোনও অতিরিক্ত কোডের প্রয়োজন হবে না, যদি তারা বিমূর্ত প্রয়োগ করে iterator()এবংbuilder()পদ্ধতি (একই ধরণের সংগ্রহ পুনরুদ্ধার করার জন্য আধুনিক - যা নিজেই খুব কার্যকর বৈশিষ্ট্য হবে)। অন্য উদাহরণটি হ'ল আমরা কোন কোডটি পুরোপুরি নিয়ন্ত্রণ করি না - এমন একটি শ্রেণিবদ্ধ শ্রেণিবদ্ধতা রয়েছে - একটি তৃতীয় পক্ষের লাইব্রেরি থেকে আমাদের বেস শ্রেণি (এস) এমন অনেকগুলি ব্যক্তিগত ক্ষেত্র থাকতে পারে যার সম্পর্কে আমরা কিছুই জানিনা এবং যা একটি সংস্করণ থেকে অন্য সংস্করণে পরিবর্তিত হতে পারে আমাদের সিরিয়ালযুক্ত বস্তু সেক্ষেত্রে ডিজিটালাইজেশনে ডেটা লিখতে এবং বস্তুটিকে ম্যানুয়ালি পুনর্নির্মাণ করা নিরাপদ হবে।


0

পড়ার সমাধানের পদ্ধতি

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

যে কোনও অ্যাক্সেস-মোডিফায়ার অবজেক্ট রিড রিসলভ () থ্রো করে অবজেক্টস্ট্রিমএক্সসেপশন;

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

উদাহরণস্বরূপ, একটি প্রতীক শ্রেণি তৈরি করা যেতে পারে যার জন্য প্রতিটি ভার্চুয়াল মেশিনের মধ্যে প্রতিটি প্রতীক বাঁধাইয়ের একমাত্র উদাহরণ বিদ্যমান। ReadResolve পদ্ধতি যদি এই চিহ্নের ইতিমধ্যে সংজ্ঞায়িত করা হয় নির্ধারণ preexisting সমতুল্য সাংকেতিক বস্তুর প্রতিস্থাপন পরিচয় বাধ্যতা বজায় রাখার জন্য বাস্তবায়িত করা হবে। এইভাবে সিরিয়ালকরণ জুড়ে প্রতীক বস্তুর স্বতন্ত্রতা বজায় রাখা যায়।


0

ইতিমধ্যে উত্তর হিসাবে, readResolveঅবজেক্ট ইনপুটস্ট্রিমে কোনও ব্যক্তিগত পদ্ধতি ব্যবহৃত হয় যখন কোনও অবজেক্টের ডিসায়রিজ করা হয়। এটিকে প্রকৃত উদাহরণ ফিরে আসার ঠিক আগে বলা হয়। সিঙ্গেলনের ক্ষেত্রে, আমরা ইতিমধ্যে ডিসরিয়ালাইজড উদাহরণের রেফারেন্সের পরিবর্তে ইতিমধ্যে বিদ্যমান সিঙ্গলটন দৃষ্টান্ত রেফারেন্সটিকে জোর করতে পারি। Similary আমরা আছে writeReplaceObjectOutputStream জন্য।

উদাহরণস্বরূপ readResolve:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SingletonWithSerializable implements Serializable {
private static final long serialVersionUID = 1L;

public static final SingletonWithSerializable INSTANCE = new SingletonWithSerializable();

private SingletonWithSerializable() {
    if (INSTANCE != null)
        throw new RuntimeException("Singleton instance already exists!");
}

private Object readResolve() {
    return INSTANCE;
}

public void leaveTheBuilding() {
    System.out.println("SingletonWithPublicFinalField.leaveTheBuilding() called...");
}

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    SingletonWithSerializable instance = SingletonWithSerializable.INSTANCE;

    System.out.println("Before serialization: " + instance);

    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
        out.writeObject(instance);
    }

    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
        SingletonWithSerializable readObject = (SingletonWithSerializable) in.readObject();
        System.out.println("After deserialization: " + readObject);
    }

}

}

আউটপুট:

Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.