نمط Builder
أنشئ الكائنات خطوة بخطوة
نمط Builder درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
لماذا نستخدم Builder؟
لا تحتوي Rust على وسيطات مسمّاة أو اختيارية للدوال. وعندما يحتوي struct على العديد من الحقول، ولا سيما الحقول الاختيارية، يصبح المُنشئ الذي يستقبل ثمانية معاملات موضعية صعب القراءة وعرضة للأخطاء.
يحل نمط Builder هذه المشكلة. إذ تضبطون الكائن خطوةً خطوةً باستخدام دوال مسمّاة، ثم تستدعون build() النهائية لإنتاج القيمة. ويصبح الكود مقروءًا كأنه جملة سلسة.
البنية الهدف
ابدؤوا بالنوع الذي تريدون إنشاؤه فعليًا. يحمل إعداد الخادم هنا مضيفًا مطلوبًا، إلى جانب عدة خيارات اختيارية.
لاحظوا أن الحقول خاصة، وذلك لتشجيع الإنشاء من خلال Builder بدلًا من القيم الحرفية للبنية.
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
use_tls: bool,
}نوع Builder منفصل
يستخدم الأسلوب التقليدي struct ثانيةً تُسمّى Builder. وهي تحاكي النوع الهدف، لكنها تخزّن الحالة قيد الإنشاء. وغالبًا ما تصبح الحقول الاختيارية من النوع Option<T> حتى تتمكنوا من التمييز بين عدم التعيين والتعيين الصريح.
pub struct ServerConfigBuilder {
host: String,
port: Option<u16>,
max_connections: Option<usize>,
use_tls: bool,
}بدء Builder
امنحوا Builder مُنشئًا لا يستقبل سوى الحقول المطلوبة. وتبدأ جميع الحقول الاختيارية بالقيمة None أو بقيمة افتراضية.
ومن الاصطلاحات الشائعة إضافة دالة builder() إلى النوع الهدف، بحيث تعيد Builder.
impl ServerConfig {
pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
ServerConfigBuilder {
host: host.into(),
port: None,
max_connections: None,
use_tls: false,
}
}
}تستقبل دوال الإسناد self بالقيمة
تستهلك كل دالة إسناد self، وتعدّل حقلًا، ثم تعيد self. ويسمح إرجاع القيمة المالكة بربط الاستدعاءات بأسلوب سلس.
يُعد هذا الربط القائم على الملكية أسلوب Rust الاصطلاحي، كما أنه يتجنب مشكلات أعمار القيم.
impl ServerConfigBuilder {
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn use_tls(mut self, yes: bool) -> Self {
self.use_tls = yes;
self
}
}تطبيق القيم الافتراضية في build()
تحوّل build() النهائية Builder إلى النوع الحقيقي. وهنا تملؤون القيم الافتراضية لأي شيء ما زال None باستخدام unwrap_or.
impl ServerConfigBuilder {
pub fn build(self) -> ServerConfig {
ServerConfig {
host: self.host,
port: self.port.unwrap_or(8080),
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls,
}
}
}الإنشاء السلس
يصبح الإنشاء الآن مقروءًا من الأعلى إلى الأسفل. تُمرَّر البيانات المطلوبة إلى builder()، ويكون كل تعديل اختياري استدعاءً واضح الاسم.
أما الحقول التي تتجاوزونها فتأخذ قيمها الافتراضية تلقائيًا.
fn main() {
let cfg = ServerConfig::builder("localhost")
.port(9000)
.use_tls(true)
.build();
println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}مثال كامل قابل للتشغيل
إليكم النمط كاملًا ومختصرًا في برنامج واحد يمكنكم تشغيله. يوضح المثال أن الحقول التي تتجاوزونها تعود إلى القيم الافتراضية داخل build().
struct Config { name: String, retries: u32 }
struct Builder { name: String, retries: Option<u32> }
impl Config {
fn builder(name: &str) -> Builder {
Builder { name: name.to_string(), retries: None }
}
}
impl Builder {
fn retries(mut self, n: u32) -> Self { self.retries = Some(n); self }
fn build(self) -> Config {
Config { name: self.name, retries: self.retries.unwrap_or(3) }
}
}
fn main() {
let c = Config::builder("job").build();
println!("{} retries={}", c.name, c.retries);
}إنشاء قابل للفشل باستخدام Result
قد يكون الإعداد غير صالح أحيانًا، مثل أن يكون المنفذ صفرًا. اجعلوا build() تعيد Result حتى تظهر أخطاء التحقق كأخطاء قابلة للاسترداد بدلًا من حالات الذعر.
impl ServerConfigBuilder {
pub fn try_build(self) -> Result<ServerConfig, String> {
let port = self.port.unwrap_or(8080);
if port == 0 {
return Err("port must be non-zero".into());
}
Ok(ServerConfig { host: self.host, port,
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls })
}
}حزمة derive_builder
تُعد كتابة Builders يدويًا أمرًا متكررًا. تنشئ حزمة derive_builder Builder كاملًا انطلاقًا من annotation.
تضعون annotation على الحقول مع القيم الافتراضية، وتحصلون مجانًا على FooBuilder مُنشأ، مع دوال إسناد وbuild() قابلة للفشل.
use derive_builder::Builder;
#[derive(Builder)]
struct Channel {
#[builder(default = "8080")]
port: u16,
name: String,
}Builders المملوكة مقابل Builders ذات المرجع القابل للتعديل
يوجد أسلوبان. يستهلك الأسلوب المملوك self ويسمح بالربط بصورة طبيعية. أما أسلوب &mut self فيعيد &mut Self ويتيح تقسيم عملية الإنشاء على عدة عبارات من دون إعادة الإسناد.
يُعد الأسلوب المملوك أكثر شيوعًا للإنشاء مرة واحدة، بينما يناسب الأسلوب القابل للتعديل الإعداد الشرطي داخل الحلقات.
impl ServerConfigBuilder {
pub fn port_ref(&mut self, port: u16) -> &mut Self {
self.port = Some(port);
self
}
}تحقق سريع
اختبروا مدى فهمكم لأسلوب Builder الذي يملك self.
مراجعة
يتعامل نمط Builder مع افتقار Rust إلى الوسيطات الاختيارية. يحتفظ نوع Builder بالحالة قيد الإنشاء، وتستهلك دوال الإسناد self وتعيده لتمكين الربط، بينما تطبّق build() القيم الافتراضية لإنتاج القيمة النهائية.
استخدموا Result من build() للتحقق، واستعينوا بـ derive_builder لتجنب التعليمات البرمجية المتكررة.
الأسئلة الشائعة
هل درس «نمط Builder» مجاني؟
نعم — نص درس «نمط Builder» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
ماذا ستتعلم في «نمط Builder»؟
أنشئ الكائنات خطوة بخطوة تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟
لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «نمط Builder»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟
نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.