অবজেক্ট-ওরিয়েন্টেড ডিজাইন প্যাটার্ন বাস্তবায়ন করা (Implementing an Object-Oriented Design Pattern)

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

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

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

প্রথমে, আমরা আরও ঐতিহ্যবাহী অবজেক্ট-ওরিয়েন্টেড উপায়ে স্টেট প্যাটার্নটি বাস্তবায়ন করব, তারপর আমরা এমন একটি পদ্ধতি ব্যবহার করব যা Rust-এ আরও কিছুটা স্বাভাবিক। আসুন স্টেট প্যাটার্ন ব্যবহার করে একটি ব্লগ পোস্ট ওয়ার্কফ্লো ক্রমবর্ধমানভাবে বাস্তবায়ন করি।

চূড়ান্ত কার্যকারিতা দেখতে এইরকম হবে:

  1. একটি ব্লগ পোস্ট একটি খালি ড্রাফট হিসাবে শুরু হয়।
  2. যখন ড্রাফট সম্পন্ন হয়, তখন পোস্টের একটি রিভিউ রিকোয়েস্ট করা হয়।
  3. যখন পোস্টটি অ্যাপ্রুভ করা হয়, তখন এটি পাবলিশ করা হয়।
  4. শুধুমাত্র পাবলিশড ব্লগ পোস্টগুলি প্রিন্ট করার জন্য কনটেন্ট রিটার্ন করে, তাই আনঅ্যাপ্রুভড পোস্টগুলি দুর্ঘটনাক্রমে পাবলিশ করা যায় না।

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

Listing 18-11 কোড আকারে এই ওয়ার্কফ্লো দেখায়: এটি blog নামক একটি লাইব্রেরি ক্রেটে আমরা যে API বাস্তবায়ন করব তার একটি ব্যবহারের উদাহরণ। এটি এখনও কম্পাইল হবে না কারণ আমরা blog ক্রেটটি বাস্তবায়ন করিনি।

use blog::Post;

fn main() {
    let mut post = Post::new();

    post.add_text("I ate a salad for lunch today");
    assert_eq!("", post.content());

    post.request_review();
    assert_eq!("", post.content());

    post.approve();
    assert_eq!("I ate a salad for lunch today", post.content());
}

আমরা ব্যবহারকারীকে Post::new দিয়ে একটি নতুন ড্রাফ্ট ব্লগ পোস্ট তৈরি করার অনুমতি দিতে চাই। আমরা ব্লগ পোস্টে টেক্সট যোগ করার অনুমতি দিতে চাই। যদি আমরা অ্যাপ্রুভালের আগে অবিলম্বে পোস্টের কনটেন্ট পাওয়ার চেষ্টা করি, তাহলে আমাদের কোনও টেক্সট পাওয়া উচিত নয় কারণ পোস্টটি এখনও একটি ড্রাফ্ট। আমরা প্রদর্শনের উদ্দেশ্যে কোডে assert_eq! যোগ করেছি। এর জন্য একটি চমৎকার ইউনিট টেস্ট হবে একটি ড্রাফ্ট ব্লগ পোস্ট content মেথড থেকে একটি খালি স্ট্রিং রিটার্ন করে কিনা তা পরীক্ষা করা, কিন্তু আমরা এই উদাহরণের জন্য পরীক্ষা লিখব না।

এরপর, আমরা পোস্টের একটি রিভিউয়ের জন্য একটি রিকোয়েস্ট সক্রিয় করতে চাই, এবং আমরা চাই অ্যাপ্রুভালের অপেক্ষায় থাকার সময় content যেন একটি খালি স্ট্রিং রিটার্ন করে। যখন পোস্টটি অ্যাপ্রুভাল পায়, তখন এটি পাবলিশ হওয়া উচিত, যার মানে হল content কল করা হলে পোস্টের টেক্সট রিটার্ন করা হবে।

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

Post সংজ্ঞায়িত করা এবং ড্রাফ্ট স্টেটে একটি নতুন ইনস্ট্যান্স তৈরি করা (Defining Post and Creating a New Instance in the Draft State)

আসুন লাইব্রেরির ইমপ্লিমেন্টেশন শুরু করি! আমরা জানি যে আমাদের একটি পাবলিক Post স্ট্রাক্ট দরকার যা কিছু কনটেন্ট ধারণ করে, তাই আমরা স্ট্রাক্টের সংজ্ঞা এবং একটি অ্যাসোসিয়েটেড পাবলিক new ফাংশন দিয়ে শুরু করব যাতে Post-এর একটি ইনস্ট্যান্স তৈরি করা যায়, যেমনটি Listing 18-12-এ দেখানো হয়েছে। আমরা একটি প্রাইভেট State ট্রেইটও তৈরি করব যা সেই আচরণকে সংজ্ঞায়িত করবে যা একটি Post-এর সমস্ত স্টেট অবজেক্টের অবশ্যই থাকতে হবে।

তারপর Post একটি state নামক প্রাইভেট ফিল্ডে একটি Option<T>-এর ভিতরে Box<dyn State>-এর একটি ট্রেইট অবজেক্ট ধারণ করবে স্টেট অবজেক্ট রাখার জন্য। আপনি একটু পরেই দেখতে পাবেন কেন Option<T> প্রয়োজন।

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }
}

trait State {}

struct Draft {}

impl State for Draft {}

State ট্রেইট বিভিন্ন পোস্ট স্টেট দ্বারা শেয়ার করা আচরণ সংজ্ঞায়িত করে। স্টেট অবজেক্টগুলি হল Draft, PendingReview, এবং Published, এবং তারা সকলেই State ট্রেইট ইমপ্লিমেন্ট করবে। আপাতত, ট্রেইটের কোনও মেথড নেই, এবং আমরা শুধুমাত্র Draft স্টেট সংজ্ঞায়িত করে শুরু করব কারণ আমরা চাই একটি পোস্ট সেই স্টেটে শুরু হোক।

যখন আমরা একটি নতুন Post তৈরি করি, তখন আমরা এর state ফিল্ডটিকে একটি Some মান সেট করি যা একটি Box ধারণ করে। এই Box টি Draft স্ট্রাক্টের একটি নতুন ইনস্ট্যান্সের দিকে নির্দেশ করে। এটি নিশ্চিত করে যে যখনই আমরা Post-এর একটি নতুন ইনস্ট্যান্স তৈরি করি, এটি একটি ড্রাফ্ট হিসাবে শুরু হবে। যেহেতু Post-এর state ফিল্ডটি প্রাইভেট, তাই অন্য কোনো স্টেটে Post তৈরি করার কোনো উপায় নেই! Post::new ফাংশনে, আমরা content ফিল্ডটিকে একটি নতুন, খালি String-এ সেট করি।

পোস্ট কনটেন্টের টেক্সট সংরক্ষণ করা (Storing the Text of the Post Content)

আমরা Listing 18-11-এ দেখেছি যে আমরা add_text নামে একটি মেথড কল করতে এবং এটিকে একটি &str পাস করতে সক্ষম হতে চাই যা তারপর ব্লগ পোস্টের টেক্সট কনটেন্ট হিসাবে যুক্ত করা হয়। আমরা এটিকে content ফিল্ডটিকে pub হিসাবে প্রকাশ করার পরিবর্তে একটি মেথড হিসাবে ইমপ্লিমেন্ট করি, যাতে পরে আমরা একটি মেথড ইমপ্লিমেন্ট করতে পারি যা content ফিল্ডের ডেটা কীভাবে পড়া হয় তা নিয়ন্ত্রণ করবে। add_text মেথডটি বেশ সহজবোধ্য, তাই আসুন Listing 18-13-এ impl Post ব্লকে ইমপ্লিমেন্টেশন যোগ করি:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }
}

trait State {}

struct Draft {}

impl State for Draft {}

add_text মেথডটি self-এর একটি মিউটেবল রেফারেন্স নেয়, কারণ আমরা Post ইনস্ট্যান্স পরিবর্তন করছি যার উপর আমরা add_text কল করছি। তারপর আমরা content-এ String-এ push_str কল করি এবং সংরক্ষিত content-এ যোগ করার জন্য text আর্গুমেন্ট পাস করি। এই আচরণটি পোস্টটি কোন স্টেটে আছে তার উপর নির্ভর করে না, তাই এটি স্টেট প্যাটার্নের অংশ নয়। add_text মেথডটি state ফিল্ডের সাথে মোটেও ইন্টারঅ্যাক্ট করে না, তবে এটি আমাদের সমর্থন করতে চাওয়া আচরণের অংশ।

একটি ড্রাফ্ট পোস্টের কনটেন্ট খালি তা নিশ্চিত করা (Ensuring the Content of a Draft Post Is Empty)

এমনকি আমরা add_text কল করার পরে এবং আমাদের পোস্টে কিছু কনটেন্ট যোগ করার পরেও, আমরা চাই যে content মেথডটি একটি খালি স্ট্রিং স্লাইস রিটার্ন করুক কারণ পোস্টটি এখনও ড্রাফ্ট স্টেটে রয়েছে, যেমনটি Listing 18-11-এর 7 লাইনে দেখানো হয়েছে। আপাতত, আসুন content মেথডটিকে সবচেয়ে সহজ জিনিস দিয়ে ইমপ্লিমেন্ট করি যা এই প্রয়োজনীয়তা পূরণ করবে: সর্বদা একটি খালি স্ট্রিং স্লাইস রিটার্ন করা। আমরা পরে এটি পরিবর্তন করব যখন আমরা একটি পোস্টের স্টেট পরিবর্তন করার ক্ষমতা ইমপ্লিমেন্ট করব যাতে এটি পাবলিশ করা যায়। এখন পর্যন্ত, পোস্টগুলি শুধুমাত্র ড্রাফ্ট স্টেটে থাকতে পারে, তাই পোস্টের কনটেন্ট সবসময় খালি হওয়া উচিত। Listing 18-14 এই প্লেসহোল্ডার ইমপ্লিমেন্টেশন দেখায়:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }
}

trait State {}

struct Draft {}

impl State for Draft {}

এই যোগ করা content মেথড সহ, Listing 18-11-এর 7 লাইন পর্যন্ত সবকিছু উদ্দেশ্য অনুযায়ী কাজ করে।

পোস্টের একটি রিভিউয়ের অনুরোধ করা তার স্টেট পরিবর্তন করে (Requesting a Review of the Post Changes Its State)

এরপর, আমাদের একটি পোস্টের রিভিউয়ের জন্য অনুরোধ করার কার্যকারিতা যোগ করতে হবে, যা এর স্টেট Draft থেকে PendingReview-তে পরিবর্তন করবে। Listing 18-15 এই কোডটি দেখায়:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

আমরা Post-কে request_review নামে একটি পাবলিক মেথড দিই যা self-এর একটি মিউটেবল রেফারেন্স নেবে। তারপর আমরা Post-এর বর্তমান স্টেটে একটি অভ্যন্তরীণ request_review মেথড কল করি এবং এই দ্বিতীয় request_review মেথডটি বর্তমান স্টেটটিকে কনজিউম করে এবং একটি নতুন স্টেট রিটার্ন করে।

আমরা State ট্রেইটে request_review মেথড যোগ করি; যে সমস্ত টাইপ ট্রেইট ইমপ্লিমেন্ট করে তাদের এখন request_review মেথড ইমপ্লিমেন্ট করতে হবে। লক্ষ্য করুন যে মেথডের প্রথম প্যারামিটার হিসাবে self, &self, বা &mut self থাকার পরিবর্তে, আমাদের কাছে self: Box<Self> রয়েছে। এই সিনট্যাক্সের অর্থ হল মেথডটি শুধুমাত্র তখনই বৈধ যখন একটি Box-এ টাইপটি ধারণ করে কল করা হয়। এই সিনট্যাক্সটি Box<Self>-এর ownership নেয়, পুরানো স্টেটটিকে অকার্যকর করে যাতে Post-এর স্টেট ভ্যালু একটি নতুন স্টেটে রূপান্তরিত হতে পারে।

পুরানো স্টেটটি কনজিউম করার জন্য, request_review মেথডের স্টেট ভ্যালুর ownership নেওয়া প্রয়োজন। এখানেই Post-এর state ফিল্ডের Option আসে: আমরা state ফিল্ড থেকে Some মানটি বের করে নিতে take মেথড কল করি এবং এর জায়গায় একটি None রেখে যাই, কারণ Rust আমাদের স্ট্রাক্টগুলিতে জনবসতিহীন ফিল্ড রাখতে দেয় না। এটি আমাদের state ভ্যালুকে Post থেকে সরানোর অনুমতি দেয়, ধার করার পরিবর্তে। তারপর আমরা পোস্টের state ভ্যালুকে এই অপারেশনের ফলাফলে সেট করব।

আমাদের state-কে সাময়িকভাবে None-এ সেট করতে হবে, সরাসরি self.state = self.state.request_review();-এর মতো কোড দিয়ে সেট করার পরিবর্তে, স্টেট ভ্যালুর ownership পেতে। এটি নিশ্চিত করে যে Post পুরানো state ভ্যালুটি ব্যবহার করতে পারবে না যখন আমরা এটিকে একটি নতুন স্টেটে রূপান্তরিত করব।

Draft-এ request_review মেথড একটি নতুন PendingReview স্ট্রাক্টের একটি নতুন, বক্সড ইনস্ট্যান্স রিটার্ন করে, যা সেই স্টেটকে উপস্থাপন করে যখন একটি পোস্ট রিভিউয়ের জন্য অপেক্ষা করছে। PendingReview স্ট্রাক্টটিও request_review মেথড ইমপ্লিমেন্ট করে কিন্তু কোনও রূপান্তর করে না। বরং, এটি নিজেই রিটার্ন করে, কারণ যখন আমরা ইতিমধ্যে PendingReview স্টেটে থাকা একটি পোস্টে একটি রিভিউয়ের অনুরোধ করি, তখন এটি PendingReview স্টেটে থাকা উচিত।

এখন আমরা স্টেট প্যাটার্নের সুবিধাগুলি দেখতে শুরু করতে পারি: Post-এ request_review মেথডটি তার state ভ্যালু যাই হোক না কেন একই। প্রতিটি স্টেট তার নিজস্ব নিয়মের জন্য দায়ী।

আমরা Post-এ content মেথডটিকে যেমন আছে তেমনই রাখব, একটি খালি স্ট্রিং স্লাইস রিটার্ন করব। আমরা এখন Draft স্টেটের পাশাপাশি PendingReview স্টেটেও একটি Post রাখতে পারি, কিন্তু আমরা PendingReview স্টেটে একই আচরণ চাই। Listing 18-11 এখন 10 লাইন পর্যন্ত কাজ করে!

content-এর আচরণ পরিবর্তন করতে approve যোগ করা (Adding approveto Change the Behavior ofcontent`)

approve মেথডটি request_review মেথডের মতোই হবে: এটি state-কে সেই মানে সেট করবে যা বর্তমান স্টেটের বলা উচিত যখন সেই স্টেটটি অ্যাপ্রুভ করা হয়, যেমনটি Listing 18-16-এ দেখানো হয়েছে:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
}

struct Draft {}

impl State for Draft {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

আমরা State ট্রেইটে approve মেথড যোগ করি এবং একটি নতুন স্ট্রাক্ট যোগ করি যা State ইমপ্লিমেন্ট করে, Published স্টেট।

PendingReview-তে request_review যেভাবে কাজ করে, আমরা যদি একটি Draft-এ approve মেথড কল করি, তাহলে এর কোনো প্রভাব থাকবে না কারণ approve self রিটার্ন করবে। যখন আমরা PendingReview-তে approve কল করি, তখন এটি Published স্ট্রাক্টের একটি নতুন, বক্সড ইনস্ট্যান্স রিটার্ন করে। Published স্ট্রাক্টটি State ট্রেইট ইমপ্লিমেন্ট করে এবং request_review মেথড এবং approve মেথড উভয়ের জন্য, এটি নিজেই রিটার্ন করে, কারণ সেই ক্ষেত্রগুলিতে পোস্টটি Published স্টেটে থাকা উচিত।

এখন আমাদের Post-এ content মেথড আপডেট করতে হবে। আমরা চাই content থেকে রিটার্ন করা মান Post-এর বর্তমান স্টেটের উপর নির্ভর করুক, তাই আমরা Post-কে তার state-এ সংজ্ঞায়িত একটি content মেথডে অর্পণ করব, যেমনটি Listing 18-17-এ দেখানো হয়েছে:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        self.state.as_ref().unwrap().content(self)
    }
    // --snip--

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

যেহেতু লক্ষ্য হল এই সমস্ত নিয়মগুলিকে স্ট্রাক্টগুলির মধ্যে রাখা যা State ইমপ্লিমেন্ট করে, তাই আমরা state-এ একটি content মেথড কল করি এবং পোস্ট ইনস্ট্যান্সটি (অর্থাৎ, self) একটি আর্গুমেন্ট হিসাবে পাস করি। তারপর আমরা state ভ্যালুতে content মেথড ব্যবহার করে যে মানটি রিটার্ন করা হয়েছে তা রিটার্ন করি।

আমরা Option-এ as_ref মেথড কল করি কারণ আমরা Option-এর ভিতরের মানের একটি রেফারেন্স চাই, মানের ownership নয়। যেহেতু state হল একটি Option<Box<dyn State>>, যখন আমরা as_ref কল করি, তখন একটি Option<&Box<dyn State>> রিটার্ন করা হয়। যদি আমরা as_ref কল না করতাম, তাহলে আমরা একটি error পেতাম কারণ আমরা ফাংশন প্যারামিটারের ধার করা &self থেকে state সরাতে পারি না।

তারপর আমরা unwrap মেথড কল করি, যা আমরা জানি কখনই প্যানিক করবে না, কারণ আমরা জানি যে Post-এর মেথডগুলি নিশ্চিত করে যে সেই মেথডগুলি সম্পন্ন হলে state-এ সর্বদা একটি Some মান থাকবে। এটি সেই ক্ষেত্রগুলির মধ্যে একটি যা আমরা Chapter 9-এর “Cases In Which You Have More Information Than the Compiler” বিভাগে আলোচনা করেছি যখন আমরা জানি যে একটি None মান কখনই সম্ভব নয়, যদিও কম্পাইলার সেটি বুঝতে সক্ষম নয়।

এই মুহুর্তে, যখন আমরা &Box<dyn State>-এ content কল করি, তখন & এবং Box-এ ডিরেফ কোয়েরশন কার্যকর হবে যাতে content মেথডটি শেষ পর্যন্ত সেই টাইপে কল করা হয় যা State ট্রেইট ইমপ্লিমেন্ট করে। তার মানে আমাদের State ট্রেইটের সংজ্ঞায় content যোগ করতে হবে, এবং সেখানেই আমরা কোন কনটেন্ট রিটার্ন করতে হবে তার লজিক রাখব, আমরা কোন স্টেটে আছি তার উপর নির্ভর করে, যেমনটি Listing 18-18-এ দেখানো হয়েছে:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        self.state.as_ref().unwrap().content(self)
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;

    fn content<'a>(&self, post: &'a Post) -> &'a str {
        ""
    }
}

// --snip--

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    // --snip--
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn content<'a>(&self, post: &'a Post) -> &'a str {
        &post.content
    }
}

আমরা content মেথডের জন্য একটি ডিফল্ট ইমপ্লিমেন্টেশন যোগ করি যা একটি খালি স্ট্রিং স্লাইস রিটার্ন করে। এর মানে হল যে আমাদের Draft এবং PendingReview স্ট্রাক্টগুলিতে content ইমপ্লিমেন্ট করার দরকার নেই। Published স্ট্রাক্টটি content মেথডটিকে ওভাররাইড করবে এবং post.content-এর মান রিটার্ন করবে।

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

এবং আমরা সম্পন্ন করেছি—Listing 18-11-এর সমস্ত কিছুই এখন কাজ করে! আমরা ব্লগ পোস্ট ওয়ার্কফ্লোর নিয়ম সহ স্টেট প্যাটার্নটি ইমপ্লিমেন্ট করেছি। নিয়ম সম্পর্কিত লজিক Post-এর মধ্যে ছড়িয়ে ছিটিয়ে না থেকে স্টেট অবজেক্টগুলিতে থাকে।

কেন একটি এনাম (Enum) নয়?

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

স্টেট প্যাটার্নের ট্রেড-অফ (Trade-offs of the State Pattern)

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

আমরা যদি এমন একটি বিকল্প ইমপ্লিমেন্টেশন তৈরি করতাম যা স্টেট প্যাটার্ন ব্যবহার করে না, তাহলে আমরা পরিবর্তে Post-এর মেথডগুলিতে বা এমনকি main কোডে match এক্সপ্রেশন ব্যবহার করতে পারতাম যা পোস্টের স্টেট পরীক্ষা করে এবং সেই স্থানগুলিতে আচরণ পরিবর্তন করে। এর মানে হল যে একটি পোস্ট পাবলিশড স্টেটে থাকার সমস্ত প্রভাব বোঝার জন্য আমাদের বেশ কয়েকটি জায়গায় দেখতে হবে! এটি শুধুমাত্র আরও বাড়বে যত বেশি স্টেট আমরা যোগ করব: সেই match এক্সপ্রেশনগুলির প্রত্যেকটির জন্য অন্য একটি আর্ম প্রয়োজন হবে।

স্টেট প্যাটার্নের সাথে, Post মেথড এবং আমরা যেখানে Post ব্যবহার করি সেখানে match এক্সপ্রেশনের প্রয়োজন নেই এবং একটি নতুন স্টেট যোগ করার জন্য, আমাদের কেবল একটি নতুন স্ট্রাক্ট যুক্ত করতে হবে এবং সেই একটি স্ট্রাক্টে ট্রেইট মেথডগুলি ইমপ্লিমেন্ট করতে হবে।

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

  • একটি reject মেথড যুক্ত করুন যা পোস্টের স্টেটকে PendingReview থেকে Draft-এ পরিবর্তন করে।
  • স্টেটটিকে Published-এ পরিবর্তন করার আগে approve-এ দুটি কল প্রয়োজন।
  • ব্যবহারকারীদের শুধুমাত্র তখনই টেক্সট কনটেন্ট যোগ করার অনুমতি দিন যখন একটি পোস্ট Draft স্টেটে থাকে। ইঙ্গিত: স্টেট অবজেক্টকে কনটেন্ট সম্পর্কে কী পরিবর্তন হতে পারে তার জন্য দায়ী করুন কিন্তু Post পরিবর্তন করার জন্য দায়ী নয়।

স্টেট প্যাটার্নের একটি অসুবিধা হল, যেহেতু স্টেটগুলি স্টেটগুলির মধ্যে ট্রানজিশন ইমপ্লিমেন্ট করে, তাই কিছু স্টেট একে অপরের সাথে কাপল্ড। যদি আমরা PendingReview এবং Published-এর মধ্যে আরেকটি স্টেট যোগ করি, যেমন Scheduled, তাহলে আমাদের PendingReview-এর কোড পরিবর্তন করতে হবে যাতে পরিবর্তে Scheduled-এ ট্রানজিশন করা যায়। একটি নতুন স্টেট যোগ করার সাথে PendingReview-এর পরিবর্তন করার প্রয়োজন না হলে এটি কম কাজ হত, কিন্তু তার মানে অন্য একটি ডিজাইন প্যাটার্নে স্যুইচ করা।

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

অন্যান্য ডুপ্লিকেশনের মধ্যে রয়েছে Post-এ request_review এবং approve মেথডগুলির অনুরূপ ইমপ্লিমেন্টেশন। উভয় মেথডই Option-এর state ফিল্ডের মানের উপর একই মেথডের ইমপ্লিমেন্টেশনে অর্পণ করে এবং state ফিল্ডের নতুন মানটিকে ফলাফলে সেট করে। যদি আমাদের Post-এ অনেকগুলি মেথড থাকত যা এই প্যাটার্ন অনুসরণ করে, তাহলে আমরা পুনরাবৃত্তি দূর করতে একটি ম্যাক্রো সংজ্ঞায়িত করার কথা বিবেচনা করতে পারি (Chapter 20-এর “Macros” বিভাগটি দেখুন)।

অবজেক্ট-ওরিয়েন্টেড ভাষাগুলির জন্য যেমন সংজ্ঞায়িত করা হয়েছে ঠিক তেমনই স্টেট প্যাটার্নটি ইমপ্লিমেন্ট করার মাধ্যমে, আমরা Rust-এর শক্তির পূর্ণ সুবিধা নিচ্ছি না যতটা আমরা পারতাম। আসুন blog ক্রেটে আমরা কিছু পরিবর্তন দেখি যা অবৈধ স্টেট এবং ট্রানজিশনগুলিকে কম্পাইল টাইমের error-এ পরিণত করতে পারে।

টাইপের মধ্যে স্টেট এবং আচরণ এনকোড করা (Encoding States and Behavior as Types)

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

আসুন Listing 18-11-এর main-এর প্রথম অংশটি বিবেচনা করি:

use blog::Post;

fn main() {
    let mut post = Post::new();

    post.add_text("I ate a salad for lunch today");
    assert_eq!("", post.content());

    post.request_review();
    assert_eq!("", post.content());

    post.approve();
    assert_eq!("I ate a salad for lunch today", post.content());
}

আমরা এখনও Post::new ব্যবহার করে ড্রাফ্ট স্টেটে নতুন পোস্ট তৈরি করা এবং পোস্টে টেক্সট যুক্ত করার ক্ষমতা সক্রিয় করি। কিন্তু একটি ড্রাফ্ট পোস্টে একটি content মেথড থাকার পরিবর্তে যা একটি খালি স্ট্রিং রিটার্ন করে, আমরা এমন করব যাতে ড্রাফ্ট পোস্টগুলিতে content মেথড না থাকে। এইভাবে, যদি আমরা একটি ড্রাফ্ট পোস্টের কনটেন্ট পেতে চেষ্টা করি, তাহলে আমরা একটি কম্পাইলার error পাব যা আমাদের বলবে যে মেথডটি বিদ্যমান নেই। ফলস্বরূপ, আমাদের জন্য দুর্ঘটনাক্রমে প্রোডাকশনে ড্রাফ্ট পোস্টের কনটেন্ট প্রদর্শন করা অসম্ভব হবে, কারণ সেই কোডটি কম্পাইলই হবে না। Listing 18-19 একটি Post স্ট্রাক্ট এবং একটি DraftPost স্ট্রাক্টের সংজ্ঞা দেখায়, সেইসাথে প্রত্যেকটির মেথডগুলি:

pub struct Post {
    content: String,
}

pub struct DraftPost {
    content: String,
}

impl Post {
    pub fn new() -> DraftPost {
        DraftPost {
            content: String::new(),
        }
    }

    pub fn content(&self) -> &str {
        &self.content
    }
}

impl DraftPost {
    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }
}

Post এবং DraftPost উভয় স্ট্রাক্টের একটি প্রাইভেট content ফিল্ড রয়েছে যা ব্লগ পোস্টের টেক্সট সংরক্ষণ করে। স্ট্রাক্টগুলিতে আর state ফিল্ড নেই কারণ আমরা স্টেটের এনকোডিংকে স্ট্রাক্টগুলির টাইপে স্থানান্তরিত করছি। Post স্ট্রাক্টটি একটি পাবলিশড পোস্টকে উপস্থাপন করবে এবং এতে একটি content মেথড রয়েছে যা content রিটার্ন করে।

আমাদের এখনও একটি Post::new ফাংশন রয়েছে, কিন্তু Post-এর একটি ইনস্ট্যান্স রিটার্ন করার পরিবর্তে, এটি DraftPost-এর একটি ইনস্ট্যান্স রিটার্ন করে। যেহেতু content প্রাইভেট এবং এমন কোনও ফাংশন নেই যা Post রিটার্ন করে, তাই এই মুহূর্তে Post-এর একটি ইনস্ট্যান্স তৈরি করা সম্ভব নয়।

DraftPost স্ট্রাক্টের একটি add_text মেথড রয়েছে, তাই আমরা আগের মতোই content-এ টেক্সট যোগ করতে পারি, কিন্তু লক্ষ্য করুন যে DraftPost-এ একটি content মেথড সংজ্ঞায়িত করা নেই! তাই এখন প্রোগ্রামটি নিশ্চিত করে যে সমস্ত পোস্ট ড্রাফ্ট পোস্ট হিসাবে শুরু হয় এবং ড্রাফ্ট পোস্টগুলির কনটেন্ট প্রদর্শনের জন্য উপলব্ধ নেই। এই সীমাবদ্ধতাগুলি এড়ানোর যেকোনো প্রচেষ্টা কম্পাইলার error-এর কারণ হবে।

বিভিন্ন টাইপে রূপান্তর হিসাবে ট্রানজিশন বাস্তবায়ন করা (Implementing Transitions as Transformations into Different Types)

তাহলে আমরা কীভাবে একটি পাবলিশড পোস্ট পাব? আমরা এই নিয়মটি প্রয়োগ করতে চাই যে একটি ড্রাফ্ট পোস্ট পাবলিশ করার আগে অবশ্যই রিভিউ এবং অ্যাপ্রুভ করতে হবে। পেন্ডিং রিভিউ স্টেটের একটি পোস্টের এখনও কোনও কনটেন্ট প্রদর্শন করা উচিত নয়। আসুন Listing 18-20-এ দেখানো আরেকটি স্ট্রাক্ট, PendingReviewPost যোগ করে, DraftPost-এ request_review মেথড সংজ্ঞায়িত করে একটি PendingReviewPost রিটার্ন করার জন্য এবং PendingReviewPost-এ একটি approve মেথড সংজ্ঞায়িত করে একটি Post রিটার্ন করার জন্য এই সীমাবদ্ধতাগুলি বাস্তবায়ন করি:

pub struct Post {
    content: String,
}

pub struct DraftPost {
    content: String,
}

impl Post {
    pub fn new() -> DraftPost {
        DraftPost {
            content: String::new(),
        }
    }

    pub fn content(&self) -> &str {
        &self.content
    }
}

impl DraftPost {
    // --snip--
    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn request_review(self) -> PendingReviewPost {
        PendingReviewPost {
            content: self.content,
        }
    }
}

pub struct PendingReviewPost {
    content: String,
}

impl PendingReviewPost {
    pub fn approve(self) -> Post {
        Post {
            content: self.content,
        }
    }
}

request_review এবং approve মেথডগুলি self-এর ownership নেয়, এইভাবে DraftPost এবং PendingReviewPost ইনস্ট্যান্সগুলিকে গ্রাস করে এবং সেগুলিকে যথাক্রমে একটি PendingReviewPost এবং একটি পাবলিশড Post-এ রূপান্তরিত করে। এইভাবে, আমরা তাদের উপর request_review কল করার পরে আমাদের কাছে কোনও অবশিষ্ট DraftPost ইনস্ট্যান্স থাকবে না, ইত্যাদি। PendingReviewPost স্ট্রাক্টটিতে একটি content মেথড সংজ্ঞায়িত করা নেই, তাই এর কনটেন্ট পড়ার চেষ্টা করলে DraftPost-এর মতো কম্পাইলার error হবে। যেহেতু একটি পাবলিশড Post ইনস্ট্যান্স পাওয়ার একমাত্র উপায় যার একটি content মেথড সংজ্ঞায়িত করা হয়েছে তা হল একটি PendingReviewPost-এ approve মেথড কল করা এবং একটি PendingReviewPost পাওয়ার একমাত্র উপায় হল একটি DraftPost-এ request_review মেথড কল করা, তাই আমরা এখন ব্লগ পোস্ট ওয়ার্কফ্লোটিকে টাইপ সিস্টেমে এনকোড করেছি।

কিন্তু আমাদের main-এও কিছু ছোট পরিবর্তন করতে হবে। request_review এবং approve মেথডগুলি যে স্ট্রাক্টগুলিতে কল করা হয় সেগুলিকে পরিবর্তন করার পরিবর্তে নতুন ইনস্ট্যান্স রিটার্ন করে, তাই আমাদের রিটার্ন করা ইনস্ট্যান্সগুলি সংরক্ষণ করার জন্য আরও let post = শ্যাডোয়িং অ্যাসাইনমেন্ট যোগ করতে হবে। আমরা ড্রাফ্ট এবং পেন্ডিং রিভিউ পোস্টের কনটেন্ট খালি স্ট্রিং হওয়ার অ্যাসারশনগুলিও রাখতে পারি না, বা আমাদের সেগুলি প্রয়োজনও নেই: আমরা আর সেই স্টেটগুলিতে পোস্টের কনটেন্ট ব্যবহার করার চেষ্টা করে এমন কোড কম্পাইল করতে পারি না। main-এর আপডেটেড কোড Listing 18-21-এ দেখানো হয়েছে:

use blog::Post;

fn main() {
    let mut post = Post::new();

    post.add_text("I ate a salad for lunch today");

    let post = post.request_review();

    let post = post.approve();

    assert_eq!("I ate a salad for lunch today", post.content());
}

post পুনরায় অ্যাসাইন করার জন্য আমাদের main-এ যে পরিবর্তনগুলি করতে হয়েছিল তার মানে হল যে এই ইমপ্লিমেন্টেশনটি আর অবজেক্ট-ওরিয়েন্টেড স্টেট প্যাটার্নকে সম্পূর্ণরূপে অনুসরণ করে না: স্টেটগুলির মধ্যে রূপান্তরগুলি আর সম্পূর্ণরূপে Post ইমপ্লিমেন্টেশনের মধ্যে এনক্যাপসুলেট করা হয় না। যাইহোক, আমাদের লাভ হল যে অবৈধ স্টেটগুলি এখন টাইপ সিস্টেম এবং কম্পাইল করার সময় টাইপ চেকিংয়ের কারণে অসম্ভব! এটি নিশ্চিত করে যে কিছু বাগ, যেমন একটি আনপাবলিশড পোস্টের কনটেন্ট প্রদর্শন, প্রোডাকশনে যাওয়ার আগেই ধরা পড়বে।

Listing 18-21-এর পরে blog ক্রেটে এই বিভাগের শুরুতে প্রস্তাবিত কাজগুলি চেষ্টা করুন যাতে আপনি কোডের এই সংস্করণের ডিজাইন সম্পর্কে কী ভাবেন তা দেখতে পারেন। লক্ষ্য করুন যে এই ডিজাইনে কিছু কাজ ইতিমধ্যেই সম্পন্ন হতে পারে।

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

এরপর, আমরা প্যাটার্নগুলি দেখব, যা Rust-এর আরেকটি বৈশিষ্ট্য যা প্রচুর নমনীয়তা সক্ষম করে। আমরা পুরো বই জুড়ে সংক্ষেপে সেগুলি দেখেছি কিন্তু এখনও তাদের সম্পূর্ণ ক্ষমতা দেখিনি। চলুন শুরু করা যাক!