অবজেক্ট-ওরিয়েন্টেড ডিজাইন প্যাটার্ন বাস্তবায়ন করা (Implementing an Object-Oriented Design Pattern)
স্টেট প্যাটার্ন হল একটি অবজেক্ট-ওরিয়েন্টেড ডিজাইন প্যাটার্ন। এই প্যাটার্নের মূল বিষয় হল, আমরা স্টেটগুলির একটি সেট সংজ্ঞায়িত করি যা একটি ভ্যালুর অভ্যন্তরীণভাবে থাকতে পারে। স্টেটগুলি স্টেট অবজেক্ট-এর একটি সেট দ্বারা উপস্থাপিত হয় এবং ভ্যালুর আচরণ তার স্টেটের উপর ভিত্তি করে পরিবর্তিত হয়। আমরা একটি ব্লগ পোস্ট স্ট্রাক্টের উদাহরণ নিয়ে কাজ করব যাতে একটি ফিল্ড থাকবে যা তার স্টেট ধারণ করবে, যেটি "ড্রাফট", "রিভিউ" বা "পাবলিশড" সেট থেকে একটি স্টেট অবজেক্ট হবে।
স্টেট অবজেক্টগুলি কার্যকারিতা শেয়ার করে: অবশ্যই, Rust-এ আমরা অবজেক্ট এবং ইনহেরিটেন্সের পরিবর্তে স্ট্রাক্ট এবং ট্রেইট ব্যবহার করি। প্রতিটি স্টেট অবজেক্ট তার নিজস্ব আচরণের জন্য এবং কখন এটি অন্য স্টেটে পরিবর্তিত হওয়া উচিত তা নিয়ন্ত্রণ করার জন্য দায়ী। যে ভ্যালুটি একটি স্টেট অবজেক্ট ধারণ করে সেটি স্টেটগুলির বিভিন্ন আচরণ বা কখন স্টেটগুলির মধ্যে ট্রানজিশন করতে হবে সে সম্পর্কে কিছুই জানে না।
স্টেট প্যাটার্ন ব্যবহারের সুবিধা হল, যখন প্রোগ্রামের ব্যবসার প্রয়োজনীয়তা পরিবর্তন হয়, তখন আমাদের স্টেট ধারণ করা ভ্যালুর কোড বা ভ্যালু ব্যবহার করা কোড পরিবর্তন করার প্রয়োজন হবে না। আমাদের কেবল স্টেট অবজেক্টগুলির মধ্যে একটির ভিতরের কোড আপডেট করতে হবে যাতে এর নিয়মগুলি পরিবর্তন করা যায় বা সম্ভবত আরও স্টেট অবজেক্ট যুক্ত করা যায়।
প্রথমে, আমরা আরও ঐতিহ্যবাহী অবজেক্ট-ওরিয়েন্টেড উপায়ে স্টেট প্যাটার্নটি বাস্তবায়ন করব, তারপর আমরা এমন একটি পদ্ধতি ব্যবহার করব যা Rust-এ আরও কিছুটা স্বাভাবিক। আসুন স্টেট প্যাটার্ন ব্যবহার করে একটি ব্লগ পোস্ট ওয়ার্কফ্লো ক্রমবর্ধমানভাবে বাস্তবায়ন করি।
চূড়ান্ত কার্যকারিতা দেখতে এইরকম হবে:
- একটি ব্লগ পোস্ট একটি খালি ড্রাফট হিসাবে শুরু হয়।
- যখন ড্রাফট সম্পন্ন হয়, তখন পোস্টের একটি রিভিউ রিকোয়েস্ট করা হয়।
- যখন পোস্টটি অ্যাপ্রুভ করা হয়, তখন এটি পাবলিশ করা হয়।
- শুধুমাত্র পাবলিশড ব্লগ পোস্টগুলি প্রিন্ট করার জন্য কনটেন্ট রিটার্ন করে, তাই আনঅ্যাপ্রুভড পোস্টগুলি দুর্ঘটনাক্রমে পাবলিশ করা যায় না।
একটি পোস্টে অন্য কোনো পরিবর্তন করার চেষ্টা করা হলে তার কোনো প্রভাব থাকা উচিত নয়। উদাহরণস্বরূপ, যদি আমরা একটি রিভিউ রিকোয়েস্ট করার আগে একটি ড্রাফ্ট ব্লগ পোস্ট অ্যাপ্রুভ করার চেষ্টা করি, তাহলে পোস্টটি আনপাবলিশড ড্রাফ্ট থাকা উচিত।
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 of
content`)
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-এর আরেকটি বৈশিষ্ট্য যা প্রচুর নমনীয়তা সক্ষম করে। আমরা পুরো বই জুড়ে সংক্ষেপে সেগুলি দেখেছি কিন্তু এখনও তাদের সম্পূর্ণ ক্ষমতা দেখিনি। চলুন শুরু করা যাক!