কোটলিনে বিল্ডার প্যাটার্ন কীভাবে প্রয়োগ করা যায়?


144

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

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

২ য় আপডেট: প্রশ্নটি কীভাবে কোটলিনে কিছু পরামিতি সহ সাধারণ পোজোর জন্য একটি বিল্ডার ডিজাইন-প্যাটার্ন লিখবেন? নীচের কোডটি জাভা কোড লিখে এবং তারপরে কোটলিনে রূপান্তর করতে গ্রহ-কোটলিন-প্লাগইন ব্যবহার করে আমার প্রয়াস।

class Car private constructor(builder:Car.Builder) {
    var model:String? = null
    var year:Int = 0
    init {
        this.model = builder.model
        this.year = builder.year
    }
    companion object Builder {
        var model:String? = null
        private set

        var year:Int = 0
        private set

        fun model(model:String):Builder {
            this.model = model
            return this
        }
        fun year(year:Int):Builder {
            this.year = year
            return this
        }
        fun build():Car {
            val car = Car(this)
            return car
        }
    }
}

1
আপনার কি দরকার modelএবং yearপরিবর্তনীয় হতে হবে? আপনি কি কোনও Carসৃষ্টির পরে এগুলি পরিবর্তন করেন ?
ভোটদান

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

1
আপনি নিজের জন্য বিল্ডার ক্লাসটি স্বয়ংক্রিয়ভাবে উত্পন্ন করতে এই github.com/jffiorillo/jvmbuilder এ্যানোটেশন প্রসেসরটিও ব্যবহার করতে পারেন।
জোসেফ

@ জোসেফ এটি স্ট্যান্ডার্ড কোটলিনে যুক্ত করার জন্য ভাল ধারণা। এটি কোটলিনে লিখিত পাঠাগারগুলির জন্য দরকারী is
কিহান

উত্তর:


271

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

class Car(val model: String? = null, val year: Int = 0)

এবং এটি এর মতো ব্যবহার করুন:

val car = Car(model = "X")

আপনি যদি পুরোপুরি বিল্ডারদের ব্যবহার করতে চান তবে আপনি এটি কীভাবে করতে পারেন তা এখানে:

বিল্ডারকে একটি companion objectকরে তোলা অর্থবোধ করে না কারণ objectএটি সিঙ্গেলন। পরিবর্তে এটিকে নেস্টেড শ্রেণি হিসাবে ঘোষণা করুন (যা কোটলিনে ডিফল্টরূপে স্থির)।

বৈশিষ্ট্যগুলি কনস্ট্রাক্টরে সরান যাতে অবজেক্টটিও নিয়মিত উপায়ে ইনস্ট্যান্ট করা যায় (কনস্ট্রাক্টরকে এটি করা উচিত না হলে প্রাইভেট করুন) এবং একটি গৌণ কনস্ট্রাক্টর ব্যবহার করুন যা কোনও বিল্ডার এবং প্রাথমিক কনস্ট্রাক্টরে প্রতিনিধিদের নিয়ে যায়। কোড অনুসরণ হিসাবে দেখতে হবে:

class Car( //add private constructor if necessary
        val model: String?,
        val year: Int
) {

    private constructor(builder: Builder) : this(builder.model, builder.year)

    class Builder {
        var model: String? = null
            private set

        var year: Int = 0
            private set

        fun model(model: String) = apply { this.model = model }

        fun year(year: Int) = apply { this.year = year }

        fun build() = Car(this)
    }
}

ব্যবহার: val car = Car.Builder().model("X").build()

এই কোডটি কোনও বিল্ডার ডিএসএল ব্যবহার করে অতিরিক্ত ছোট করা যেতে পারে :

class Car (
        val model: String?,
        val year: Int
) {

    private constructor(builder: Builder) : this(builder.model, builder.year)

    companion object {
        inline fun build(block: Builder.() -> Unit) = Builder().apply(block).build()
    }

    class Builder {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}

ব্যবহার: val car = Car.build { model = "X" }

যদি কিছু মান প্রয়োজন হয় এবং এটির ডিফল্ট মান না থাকে তবে আপনাকে সেগুলি বিল্ডারের নির্মাতায় এবং buildকেবলমাত্র আমরা যে পদ্ধতিতে সংজ্ঞায়িত করেছি তাতে রাখা দরকার:

class Car (
        val model: String?,
        val year: Int,
        val required: String
) {

    private constructor(builder: Builder) : this(builder.model, builder.year, builder.required)

    companion object {
        inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build()
    }

    class Builder(
            val required: String
    ) {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}

ব্যবহার: val car = Car.build(required = "requiredValue") { model = "X" }


2
কিছুই নয়, তবে প্রশ্নের লেখক বিশেষত কীভাবে বিল্ডার প্যাটার্নটি প্রয়োগ করবেন তা জিজ্ঞাসা করেছিলেন।
কিরিল রাখমান

4
আমার নিজেকে সংশোধন করা উচিত, বিল্ডার প্যাটার্নটির কিছু সুবিধা রয়েছে, যেমন আপনি আংশিকভাবে নির্মিত বিল্ডারকে অন্য পদ্ধতিতে পাস করতে পারেন। তবে আপনি ঠিক বলেছেন, আমি একটি মন্তব্য যুক্ত করব।
কিরিল রাখমান

3
@ কিরিলরখমান কীভাবে জাভা থেকে বিল্ডারকে ফোন করবেন? বিল্ডারকে জাভা উপলব্ধ করার সহজ উপায় কি আছে?
কিহান

6
সমস্ত তিনটি সংস্করণ জাভা থেকে বলা যেতে পারে যেমন: Car.Builder builder = new Car.Builder();। তবে কেবলমাত্র প্রথম সংস্করণে একটি সাবলীল ইন্টারফেস রয়েছে যাতে দ্বিতীয় এবং তৃতীয় সংস্করণে কলগুলি বেঁধে রাখা যায় না।
কিরিল রাখমান

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

19

একটি পদ্ধতির নীচের মত কিছু করতে হয়:

class Car(
  val model: String?,
  val color: String?,
  val type: String?) {

    data class Builder(
      var model: String? = null,
      var color: String? = null,
      var type: String? = null) {

        fun model(model: String) = apply { this.model = model }
        fun color(color: String) = apply { this.color = color }
        fun type(type: String) = apply { this.type = type }
        fun build() = Car(model, color, type)
    }
}

ব্যবহারের নমুনা:

val car = Car.Builder()
  .model("Ford Focus")
  .color("Black")
  .type("Type")
  .build()

অনেক ধন্যবাদ! আপনি আমার দিন তৈরি! আপনার উত্তরটি SOLUTION হিসাবে চিহ্নিত করা উচিত।
এসভিডি

9

যেহেতু আমি জ্যাকসন লাইব্রেরিটি জেএসএন থেকে জিনিসগুলি পার্সিংয়ের জন্য ব্যবহার করছি, আমার খালি নির্মাতা থাকা দরকার এবং আমার optionচ্ছিক ক্ষেত্র থাকতে পারে না। এছাড়াও সমস্ত ক্ষেত্র পরিবর্তনীয় হতে হবে। তারপরে আমি এই দুর্দান্ত বাক্য গঠনটি ব্যবহার করতে পারি যা বিল্ডার প্যাটার্নের মতো একই কাজ করে:

val car = Car().apply{ model = "Ford"; year = 2000 }

8
জ্যাকসনে আপনার আসলে একটি খালি নির্মাণকারীর দরকার নেই এবং ক্ষেত্রগুলিকে পরিবর্তনযোগ্য হতে হবে না। আপনাকে কেবল আপনার কন্সট্রাক্টর প্যারামিটারগুলি @JsonProperty
বাস্তিয়ান ভয়েগট

2
এমনকি @JsonPropertyযদি আপনি -parametersস্যুইচটি সংকলন করেন তবে আপনার আর কোনওর সাথে মন্তব্য করতে হবে না ।
আমির আবিরি

2
জ্যাকসন আসলে একজন নির্মাতা ব্যবহারের জন্য কনফিগার করা যায়।
কিহান

1
যদি আপনি আপনার প্রকল্পে জ্যাকসন-মডিউল-কোটলিন মডিউল যুক্ত করেন তবে আপনি কেবল ডেটা ক্লাস ব্যবহার করতে পারেন এবং এটি কার্যকর হবে।
নীল ব্রুনিজ

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

7

আমি ব্যক্তিগতভাবে কখনও কোটলিনে কোনও বিল্ডারকে দেখিনি, তবে এটি কেবল আমারই হবে।

সমস্ত বৈধতা যা দরকার তা initব্লকে ঘটে :

class Car(val model: String,
          val year: Int = 2000) {

    init {
        if(year < 1900) throw Exception("...")
    }
}

আপনি প্রকৃতপক্ষে চান নি modelএবং yearপরিবর্তনযোগ্য হতে পারেন তা অনুমান করার জন্য আমি এখানে একটি স্বাধীনতা নিয়েছি । এছাড়াও সেই ডিফল্ট মানগুলির কোনও বুদ্ধি নেই বলে মনে হয় (বিশেষত nullজন্য) forname ) কিন্তু আমি বিক্ষোভের উদ্দেশ্যে এক ত্যাগ করেন।

একটি মতামত: নাম নির্ধারিত পরামিতিগুলি না থাকার জন্য জাভাতে বিল্ডার প্যাটার্নটি ব্যবহৃত হয়। নামযুক্ত প্যারামিটারগুলির মতো ভাষায় (যেমন কোটলিন বা পাইথন) প্যারামিটারগুলির দীর্ঘ তালিকা সহ কন্সট্রাক্টরগুলি রাখা ভাল অনুশীলন।


2
ধন্যবাদ উত্তরের জন্য অনেক। আমি আপনার পদ্ধতির পছন্দ করি তবে ক্ষতিটি এমন অনেক ক্লাসের জন্য যা অনেকগুলি পরামিতি সহ এটি নির্মাণকারীর ব্যবহার এবং শ্রেণীর পরীক্ষা করা এতটা বন্ধুত্বপূর্ণ হয় না।
কীহান

1
+ কীহান অন্য দুটি উপায়ে আপনি বৈধতা যাচাই করতে পারেন, ধরে নিলে ক্ষেত্রগুলির মধ্যে বৈধতাটি ঘটে না: 1) সেটার বৈধতা দেয় যেখানে সম্পত্তি প্রতিনিধি ব্যবহার করুন - এটি একটি সাধারণ সেটারের সাথে বৈধতা যা যথেষ্ট 2) এড়ান আদিম আবেগ এবং তাদের বৈধতা পাস করার জন্য নতুন ধরনের তৈরি করুন।
জ্যাকব জিমারম্যান

1
@ কেহান এটি পাইথনের একটি ক্লাসিক পদ্ধতি, এটি দশক যুক্তিযুক্ত কাজের জন্য এমনকি খুব ভালভাবে কাজ করে। এখানে কৌশলটি নাম দেওয়া যুক্তি (জাভাতে উপলভ্য নয়) ব্যবহার করা
ভোডদান

1
হ্যাঁ, এটিও ব্যবহার করার মতো সমাধান, এটি জাভা থেকে আলাদা নয় যেখানে বিল্ডার শ্রেণীর কিছু সুস্পষ্ট সুবিধা রয়েছে, কোটলিনে এটি এতটা সুস্পষ্ট নয়, সি # বিকাশকারীদের সাথে কথা বলেছে, সি # এর বৈশিষ্ট্যগুলির মতো কোটলিনও রয়েছে (ডিফল্ট মান এবং আপনি যখন প্যারামের নাম রাখতে পারেন) কলিং কনস্ট্রাক্টর) তারা বিল্ডার প্যাটার্নটিও ব্যবহার করেনি।
কীহান

1
@ ভিএক্সএইচ.ভিয়েট এ জাতীয় অনেকগুলি সমস্যা সমাধান করা যেতে পারে @JvmOverloads কোটলিংলং.আর্গ
ডকস /

4

আমি এমন অনেক উদাহরণ দেখেছি যা বিল্ডার হিসাবে অতিরিক্ত মজাদার ঘোষণা করে। আমি ব্যক্তিগতভাবে এই পদ্ধতির পছন্দ করি। বিল্ডারদের লেখার প্রচেষ্টা সংরক্ষণ করুন।

package android.zeroarst.lab.koltinlab

import kotlin.properties.Delegates

class Lab {
    companion object {
        @JvmStatic fun main(args: Array<String>) {

            val roy = Person {
                name = "Roy"
                age = 33
                height = 173
                single = true
                car {
                    brand = "Tesla"
                    model = "Model X"
                    year = 2017
                }
                car {
                    brand = "Tesla"
                    model = "Model S"
                    year = 2018
                }
            }

            println(roy)
        }

        class Person() {
            constructor(init: Person.() -> Unit) : this() {
                this.init()
            }

            var name: String by Delegates.notNull()
            var age: Int by Delegates.notNull()
            var height: Int by Delegates.notNull()
            var single: Boolean by Delegates.notNull()
            val cars: MutableList<Car> by lazy { arrayListOf<Car>() }

            override fun toString(): String {
                return "name=$name, age=$age, " +
                        "height=$height, " +
                        "single=${when (single) {
                            true -> "looking for a girl friend T___T"
                            false -> "Happy!!"
                        }}\nCars: $cars"
            }
        }

        class Car() {

            var brand: String by Delegates.notNull()
            var model: String by Delegates.notNull()
            var year: Int by Delegates.notNull()

            override fun toString(): String {
                return "(brand=$brand, model=$model, year=$year)"
            }
        }

        fun Person.car(init: Car.() -> Unit): Unit {
            cars.add(Car().apply(init))
        }

    }
}

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


2

সাধারণ শ্রেণীর জন্য আপনার আলাদা বিল্ডার লাগবে না। কিরিল রাখমান বর্ণিত হিসাবে আপনি alচ্ছিক নির্মাণকারী যুক্তিগুলি ব্যবহার করতে পারেন।

আপনার যদি আরও জটিল শ্রেণি থাকে তবে কোটলিন গ্রোভী স্টাইল বিল্ডার / ডিএসএল তৈরি করার একটি উপায় সরবরাহ করে:

প্রকার-নিরাপদ নির্মাতারা

এখানে একটি উদাহরণ:

গিথুব উদাহরণ - বিল্ডার / এসেম্ব্লার


ধন্যবাদ, তবে আমি এটি জাভা থেকেও ব্যবহার করার কথা ভাবছিলাম। যতদূর আমি জানি optionচ্ছিক যুক্তি জাভা থেকে কাজ করবে না।
কিহান

2

লোকেরা আজকাল কোটলিনের টাইপ-সেফ বিল্ডারদের চেক করা উচিত

অবজেক্ট তৈরির উপায় বলে ব্যবহার করে এরকম কিছু দেখাবে:

html {
    head {
        title {+"XML encoding with Kotlin"}
    }
    // ...
}

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


1

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

আপনার যদি সত্যিই বাস্তবায়নের প্রয়োজন হয়, কীরিল রাখমানের উত্তরটি সবচেয়ে কার্যকর উপায়ে কীভাবে বাস্তবায়ন করা যায় তার দৃ solid় উত্তর। আপনি যে জিনিসটিকে দরকারী মনে করতে পারেন তা হ'ল https://www.baeldung.com/kotlin-builder- Pattern আপনি জাভা এবং কোটলিনের সাথে তাদের প্রয়োগের ক্ষেত্রে তুলনা করতে এবং তার বিপরীতে তুলনা করতে পারেন


0

আমি বলব প্যাটার্ন এবং বাস্তবায়ন কোটলিনে অনেকটা একই রকম রয়েছে। আপনি কখনও কখনও এটি ডিফল্ট মানগুলির জন্য ধন্যবাদ এড়িয়ে যেতে পারেন, তবে আরও জটিল অবজেক্ট তৈরির জন্য, বিল্ডাররা এখনও একটি দরকারী সরঞ্জাম যা বাদ দেওয়া যায় না।


যতক্ষণ না ডিফল্ট মান সহ নির্মাতারা আপনি এমনকি প্রাথমিক ব্লক ব্যবহার করে ইনপুটটির বৈধতাও করতে পারেন । যাইহোক, আপনার যদি কিছু রাষ্ট্রীয় প্রয়োজন হয় (যাতে আপনাকে সামনে সবকিছু সুনির্দিষ্ট করতে না হয়) তবে বিল্ডার প্যাটার্নটি এখনও যাওয়ার উপায়।
mfulton26

আপনি কোড সহ একটি সহজ উদাহরণ দিতে পারেন? ইমেলের বৈধতা সহ নাম এবং ইমেল ক্ষেত্র সহ একটি সাধারণ ব্যবহারকারী শ্রেণি বলুন।
কিহান

0

আপনি কোটলিন উদাহরণে alচ্ছিক পরামিতি ব্যবহার করতে পারেন:

fun myFunc(p1: String, p2: Int = -1, p3: Long = -1, p4: String = "default") {
    System.out.printf("parameter %s %d %d %s\n", p1, p2, p3, p4)
}

তারপর

myFunc("a")
myFunc("a", 1)
myFunc("a", 1, 2)
myFunc("a", 1, 2, "b")

0
class Foo private constructor(@DrawableRes requiredImageRes: Int, optionalTitle: String?) {

    @DrawableRes
    @get:DrawableRes
    val requiredImageRes: Int

    val optionalTitle: String?

    init {
        this.requiredImageRes = requiredImageRes
        this.requiredImageRes = optionalTitle
    }

    class Builder {

        @DrawableRes
        private var requiredImageRes: Int = -1

        private var optionalTitle: String? = null

        fun requiredImageRes(@DrawableRes imageRes: Int): Builder {
            this.intent = intent
            return this
        } 

        fun optionalTitle(title: String): Builder {
            this.optionalTitle = title
            return this
        }

        fun build(): Foo {
            if(requiredImageRes == -1) {
                throw IllegalStateException("No image res provided")
            }
            return Foo(this.requiredImageRes, this.optionalTitle)
        }

    }

}

0

আমি নিম্নলিখিত কোড সহ কোটলিনে একটি বেসিক বিল্ডার প্যাটার্নটি প্রয়োগ করেছি:

data class DialogMessage(
        var title: String = "",
        var message: String = ""
) {


    class Builder( context: Context){


        private var context: Context = context
        private var title: String = ""
        private var message: String = ""

        fun title( title : String) = apply { this.title = title }

        fun message( message : String ) = apply { this.message = message  }    

        fun build() = KeyoDialogMessage(
                title,
                message
        )

    }

    private lateinit var  dialog : Dialog

    fun show(){
        this.dialog= Dialog(context)
        .
        .
        .
        dialog.show()

    }

    fun hide(){
        if( this.dialog != null){
            this.dialog.dismiss()
        }
    }
}

এবং পরিশেষে

জাভা:

new DialogMessage.Builder( context )
       .title("Title")
       .message("Message")
       .build()
       .show();

Kotlin:

DialogMessage.Builder( context )
       .title("Title")
       .message("")
       .build()
       .show()

0

আমি একটি কোটলিন প্রকল্পে কাজ করছিলাম যা জাভা ক্লায়েন্টদের দ্বারা গ্রাহিত একটি API (যা কোটলিন ভাষা নির্মাণের সুবিধা নিতে পারে না) উন্মুক্ত করে। জাভাতে তাদের ব্যবহারযোগ্য করে তুলতে আমাদের বিল্ডারদের যোগ করতে হয়েছিল, তাই আমি @ বিল্ডার টীকা তৈরি করেছি: https://github.com/ThinkingLogic/kotlin-builder-notnotation - এটি মূলত কোটলিনের লম্বক @ বিল্ডার টীকাটির প্রতিস্থাপন।

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