C# Geeks (.NET) – Telegram
💡 اصول طراحی GRASP در تحلیل و طراحی شیءگرا (OOAD)
Object-Oriented Analysis and Design

در تحلیل و طراحی شیءگرا (OOAD)، الگوهای GRASP (General Responsibility Assignment Software Patterns) نقش حیاتی در طراحی نرم‌افزارهای مؤثر و قابل نگهداری دارند.
GRASP
مجموعه‌ای از راهنماها برای تعیین مسئولیت کلاس‌ها و اشیاء ارائه می‌دهد تا وابستگی کم، انسجام بالا و پایداری سیستم حفظ شود.

اصول اصلی GRASP:

1️⃣ Creator (ایجادگر):
مسئولیت ایجاد نمونه‌های یک کلاس را به کلاسی بدهید که بیشترین دانش درباره زمان و نحوه ایجاد آن‌ها را دارد.

2️⃣ Information Expert (کارشناس اطلاعات):
مسئولیت را به کلاسی اختصاص دهید که اطلاعات لازم برای انجام آن را دارد. این کار انسجام بالا و وابستگی کم را تقویت می‌کند.

3️⃣ Low Coupling (وابستگی کم):
هدف این است که کلاس‌ها کمترین وابستگی را به یکدیگر داشته باشند، تا نگهداری آسان‌تر و انعطاف‌پذیری سیستم افزایش یابد.

4️⃣ High Cohesion (انسجام بالا):
اطمینان حاصل کنید مسئولیت‌های یک کلاس مرتبط و متمرکز باشند. این باعث خوانایی، نگهداری و قابلیت استفاده مجدد بهتر می‌شود.

5️⃣ Controller (کنترل‌کننده):
مسئولیت مدیریت رویدادهای سیستم یا هماهنگی فعالیت‌ها را به یک کلاس کنترل‌کننده بسپارید تا کنترل متمرکز داشته باشید و کلاس‌ها شلوغ نشوند.

6️⃣ Pure Fabrication (ساخت مصنوعی):
کلاس‌های جدیدی ایجاد کنید تا مسئولیت‌ها را بدون نقض انسجام و وابستگی انجام دهند و طراحی تمیز و قابل نگهداری داشته باشید.

7️⃣ Indirection (واسطه‌گری):
از واسطه‌ها یا انتزاع‌ها برای کاهش وابستگی بین کلاس‌ها و افزایش انعطاف‌پذیری طراحی استفاده کنید.

8️⃣ Polymorphism (چندریختی):
از ارث‌بری و اینترفیس‌ها استفاده کنید تا چندین پیاده‌سازی برای رفتارها ایجاد شود و سیستم انعطاف‌پذیر و توسعه‌پذیر باشد.

خلاصه:

با اعمال این اصول GRASP، توسعه‌دهندگان می‌توانند طراحی شیءگرای مقاوم، قابل نگهداری و تطبیق‌پذیر با تغییرات نیازمندی‌ها ایجاد کنند.

💡 اهمیت اصول GRASP در تحلیل و طراحی شیءگرا (OOAD)

در تحلیل و طراحی شیءگرا (OOAD)، اصول GRASP اهمیت ویژه‌ای دارند زیرا چارچوبی برای طراحی سیستم‌ها با شفافیت، انعطاف‌پذیری و قابلیت نگهداری بالا ارائه می‌کنند. در ادامه دلایل اهمیت آن‌ها آمده است:

📝 شفافیت طراحی

اصول GRASP کمک می‌کنند تا کلاس‌ها و مسئولیت‌ها به گونه‌ای سازماندهی شوند که طراحی سیستم قابل فهم‌تر باشد. مسئولیت‌های مشخص و واضح برای هر کلاس باعث می‌شود توسعه‌دهندگان معماری سیستم را سریع‌تر درک کنند.

🔗 وابستگی کم و انسجام بالا

اصول GRASP تشویق می‌کند کلاس‌ها وابستگی کمی به یکدیگر داشته باشند. این موضوع باعث می‌شود کد ماژولار و قابل استفاده مجدد باشد. همچنین، انسجام بالا تضمین می‌کند که هر کلاس هدف مشخص و متمرکزی دارد و سیستم راحت‌تر نگهداری و تغییر می‌کند.

⚡️ طراحی انعطاف‌پذیر

با پیروی از اصولی مانند واسطه‌گری (Indirection) و چندریختی (Polymorphism)، طراحی سیستم انعطاف‌پذیرتر و قابل تطبیق با تغییرات می‌شود. واسطه‌گری امکان معرفی موجودیت‌های میانی را فراهم می‌کند که تعاملات پیچیده را ساده‌تر می‌کند، و چندریختی اجازه می‌دهد چندین پیاده‌سازی برای رفتارها استفاده شود و قابلیت توسعه بهبود یابد.

📈 مقیاس‌پذیری

اصول GRASP با ترویج طراحی‌ای که می‌تواند تغییرات و بهبودهای آینده را بدون بازنویسی گسترده بپذیرد، به مقیاس‌پذیری سیستم کمک می‌کنند. این مقیاس‌پذیری برای رشد و توسعه طولانی‌مدت سیستم‌ها حیاتی است.

🛠 سهولت نگهداری

با اختصاص مسئولیت‌های واضح به کلاس‌ها و تعریف روابط روشن بین آن‌ها، نگهداری سیستم ساده‌تر می‌شود. توسعه‌دهندگان می‌توانند سریعاً مکان تغییرات را شناسایی کرده و بدون تأثیر ناخواسته بر سایر بخش‌ها، تغییرات را اعمال کنند.

🔄 افزایش قابلیت استفاده مجدد

اصول GRASP ایجاد کلاس‌ها و اشیایی با مسئولیت‌ها و رابط‌های مشخص را تشویق می‌کنند. این باعث می‌شود کد قابل استفاده مجدد باشد و اجزا به راحتی در بخش‌های مختلف سیستم یا پروژه‌های جدید استفاده شوند، که بهره‌وری را افزایش داده و زمان توسعه را کاهش می‌دهد.
📚 اصول GRASP و مثال‌های آن در OOAD

GRASP (General Responsibility Assignment Software Patterns)
مجموعه‌ای از دستورالعمل‌ها در تحلیل و طراحی شیءگرا (OOAD) هستند که به ما کمک می‌کنند مسئولیت‌ها را به کلاس‌ها و اشیا به‌صورت مؤثر تخصیص دهیم. در ادامه هر اصل را با یک مثال کاربردی بررسی می‌کنیم:

1️⃣ Creator – ایجاد کننده

مسئولیت ایجاد نمونه‌های یک کلاس را به خود کلاس یا یک کلاس کارخانه مرتبط واگذار کنید.

🔹️مثال:
در سیستم مدیریت کتابخانه، وقتی کتاب جدیدی اضافه می‌شود، یک شیء Book باید ایجاد شود. این مسئولیت می‌تواند به کلاس Library یا یک کلاس جداگانه مانند BookFactory سپرده شود تا منطق ایجاد شیء Book متمرکز و قابل مدیریت باشد.

2️⃣ Information Expert – کارشناس اطلاعات

مسئولیت را به کلاسی واگذار کنید که اطلاعات لازم برای انجام آن مسئولیت را دارد.

🔹️مثال:
هنگام امانت گرفتن کتاب، بررسی موجود بودن کتاب باید توسط کلاس Book انجام شود. کلاس Book اطلاعات وضعیت موجودی خود را دارد و می‌تواند بدون وابستگی به کلاس‌های دیگر این بررسی را انجام دهد. این کار انسجام بالا و کاهش وابستگی را تضمین می‌کند.

3️⃣ Low Coupling – وابستگی کم

کلاس‌ها باید حداقل وابستگی را به یکدیگر داشته باشند.

🔹️مثال:
در سیستم مدیریت کتابخانه، کلاس LibraryCatalog مسئول مدیریت کاتالوگ کتاب‌هاست. به جای دسترسی مستقیم به کلاس Book برای بررسی موجودی، می‌تواند از یک رابط (interface) مانند Searchable استفاده کند. این کار باعث می‌شود LibraryCatalog وابستگی کمتری به کلاس Book داشته باشد و نگهداری سیستم آسان‌تر شود.

4️⃣ High Cohesion – انسجام بالا

مسئولیت‌های درون یک کلاس باید مرتبط و متمرکز باشند.

🔹️مثال:
کلاس Book باید مسئولیت‌های مرتبط با جزئیات کتاب مانند عنوان، نویسنده و موجودی را مدیریت کند. وظایف نامرتبط مثل احراز هویت کاربر باید توسط کلاس‌های جداگانه مدیریت شوند تا هر کلاس تمرکز مشخصی داشته باشد و سیستم قابل نگهداری باشد.

5️⃣ Controller – کنترل‌کننده

مسئولیت مدیریت رویدادهای سیستم یا هماهنگی فعالیت‌ها را به یک کلاس کنترل‌کننده واگذار کنید.

🔹️مثال:
در یک اپلیکیشن وب کتابخانه، زمانی که کاربر درخواست امانت کتاب می‌دهد، کلاس BorrowBookController مسئول مدیریت درخواست و هماهنگی با کلاس‌های Book، User و Library است. این کار باعث تمرکز منطق کنترل و سازماندهی بهتر سیستم می‌شود.

6️⃣ Pure Fabrication – ساختگی خالص

کلاس‌های جدیدی معرفی کنید تا مسئولیت‌ها را بدون نقض انسجام و وابستگی کم انجام دهند.

🔹️مثال:
برای ارسال ایمیل هنگام امانت یا بازگشت کتاب، به جای افزودن منطق ایمیل به کلاس‌های Book یا User، کلاس جداگانه NotificationService ایجاد می‌کنیم. این کلاس مسئولیت خالص ارسال ایمیل را برعهده دارد و انسجام و وابستگی کم را حفظ می‌کند.

7️⃣ Indirection – واسطه‌گری

از واسطه‌ها یا انتزاع‌ها برای کاهش وابستگی کلاس‌ها و افزایش انعطاف‌پذیری طراحی استفاده کنید.

🔹️مثال:
اگر چندین کلاس نیاز به دسترسی به اطلاعات کتاب دارند، می‌توان یک رابط BookRepository معرفی کرد. کلاس‌ها به جای دسترسی مستقیم به Book، از این رابط استفاده کنند تا انعطاف‌پذیری و قابلیت تغییر آینده آسان‌تر شود.

8️⃣ Polymorphism – چندریختی

از ارث‌بری و اینترفیس‌ها برای پیاده‌سازی چندین رفتار استفاده کنید.

🔹️مثال:
فرض کنید کتاب‌ها به دو نوع FictionBook و NonFictionBook تقسیم شوند و هر کدام قوانین امانت خاص خود را داشته باشند. با تعریف یک اینترفیس مشترک Book و پیاده‌سازی آن در کلاس‌های مختلف، چندریختی اجازه می‌دهد فرآیند امانت به‌طور یکنواخت و مستقل از نوع کتاب مدیریت شود و استفاده مجدد کد ساده‌تر شود.

🔖هشتگ‌ها:
#GRASP #OOAD #ObjectOrientedDesign #LowCoupling #HighCohesion
استفاده از Static class یا Singleton ؟ یا می‌توان بهتر عمل کرد؟ 🤔

وقتی می‌خواستم به شخصی در مورد این سؤال مشاوره بدهم، احساس دوگانگی عجیبی داشتم.

دو گزینه‌ای که او مطرح کرده بود این‌ها بودند:

1️⃣ استفاده از یک کلاس استاتیک برای اتصال به پایگاه داده

2️⃣ استفاده از یک سینگلتون برای اتصال به پایگاه داده

واقعیت این است که من هیچ‌کدام از این دو گزینه را واقعاً دوست ندارم... اما می‌خواستم شرایط مطرح‌شده را در نظر بگیرم. به نظر می‌رسید این پروژه، از آن دسته اپلیکیشن‌هایی نبود که نیاز به مقیاس‌پذیری داشته باشد؛ بیشتر شبیه یک پروژه سرگرمی یا چیزی با چرخه توسعه کوتاه‌مدت بود.

این موضوع برای من همیشه کمی سخت است که با آن کنار بیایم، چون معمولاً سعی می‌کنم سیستم‌ها را طوری طراحی کنم که قابل توسعه و ماندگار باشند. اما این محدودیت‌ها وجود داشت و این هم تنها گزینه‌های مطرح‌شده بودند.

در این شرایط، من سینگلتون را انتخاب می‌کنم — و حتی نوشتن این جمله هم برایم کمی ناخوشایند است 😅

حقیقت این است که استفاده از یک کلاس استاتیک برای چیزی که حالت (State) دارد، می‌تواند خیلی راحت منجر به بروز باگ شود؛ چون عملاً هیچ کنترلی بر اینکه چه کسی می‌تواند به آن کلاس دسترسی داشته باشد، ندارید.
در مورد سینگلتون هم می‌توان همین استدلال را مطرح کرد، اما نکته‌ای که می‌خواهم اضافه کنم این است که سینگلتون ذاتاً نیازی ندارد که از همه‌جا قابل دسترسی باشد… فقط اغلب به این شکل پیاده‌سازی می‌شود.

هدف از سینگلتون این است که اطمینان حاصل کنیم بیش از یک نمونه از یک شئ ساخته نمی‌شود. اما برای رسیدن به این هدف معمولاً به این معنی است که آن شئ از همه‌جا «قابل دیدن» خواهد بود… معمولاً همین‌طور است.

به نظر من، شروع با سینگلتون به شما یک نمونه (Instance) می‌دهد که می‌توانید دسترسی به آن را به‌تدریج محدود کنید. می‌توانید شروع کنید به ارسال آن از طریق سازنده‌ها (Constructors). می‌توانید کم‌کم از تکیه به ویژگی سراسری (Global Instance Property) فاصله بگیرید.

در واقع، این کار یک قدم شما را به سمت طراحی بهتر نزدیک‌تر می‌کند؛ یعنی جایی که در ابتدای برنامه، یک نمونه از شئ می‌سازید و آن را به قسمت‌های مختلف پاس می‌دهید. خوشبختانه بسیاری از فریم‌ورک‌های تزریق وابستگی (Dependency Injection) این روند را برای ما بسیار خوب مدیریت می‌کنند.

پس در نهایت، هیچ‌کدام از این دو گزینه انتخاب مورد علاقه من نیستند، اما سینگلتون به‌نظر من «شرّ کمتر» است (هرچند خیلی جزئی!) و ما را کمی به سمت الگوهای طراحی‌ای که ترجیح می‌دهم، پیش می‌برد.
چه زیبا فرمودن😊
🔒 امنیت لایه انتقال (Transport Layer Security - TLS) چیست؟

(TLS)
یا Transport Layer Security یک پروتکل امنیتی پرکاربرد است که برای ایجاد حریم خصوصی و امنیت داده‌ها در ارتباطات اینترنتی طراحی شده است.
یکی از کاربردهای اصلی TLS، رمزنگاری ارتباط بین برنامه‌های وب و سرورها است — مانند زمانی که یک مرورگر وب، وب‌سایتی را بارگذاری می‌کند.

علاوه بر وب، TLS همچنین می‌تواند برای رمزنگاری سایر ارتباطات مانند ایمیل ✉️، پیام‌رسانی 💬 و تماس‌های صوتی از طریق اینترنت (VoIP 📞) مورد استفاده قرار گیرد.
در این مقاله، تمرکز ما بر نقش TLS در امنیت برنامه‌های تحت وب خواهد بود.

🌐 تاریخچه‌ی TLS

(TLS)
توسط سازمان بین‌المللی Internet Engineering Task Force (IETF) پیشنهاد شد. اولین نسخه از این پروتکل در سال ۱۹۹۹ منتشر گردید.
جدیدترین نسخه، TLS 1.3 است که در سال ۲۰۱۸ به انتشار رسید و در حال حاضر به عنوان نسخه‌ی استاندارد و امن مورد استفاده قرار می‌گیرد.

🧩 تفاوت TLS و SSL چیست؟

(TLS)
در واقع نسخه‌ی تکامل‌یافته‌ی پروتکل رمزنگاری قبلی به نام SSL (Secure Sockets Layer) است که توسط شرکت Netscape توسعه یافته بود.
جالب است بدانید نسخه‌ی TLS 1.0 در ابتدا به عنوان SSL 3.1 در حال توسعه بود، اما پیش از انتشار، نام آن به TLS تغییر یافت تا نشان دهد دیگر متعلق به Netscape نیست.

به دلیل این پیشینه، بسیاری از افراد هنوز هم اصطلاحات TLS و SSL را به جای یکدیگر به کار می‌برند، در حالی که در واقع TLS نسخه‌ی جدیدتر و امن‌تر SSL است.

🌍 تفاوت TLS و HTTPS چیست؟

HTTPS
در واقع پیاده‌سازی TLS روی پروتکل HTTP است — یعنی همان پروتکلی که همه‌ی وب‌سایت‌ها و بسیاری از سرویس‌های وب از آن استفاده می‌کنند.
به زبان ساده، هر وب‌سایتی که از HTTPS استفاده می‌کند، در واقع از رمزنگاری TLS برای ایمن‌سازی ارتباطات خود بهره می‌برد.

🏢 چرا کسب‌وکارها و برنامه‌های وب باید از TLS استفاده کنند؟

استفاده از رمزنگاری TLS می‌تواند از برنامه‌های وب در برابر نشت داده‌ها، حملات سایبری و شنود ارتباطات محافظت کند.
امروزه استفاده از HTTPS (بر پایه TLS) به یک استاندارد جهانی برای وب‌سایت‌ها تبدیل شده است.

مرورگر Google Chrome به تدریج وب‌سایت‌های بدون HTTPS را ناامن معرفی کرده و سایر مرورگرها نیز همین سیاست را در پیش گرفته‌اند.
کاربران امروزی نیز هنگام مشاهده‌ی وب‌سایت‌ها، تنها زمانی احساس اطمینان می‌کنند که آیکون قفل 🔒 (padlock) در نوار آدرس مرورگرشان دیده شود.
🔐 TLS دقیقاً چه کاری انجام می‌دهد؟

پروتکل Transport Layer Security (TLS) سه وظیفه‌ی اصلی دارد که هرکدام نقش حیاتی در امنیت ارتباطات اینترنتی ایفا می‌کنند:

1️⃣ رمزنگاری (Encryption)
🔸 داده‌های در حال انتقال را از دید اشخاص ثالث پنهان می‌کند.
🔸 به‌عبارتی، حتی اگر کسی داده‌ها را در مسیر شنود کند، نمی‌تواند محتوای آن را بخواند.

2️⃣ احراز هویت (Authentication)
🔸 تضمین می‌کند طرفین ارتباط (مانند مرورگر و سرور) همان کسانی هستند که ادعا می‌کنند.
🔸 این کار از حملاتی مثل Man-in-the-Middle جلوگیری می‌کند.

3️⃣ یکپارچگی داده (Integrity)
🔸 بررسی می‌کند که داده‌ها در مسیر انتقال دستکاری یا جعل نشده باشند.
🔸 این تضمین می‌کند که اطلاعات دقیقاً به همان شکلی که ارسال شده، دریافت می‌شوند.

📜 گواهی TLS چیست؟

برای اینکه یک وب‌سایت یا برنامه بتواند از TLS استفاده کند، باید یک گواهی TLS بر روی سرور اصلی (Origin Server) نصب شده باشد.
این گواهی که اغلب به اشتباه SSL Certificate نیز نامیده می‌شود، توسط مرجع صدور گواهی (Certificate Authority - CA) به مالک دامنه صادر می‌گردد.

🔹 این گواهی شامل اطلاعات زیر است:

• مشخصات مالک دامنه 🌍
• کلید عمومی سرور (Public Key) 🔑

هر دو مورد برای تأیید هویت سرور ضروری هستند تا مرورگر کاربر مطمئن شود که با سرور اصلی در ارتباط است، نه یک سرور جعلی.

⚙️ TLS چگونه کار می‌کند؟

هنگامی که کاربر وارد وب‌سایتی با TLS می‌شود، فرآیندی به نام TLS Handshake آغاز می‌گردد.
این فرآیند بین دستگاه کاربر (Client) و سرور وب انجام می‌شود تا ارتباطی امن برقرار گردد.

در طول TLS Handshake موارد زیر اتفاق می‌افتد 👇

1️⃣ انتخاب نسخه‌ی TLS (مثلاً TLS 1.2 یا TLS 1.3)
2️⃣ توافق روی مجموعه‌ی رمزنگاری یا Cipher Suite
3️⃣ احراز هویت سرور با استفاده از گواهی TLS
4️⃣ تولید کلیدهای نشست (Session Keys) برای رمزنگاری پیام‌ها پس از اتمام Handshake

🔢 Cipher Suite چیست؟

Cipher Suite
مجموعه‌ای از الگوریتم‌هاست که تعیین می‌کند در این ارتباط از چه روش‌های رمزنگاری و چه کلیدهایی استفاده شود.
TLS
با استفاده از فناوری رمزنگاری کلید عمومی (Public Key Cryptography) می‌تواند این کلیدها را حتی از طریق یک کانال رمزنگاری‌نشده، به‌صورت ایمن تنظیم کند.

🧩 احراز هویت از طریق کلیدهای عمومی

در این مرحله، معمولاً سرور هویت خود را به کاربر ثابت می‌کند.
این کار با استفاده از کلید عمومی (Public Key) انجام می‌شود.

🔸 هر کسی می‌تواند داده‌هایی را که با کلید خصوصی سرور رمزگذاری شده، با کلید عمومی رمزگشایی کند و از اصالت آن اطمینان یابد.
🔸 اما تنها خود سرور (دارنده‌ی کلید خصوصی) می‌تواند داده‌های جدیدی را رمز کند.

کلید عمومی سرور در گواهی TLS آن ذخیره می‌شود.

بررسی تمامیت داده‌ها (Integrity Check)

پس از رمزنگاری و احراز هویت، داده‌ها با کد تأیید پیام (Message Authentication Code - MAC) امضا می‌شوند.
گیرنده می‌تواند با بررسی MAC اطمینان یابد که داده‌ها تغییر نکرده‌اند.

💊 مثال جالب:
این شبیه پلمب آلومینیومی روی بطری قرص‌هاست — وقتی سالم است، مطمئن می‌شوید کسی محتویات آن را دستکاری نکرده است.

📘 جمع‌بندی

TLS
یک لایه‌ی امنیتی حیاتی در ارتباطات اینترنتی است که با رمزنگاری داده‌ها، از حریم خصوصی کاربران و امنیت اطلاعات محافظت می‌کند.
امروزه، هیچ وب‌سایتی بدون TLS امن تلقی نمی‌شود. 🌐

🔖هشتگ‌ها:
#TLS #HTTPS #CyberSecurity #NetworkSecurity #WebDevelopment
🚀 الگوریتم Leaky Bucket

🔹 تعریف:
الگوریتم Leaky Bucket یکی از الگوریتم‌های Traffic Shaping (شکل‌دهی به ترافیک شبکه) است که وظیفه دارد جریان داده‌ها در شبکه را کنترل و هموارسازی (regulate) کند.

در این روش، بسته‌های ورودی (packets) در یک بافر با اندازه‌ی ثابت (سطل) ذخیره می‌شوند و سپس با نرخ ثابتی از سطل خارج و به شبکه ارسال می‌گردند.
اگر بافر پر شود، بسته‌های اضافی دور انداخته می‌شوند (discarded).

📊 ویژگی‌ها:

هموارسازی ترافیک ناگهانی (bursty traffic) با ارسال در نرخ ثابت
حذف بسته‌های اضافی در صورت پر شدن بافر
مثال: اگر هاستی در حالت عادی با نرخ ۳ Mbps متعهد است، الگوریتم تضمین می‌کند حتی در زمان ارسال ناگهانی داده‌ها، نرخ خروجی از ۳ Mbps بیشتر نشود.

⚙️ نحوه‌ی کار الگوریتم Leaky Bucket:

الگوریتم Leaky Bucket معمولاً از یک صف (First In, First Out) FIFOبرای مدیریت بسته‌ها استفاده می‌کند.

🔸 برای بسته‌های با اندازه‌ی ثابت:
در هر تیک ساعت (Clock Tick)، تعداد ثابتی از بسته‌ها از صف حذف و ارسال می‌شوند.

🔸 برای بسته‌های با اندازه‌ی متغیر (Variable-size packets):
ارسال داده‌ها بر اساس یک نرخ ثابت بر حسب بایت یا بیت در ثانیه انجام می‌شود.

🧠 شبه‌کد الگوریتم برای بسته‌های با طول متغیر:

1️⃣ مقدار شمارنده (counter) را در هر تیک ساعت برابر n مقداردهی کن.
2️⃣ تا زمانی که n بزرگ‌تر از اندازه‌ی بسته‌ی موجود در سر صف است:
• بسته‌ای از ابتدای صف خارج کن (P)
• بسته P را به شبکه ارسال کن
• شمارنده را به اندازه‌ی سایز بسته کاهش بده
3️⃣ در تیک بعدی ساعت، مقدار شمارنده را مجدداً برابر n قرار بده و مرحله 1 را تکرار کن.


📦 مثال عددی:
فرض کنید:
n = 1000
و اندازه‌ی بسته‌های موجود در صف به ترتیب:
200, 400, 450 بایت هستند.

🔹 مرحله 1️⃣:
چون n > 200 → بسته ۲۰۰ بایتی ارسال می‌شود.
n = 1000 - 200 = 800

🔹 مرحله 2️⃣:
چون n > 400 → بسته ۴۰۰ بایتی ارسال می‌شود.
n = 800 - 400 = 400

🔹 مرحله 3️⃣:
چون n < 450 → الگوریتم در این تیک متوقف می‌شود.

در تیک بعدی ساعت، n مجدداً برابر ۱۰۰۰ شده و فرآیند از ابتدا تکرار می‌شود تا زمانی که همه‌ی بسته‌ها ارسال شوند.


🚀 Leaky Bucket Algorithm – C# Implementation

در ادامه‌ی توضیح الگوریتم، اینجا پیاده‌سازی کامل اون رو در زبان #C می‌بینیم 👇
💻 کد:
// C# Implementation of Leaking Bucket Algorithm
using System;

class LeakingBucket
{
static void Main(string[] args)
{
int no_of_queries, storage, output_pkt_size;
int input_pkt_size, bucket_size, size_left;

// Initial packets in the bucket
storage = 0;

// Total number of times bucket content is checked
no_of_queries = 4;

// Total number of packets that can be accommodated in the bucket
bucket_size = 10;

// Number of packets that enter the bucket at a time
input_pkt_size = 4;

// Number of packets that exit the bucket at a time
output_pkt_size = 1;

for (int i = 0; i < no_of_queries; i++)
{
size_left = bucket_size - storage; // space left in the bucket

if (input_pkt_size <= size_left)
{
storage += input_pkt_size;
}
else
{
Console.WriteLine("Packet loss = " + input_pkt_size);
}

Console.WriteLine($"Buffer size = {storage} out of bucket size = {bucket_size}");

// Sending packets out of the bucket
storage -= output_pkt_size;
}
}
}


🚀 Leaky Bucket Algorithm – Output & Comparison

💻 خروجی برنامه:
Buffer size= 4 out of bucket size= 10  
Buffer size= 7 out of bucket size= 10
Buffer size= 10 out of bucket size= 10
Packet loss = 4
Buffer size= 9 out of bucket size= 10
⚖️ تفاوت بین Leaky Bucket و Token Bucket

🌀 Leaky Bucket:
وقتی میزبان می‌خواهد بسته‌ای بفرستد، آن بسته داخل سطل قرار می‌گیرد.
سطل با نرخ ثابت نشت می‌کند، یعنی داده‌ها با سرعت یکنواخت از آن خارج می‌شوند.
این روش ترافیک‌های پرنوسان (bursty traffic) را به ترافیک یکنواخت تبدیل می‌کند.
در عمل، سطل مانند یک صف محدود است که خروجی آن نرخ ثابتی دارد.

🔸 Token Bucket:
در این روش، سطل شامل توکن‌هایی است که در فواصل زمانی منظم تولید می‌شوند.
هر زمان بسته‌ای آماده‌ی ارسال باشد، یک توکن از سطل برداشته و بسته ارسال می‌شود.
اگر سطل خالی از توکن باشد، بسته نمی‌تواند ارسال شود.
سطل ظرفیت نهایی دارد و می‌تواند تا حد مشخصی توکن ذخیره کند.

🌟 مزایای Leaky Bucket نسبت به Token Bucket

بدون هدررفت توکن‌ها:

در Token Bucket ممکن است توکن‌ها بدون استفاده باقی بمانند، اما در Leaky Bucket تنها در زمان وجود ترافیک داده ارسال می‌شود.

⚡️ تأخیر کمتر:

در Token Bucket، اگر سطل خالی باشد بسته‌ها منتظر می‌مانند، اما Leaky Bucket با ارسال ثابت، تأخیر را کاهش می‌دهد.

🔁 انعطاف‌پذیری بالاتر:

Leaky Bucket به‌راحتی با تغییرات الگوهای ترافیکی سازگار می‌شود.

🧩 سادگی در پیاده‌سازی:

در مقایسه با Token Bucket، پیاده‌سازی و مدیریت آن آسان‌تر است.

📡 استفاده‌ی مؤثر از پهنای باند:

با ارسال داده‌ها با نرخ یکنواخت، از ازدحام شبکه جلوگیری می‌کند.

🏁 جمع‌بندی:
🔹 الگوریتم Leaky Bucket یکی از کلیدی‌ترین روش‌ها برای Traffic Shaping در شبکه‌هاست.
🔹 این الگوریتم به کنترل جریان داده‌ها، جلوگیری از ازدحام، و بهینه‌سازی عملکرد سیستم کمک زیادی می‌کند.

🏷 هشتگ‌ها:
#CSharp #Networking #LeakyBucket #TokenBucket #RateLimiting #TrafficShaping
💡وقتی چیزی خراب می‌شود ، حتی اگر از نظر فنی «مسئولش نباشی» وارد عمل شو.

👣 مالکیت (Ownership) به این معنا نیست که همه‌چیز را خودت انجام دهی؛
بلکه یعنی آن‌قدر اهمیت بدهی که مطمئن شوی هیچ مشکلی نادیده گرفته نمی‌شود.

👨‍💻 بعضی از بهترین مهندسانی که می‌شناسم هرگز نمی‌گویند:
«این کار من نیست.»
آن‌ها می‌گویند: «بیا ببینیم چطور می‌تونیم حلش کنیم.»

🧭 این طرز فکر تو را به نیرویی برای تغییر مثبت تبدیل می‌کند.
مالکیت، پایه و اساس رهبری است.

و دوباره تأکید می‌کنم:
مالکیت به این معنا نیست که خودت همه چیز را تعمیر کنی.
بلکه یعنی دیگران را هم همراه کنی، به آن‌ها قدرت بدهی،
و نشان دهی که آن‌ها هم می‌توانند بخشی از تغییر مثبت باشند. 💪
🎯 الگوریتم Token Bucket


🚦 چرا Rate Limiting مهم است؟
هر API یا سیستم، محدودیت‌هایی دارد.
اگر هیچ کنترلی نباشد، ممکن است یک کاربر با ارسال درخواست‌های بیش از حد، سرور را از کار بیندازد.

Rate Limiting
این مشکل را حل می‌کند. با کنترل تعداد درخواست‌هایی که در بازه‌ی زمانی مشخص اجازه‌ی عبور دارند.

الگوریتم‌های متعددی برای این کار وجود دارند:
📍 Fixed Window
📍 Sliding Window
📍 Leaky Bucket
📍 Token Bucket

در میان آن‌ها، Token Bucket یکی از پرکاربردترین‌هاست،
چون تعادل خوبی بین کنترل پایدار ترافیک و امکان ارسال «Burst»‌های کوتاه از درخواست‌ها ایجاد می‌کند.

💡 Token Bucket چیست؟

فرض کنید یک سطل (bucket) دارید که داخل آن توکن‌ها ریخته می‌شوند:

🪙 توکن‌ها با نرخ ثابتی اضافه می‌شوند (مثلاً ۵ توکن در هر ثانیه)

هر درخواست برای عبور، باید یک توکن مصرف کند

اگر توکن در دسترس باشد → درخواست مجاز است

اگر سطل خالی باشد → درخواست رد (یا معلق) می‌شود

سطل ظرفیت محدودی دارد، پس تعداد توکن‌ها نمی‌تواند بی‌نهایت زیاد شود

🔁 نتیجه؟

این روش اجازه می‌دهد سیستم برای مدت کوتاهی درخواست‌های بیشتری بپذیرد (burst)،
اما در بازه‌ی بلندمدت، نرخ کلی همچنان کنترل‌شده باقی بماند.

🧮 فرمول ریاضی پشت Token Bucket

🪣 C = ظرفیت سطل
⚡️ R = نرخ پر شدن (تعداد توکن در هر ثانیه)
⏱️ T = مدت‌زمان سپری‌شده از آخرین پر شدن

در هر لحظه، تعداد توکن‌ها برابر است با:
tokens = min(C, tokens + R * T)

وقتی درخواستی وارد می‌شود:
if tokens > 0 → allow and tokens -= 1
else → reject


📊 مثال اجرا

• ظرفیت سطل = 10
• نرخ پر شدن = 1 توکن در ثانیه

🕒 زمان‌بندی رخدادها:
• در زمان 0s → سطل پر است (10 توکن)
• کاربر 5 درخواست فوری می‌فرستد → 5 توکن باقی می‌ماند
• پس از 5 ثانیه → 5 توکن جدید اضافه می‌شود → سطل دوباره پر (10 توکن)
• کاربر 15 درخواست می‌فرستد → فقط 10 درخواست مجاز، 5 درخواست رد می‌شوند

الگوریتم Token Bucket اجازه‌ی ارسال ناگهانی درخواست‌ها (burst) را تا سقف ظرفیت سطل می‌دهد،
اما در بلندمدت، نرخ کلی ارسال درخواست‌ها را محدود نگه می‌دارد.
📌مزایا و معایب


✅️مزایا:
🔹️ امکان ارسال ناگهانی چند درخواست (burst) را فراهم می‌کند، ولی میانگین نرخ را محدود نگه می‌دارد.

🔹️ باعث شکل‌دهی روان‌تر به ترافیک می‌شود (smooth traffic shaping).

🔹️ به‌طور گسترده در شبکه‌ها و APIها استفاده می‌شود.

❌️معایب:
🔹️کمی پیچیده‌تر از روش Fixed Window Counter است.

🔹️در سیستم‌های توزیع‌شده، نیاز به همگام‌سازی دقیق دارد.

مثال کد :
using System;

public class TokenBucket
{
private readonly int _capacity; // حداکثر تعداد توکن‌ها
private readonly double _refillRate; // نرخ پر شدن (توکن در هر ثانیه)
private double _tokens; // تعداد توکن‌های فعلی
private DateTime _lastRefill; // آخرین زمان پر شدن

public TokenBucket(int capacity, double refillRate)
{
_capacity = capacity;
_refillRate = refillRate;
_tokens = capacity;
_lastRefill = DateTime.UtcNow;
}

private void Refill()
{
var now = DateTime.UtcNow;
var elapsedSeconds = (now - _lastRefill).TotalSeconds;
var addedTokens = elapsedSeconds * _refillRate;

if (addedTokens >= 1)
{
_tokens = Math.Min(_capacity, _tokens + addedTokens);
_lastRefill = now;
}
}

public bool AllowRequest()
{
Refill();
if (_tokens >= 1)
{
_tokens -= 1;
return true;
}
return false;
}
}

public class Program
{
public static void Main()
{
var bucket = new TokenBucket(10, 1); // 10 توکن، پر شدن 1 توکن در هر ثانیه

// شبیه‌سازی درخواست‌ها در بازه‌های 200 میلی‌ثانیه
var timer = new System.Timers.Timer(200);
timer.Elapsed += (s, e) =>
{
Console.WriteLine($"Request allowed? {bucket.AllowRequest()}");
};
timer.Start();

Console.WriteLine("Press any key to stop...");
Console.ReadKey();
}
}


🚀 کاربردهای واقعی Token Bucket Algorithm

🔸 دروازه‌های API مثل AWS API Gateway، Nginx، Envoy از نسخه‌های مختلف این الگوریتم برای کنترل نرخ درخواست‌ها استفاده می‌کنند.

🔸 روترهای شبکه (Network Routers) برای Traffic Shaping یا همون تنظیم و کنترل جریان ترافیک به کار می‌ره تا از شلوغی شبکه جلوگیری بشه.

🔸 صف‌های پیام (Message Queues) برای جلوگیری از بار بیش‌ازحد روی مصرف‌کننده‌ها (Consumers) استفاده می‌شه.

⚖️ مقایسه با سایر روش‌ها

🪟 Fixed Window Counter →
ساده‌تره، ولی در مرز بازه‌ها ممکنه رفتار غیرمنصفانه نشون بده و اجازه‌ی Burst زیاد بده.

💧 Leaky Bucket →
نرخ خروج داده رو ثابت نگه می‌داره، اما انعطاف‌پذیری کمتری برای Burst داره.

🎯 Token Bucket →
ترکیبیه از نرخ ثابت و پشتیبانی از Burst کوتاه‌مدت — یعنی بهترین تعادل برای کنترل ترافیک در APIها.

🏁 نتیجه‌گیری

🔹 الگوریتم Token Bucket یکی از کاربردی‌ترین و مؤثرترین روش‌ها برای پیاده‌سازی Rate Limiting در سیستم‌هاست.
🔹 پیاده‌سازی اون سادهه، از ترافیک ناگهانی پشتیبانی می‌کنه و استفاده‌ی منصفانه از منابع رو تضمین می‌کنه.
🔹 اگر در حال ساخت API یا سیستم توزیع‌شده هستی،
حتماً این الگوریتم باید یکی از گزینه‌های اصلی تو باشه.

🔖هشتگ‌ها‌:
#TokenBucket #RateLimiting #SystemDesign #API #Networking
Forwarded from tech-afternoon (Amin Mesbahi)
🐊 بدترین مهندس نرم‌افزار دنیا چه شکلیه؟ شاید هم خودمونیم؟؟

این مطلب صرفا نظر و تجربه شخصیه؛ نسخه جهان‌شمول یا خط‌کش نیست. تجربه‌ی بیش از دو دهه تعامل و دقت در رفتارها و مسیر رشد آدم‌ها از نگاه یک نفر از ۸ میلیارد جمعیت زمین است! قطعا میشه متون دقیق‌تر، کامل‌تر و موشکافانه‌تری هم نوشت؛ ولی شاید مرورش خالی از لطف نباشه...


لیست معضلات رفتاری، فنی، اخلاقی و تیمی خیلی بلند و مفصله. اما بعضی ویژگی‌ها، نه‌فقط مشکل هستن، بلکه مانع یادگیری و ریشه‌ی معضلات دیگه هم می‌شن. من قبل از نوشتن این مطلب، سعی کردم رفتارها و خصوصیت‌هایی که در خودم «تصور» می‌کنم بهبود دادم رو مرور کنم، ببینم اگر چه خصوصتی داشتم، مانع جدی برای بهبود می‌شد؟! بعد این لیست رو اینقدر مرور کردم که چکیده‌ای از رذائل دربیارم که ریشه مشکلات باشن! به نظرم اون‌هایی واقعاً «افتضاح‌ترین» هستن که ترکیب خطرناکی از این ۳ ویژگی رو دارن:

۱. نداشتن صداقت و اخلاق حرفه‌ای
یادمون نره: بدترین برنامه‌نویس، کسی نیست که اشتباه می‌کنه؛ کسیه که اشتباهش رو پنهان می‌کنه.

✏️ مصداق‌ها:
- وانمود می‌کنه چیزی رو بلده ولی بلد نیست
- دروغ می‌گه که پیشرفت پروژه خوبه، در‌حالی‌که نیست (green shifting)
- عامدانه code review تقلبی می‌ده؛ فقط یه ابزار آنالیز خودکار باز کرده
- باگ‌ها رو قایم می‌کنه

😡 چرا بده؟ چون اعتماد تیم رو نابود می‌کنه. بدون اعتماد، حتی بهترین فرآیندها هم به لجن کشیده می‌شن. این افراد به خودشون هم دروغ می‌گن. تقلب می‌کنن و کار کردن با کسی که عامدانه دروغ می‌گه و بهش عادت کرده، دیگ داغیست از دیگ‌های داغ جهنم!

۲. بی‌سؤالی، تعصب، توقف رشد
بزرگ‌ترین ریسک صنعت ما توقف یادگیریه؛ نه نابلدی!

🧠 کسی که سوال نداره، انگار دیگه دنبال بهتر شدن نیست.
🔒 کسی که تعصب داره (فقط فلان زبان، فقط فلان ابزار)، راه اصلاح رو به خودش بسته؛ شاید فکر کنه داره یاد می‌گیره؛ ولی داره مهارتش در یاد نگرفتن و توجیه نادانی‌اش رو تقویت می‌کنه.
🙈 کسی که اشتباه می‌کنه، ولی فکر می‌کنه تقصیر دنیاست، یعنی از دورِ یادگیری خارج شده.

فرق کسی که رو به جلو می‌ره و کسی که رو به زواله توی همین چیزاست.

۳. بی‌مالکیتی و انفعال

"به من بگو چی‌کار کنم" ممکنه از دهن یه تازه‌کار قابل قبول باشه. ولی یه مهندس واقعی باید خودش دنبال معنی، مشکل، راه‌حل، و تبعات کارش باشه.

📡 نشونه‌ها:
- فقط همون کاری رو می‌کنه که دقیقاً بهش گفتن
- هیچ پیش‌فرضی رو به چالش نمی‌کشه (تفکر نقادانه نداره اساسا)
- تغییرات رو با مقاومت پاسخ می‌ده (فناوری، فرآیند، ابزار)
- کارش رو فقط "تا اینجا وظیفه‌م بود" می‌بینه

چرا بده؟ چون تیم رو از یه گروه خلاق به یه کارخانه "دستور بگیر - خروجی بده" تبدیل می‌کنه. هیچ self-organization واقعی‌ای شکل نمی‌گیره. (توی پست نرم‌افزار و این روزهای ایران مفصل نوشتم)

💡 پشت همهٔ اینا یه چیزه...
ریشه همهٔ این‌ها، نداشتن principle (به فارسی پرنسیپ گفته می‌شه). یعنی کسی که هیچ چارچوب فکری و اخلاقی برای خودش نساخته. درسته که می‌شه چارچوب بد هم داشت ولی این کلمه در ذاتش بار مثبت اخلاقی داره. کسی که principle نداره نه از خودش نمی‌پرسه:
«این رفتار درسته؟»
«چرا دارم این کار رو اینجوری انجام می‌دم؟»
«اصلاً من دارم رشد می‌کنم یا درجا می‌زنم؟»
«آیا آدم‌ها از تعامل با من خوشحالن؟ آیا مفیدم؟ چجوری بهتر بشم؟»

یه آدم فاقد principle، بر اساس منفعت لحظه‌ای رفتار می‌کنه. یه بار پنهان می‌کنه، یه بار تقلب می‌کنه، یه بار مقاومت در برابر حرف صحیح، یه بار انفعاله... چون "راهنمای درونی" نداره.

🤝 و آخرش اینه:
- می‌شه چیزی رو بلد نبود، ولی یاد گرفت، «سوال خوب داشت»
- می‌شه هم‌تیمی خوبی نبود، ولی مهارت کار تیمی رو تقویت کرد
- می‌شه اشتباه کرد، ولی پنهانش نکرد، دنبال راه‌حل گشت، مقاومت و فرافکنی نکرد و دیگه تکرار نکرد
- می‌شه بهترین نبود، بهترین جا نبود؛ ولی با ساکن و منفعل نبودن، جای بهتری قرار گرفت، محیط بهتری ساخت...

البته بعضی از این رفتارها، در واکنش به محیط‌های ناسالم یا تیم‌های سمی شکل می‌گیرن؛ برخی‌شون ریشه در تربیت، کودکی، جامعه و اطرافیان دارن. ولی اینکه ما با چه اصولی رفتار می‌کنیم، هنوز دست خودمونه.

💬 حالا نوبت شماست:
کامنت کنید؛ شاید کمک کنه فردا کمی بهتر از امروز باشیم 🌱
Please open Telegram to view this post
VIEW IN TELEGRAM
🧠 مقدمه‌ای بر NoSQL

پایگاه‌داده‌های NoSQL (مخفف Not Only SQL) برای مدیریت حجم‌های بزرگ داده‌های غیرساخت‌یافته (Unstructured) و نیمه‌ساخت‌یافته (Semi-Structured) طراحی شده‌اند.

برخلاف پایگاه‌داده‌های رابطه‌ای (Relational Databases) که به ساختار ثابت و جدول‌های مشخص متکی هستند، NoSQL مدل داده‌ای انعطاف‌پذیر ارائه می‌دهد و از مقیاس‌پذیری افقی (Horizontal Scaling) پشتیبانی می‌کند.

به همین دلیل، NoSQL گزینه‌ای ایده‌آل برای برنامه‌های مدرن است که نیاز به کارایی بالا، مقیاس‌پذیری گسترده و مدیریت مؤثر داده‌های متنوع دارند.

🔑 ویژگی‌های کلیدی پایگاه‌داده‌های NoSQL

📂 طرح پویا (Dynamic Schema):
امکان تغییر ساختار داده بدون نیاز به مهاجرت یا بازطراحی پایگاه‌داده.

⚙️ مقیاس‌پذیری افقی (Horizontal Scalability):
با افزودن نودهای جدید به خوشه، ظرفیت ذخیره‌سازی و توان پردازشی افزایش می‌یابد و بار کاری بین چند سرور توزیع می‌شود.

🧾 مبتنی بر سند (Document-Based):
داده‌ها در قالب‌های انعطاف‌پذیر مانند JSON یا BSON ذخیره می‌شوند (مثل MongoDB).

🔑 مبتنی بر کلید-مقدار (Key-Value):
داده‌ها به‌صورت جفت کلید و مقدار ذخیره می‌شوند، که دسترسی سریع و ساده‌ای فراهم می‌کند (مثل Redis).

📊 مبتنی بر ستون (Column-Based):
داده‌ها در قالب ستون‌ها سازمان‌دهی می‌شوند، نه ردیف‌ها (مثل Cassandra).

🌐 توزیع‌شده و در دسترس بالا (Distributed & High Availability):
برای در دسترس بودن مداوم طراحی شده‌اند و در صورت خرابی یک نود، داده‌ها از طریق تکثیر (Replication) روی سایر نودها حفظ می‌شوند.

🧩 انعطاف‌پذیری بالا:
توسعه‌دهندگان می‌توانند داده‌ها را به‌صورت پویا و با انواع مختلف ساختارها ذخیره و بازیابی کنند.

⚡️ کارایی بالا:
مناسب برای Big Data، تحلیل‌های لحظه‌ای (Real-Time Analytics) و برنامه‌هایی با حجم زیاد درخواست‌ها.

⚠️ چالش‌های پایگاه‌داده‌های NoSQL

📏 نبود استاندارد مشخص:
هر سیستم NoSQL ساختار و منطق خاص خود را دارد، که انتخاب گزینه‌ی مناسب برای پروژه را دشوارتر می‌کند.

عدم پشتیبانی کامل از ACID:
برخی از NoSQLها، سازگاری و یکپارچگی داده را به‌طور کامل تضمین نمی‌کنند، که برای سیستم‌های حساس به دقت داده می‌تواند مشکل‌ساز باشد.

🎯 تمرکز محدود:
برای ذخیره‌سازی داده عالی هستند، اما در زمینه‌هایی مثل مدیریت تراکنش‌ها به اندازه‌ی پایگاه‌داده‌های رابطه‌ای قوی نیستند.

🔍 پشتیبانی محدود از پرس‌وجوهای پیچیده:
برای اجرای کوئری‌های تحلیلی پیچیده یا گزارش‌گیری‌های سنگین مناسب نیستند.

🧱 بلوغ کمتر نسبت به پایگاه‌داده‌های سنتی:
از نظر امنیت، پایداری و امکانات هنوز به سطح پایگاه‌داده‌های رابطه‌ای نرسیده‌اند.

⚙️ پیچیدگی در مدیریت:
نگهداری و مدیریت پایگاه‌داده‌های NoSQL در مقیاس بزرگ می‌تواند دشوارتر از پایگاه‌داده‌های رابطه‌ای باشد.

💻 ابزارهای گرافیکی محدود:
هرچند برخی ابزارها مثل MongoDB Compass وجود دارند، اما بسیاری از NoSQLها ابزارهای تصویری قوی و کاربرپسند ندارند.
⚔️ SQL vs NoSQL

SQL (پایگاه‌داده رابطه‌ای)


🗂 مدل داده: ساخت‌یافته و جدولی

📈 مقیاس‌پذیری: عمودی (Vertical Scaling)

🏗 ساختار (Schema): از قبل تعریف‌شده

پشتیبانی ACID: قوی

🎯 مناسب برای: برنامه‌های تراکنشی

💻 نمونه‌ها: MySQL، PostgreSQL، Oracle

NoSQL (پایگاه‌داده غیررابطه‌ای)


🗂 مدل داده: انعطاف‌پذیر (سند، کلید-مقدار، گراف)

📈 مقیاس‌پذیری: افقی (Horizontal Scaling)

🏗 ساختار (Schema): پویا و بدون ساختار ثابت

پشتیبانی ACID: محدود یا سازگاری تدریجی (Eventual Consistency)

🎯 مناسب برای: داده‌های حجیم (Big Data)، تحلیل‌های لحظه‌ای

💻 نمونه‌ها: MongoDB، Cassandra، Redis

🗂 پایگاه‌داده‌های محبوب NoSQL و کاربرد آن‌ها


MongoDB (مبتنی بر سند) → مدیریت محتوا، کاتالوگ محصولات

Redis (کلید-مقدار) → کشینگ، تحلیل‌های لحظه‌ای، ذخیره‌سازی نشست‌ها

Cassandra (مبتنی بر ستون‌ها) → داده‌های حجیم، سیستم‌های با دسترسی بالا

Neo4j (گراف) → شناسایی تقلب، شبکه‌های اجتماعی

💡 کاربردهای NoSQL


📊 برنامه‌های Big Data: ذخیره و پردازش مؤثر حجم‌های بسیار زیاد داده‌های غیرساخت‌یافته و نیمه‌ساخت‌یافته

⏱️ تحلیل‌های لحظه‌ای (Real-Time Analytics): پشتیبانی از کوئری‌های سریع و تحلیل داده برای موتورهای پیشنهاددهنده یا شناسایی تقلب

🌐 برنامه‌های وب مقیاس‌پذیر: مدیریت کاربران زیاد و ترافیک بالا با مقیاس‌پذیری افقی در بین سرورها

🔄 ذخیره‌سازی داده انعطاف‌پذیر: مدیریت فرمت‌های مختلف داده (JSON، کلید-مقدار، اسناد، گراف) بدون نیاز به ساختار سخت و ثابت

🔖هشتگ‌ها:
#NoSQL #SQL #Database #DatabaseDesign
Keep showing up.
Keep learning.
Keep building.
🌳 ساختار داده Trie

ساختار داده Trie (Trie Data Structure)، که به آن درخت پیشوندی (Prefix Tree) نیز گفته می‌شود، یک ساختار داده شبیه درخت است که برای بازیابی سریع جفت‌های کلید-مقدار استفاده می‌شود.
این ساختار معمولاً برای پیاده‌سازی فرهنگ‌لغت‌ها و قابلیت Autocomplete به کار می‌رود و جزئی حیاتی در بسیاری از الگوریتم‌های جستجو محسوب می‌شود.

⚡️ ویژگی‌های ساختار داده Trie

• هر Trie دارای یک گره ریشه خالی است که لینک‌ها یا ارجاعات به سایر گره‌ها دارد.

• هر گره نمایانگر یک رشته است و هر یال (Edge) نمایانگر یک کاراکتر می‌باشد.

• هر گره شامل یک هش‌مپ (HashMap) یا یک آرایه از اشاره‌گرها است؛ هر شاخص نمایانگر یک کاراکتر بوده و یک علامت (Flag) مشخص می‌کند که آیا رشته در گره جاری پایان یافته است یا خیر.

• هر مسیر از ریشه تا هر گره، یک کلمه یا رشته را نمایندگی می‌کند.
⚔️ مقایسه Trie و Hash Table

ساختار داده‌ای Trie برای ذخیره‌سازی و بازیابی داده‌ها استفاده می‌شود و همان عملیات‌ها می‌توانند با استفاده از ساختار داده‌ای دیگری مانند Hash Table نیز انجام شوند، اما ساختار Trie این عملیات‌ها را به شکل مؤثرتری انجام می‌دهد. علاوه بر این، ساختار Trie می‌تواند برای جستجوی مبتنی بر پیشوند و بازدید مرتب از همه کلمات استفاده شود. بنابراین Trie مزایای هر دو را دارد: هم Hash Table و هم درخت جستجوی دودویی خودمتعادل.

🔹️می‌توانیم به شکل مؤثر جستجوی پیشوندی (یا autocomplete) را با Trie انجام دهیم.

🔹️می‌توانیم به راحتی تمام کلمات را به ترتیب الفبایی چاپ کنیم که در Hashing به آسانی ممکن نیست.

🔹️در ساختار Trie، هیچ سربار مربوط به توابع هش وجود ندارد.

🔹️جستجوی یک رشته حتی در مجموعه بزرگی از رشته‌ها در ساختار Trie می‌تواند با پیچیدگی زمانی O(L) انجام شود، جایی که L طول کلید ورودی است.

🔹️نیاز به فضای حافظه اضافی برای ذخیره کلمات دارد و این فضا ممکن است برای لیست‌های طولانی کلمات و/یا کلمات طولانی بسیار زیاد شود.