
معرفی ویدئو: abstraction در طراحی نرمافزار
ویدئوی «Abstraction in Software Design» به بررسی مفهوم انتزاع (Abstraction) در طراحی نرمافزار میپردازد. این ویدئو که در پلتفرم یوتیوب در دسترس است، با ارائه توضیحاتی روشن و مثالهایی کاربردی، به مخاطبان کمک میکند تا درک عمیقتری از چگونگی استفاده از انتزاع برای سادهسازی طراحی سیستمهای نرمافزاری پیدا کنند. در این ویدئو، مفاهیم کلیدی مانند کاهش پیچیدگی، بهبود قابلیت نگهداری کد و افزایش انعطافپذیری سیستمها از طریق انتزاع تشریح شدهاند. این محتوا برای توسعهدهندگان نرمافزار، دانشجویان مهندسی نرمافزار و هر کسی که به دنبال بهبود مهارتهای طراحی سیستم است، منبعی ارزشمند محسوب میشود.
لینک ویدئو: Abstraction in Software Design
سلام دوستان، من شیو کومار هستم و امروز میخوام درباره یکی از پایهایترین اصول طراحی نرمافزار حرف بزنم: انتزاعها یا همون abstractionها. خیلیها میدونن که انتزاع مهمه، اما نمیتونن دقیق توضیح بدن چیه، یا فرق بین یه انتزاع خوب و بد رو تشخیص بدن. من توی این مقاله، بر اساس صحبتهام توی ویدیو یوتیوبم (که لینکش رو میذارم: https://m.youtube.com/watch?v=hOrpppzEX14&t=218s)، میخوام این مفهوم رو به زبون خودم توضیح بدم. ساده و خلاصه، بدون پیچیدگیهای غیرضروری. همچنین، توی توضیحات به ریپازیتوری گیتهابم اشاره میکنم (https://github.com/matlus/Adapter-Design-Pattern-Message-Brokers) که مثالهایی از الگوی Adapter Design Pattern برای Message Brokers داره، و چطور میتونید از RabbitMQ برای تست محلی Azure Service Bus استفاده کنید. این ریپو شامل لینکهایی به ویدیوهای مرتبط مثل معرفی Message Brokers، شروع با RabbitMQ و Azure Service Bus هست، که کمک میکنه مفهوم انتزاع رو توی زمینه واقعی ببینید.
بیایم از اول شروع کنیم. انتزاع چیه؟ من تعریف پیچیده نمیدم، فقط یه دید کلی. دیکسترا (Edsger Dijkstra) گفته بود: "هدف انتزاع این نیست که مبهم باشه." یعنی انتزاع نباید کلی و نامفهوم باشه، بلکه باید دقیقتر و سادهتر کنه چیزها رو. خیلیها اشتباه میکنن و سعی میکنن انتزاعهایی بسازن که خیلی بزرگ و عمومی باشه، انگار برای کل دنیا! نه، این کار رو نکنید. انتزاع برای برنامه خاص خودتون هست، نه برای همه.
یه طیف داریم برای انتزاع: از خیلی کم (که نزدیک به پیادهسازی واقعی هست و جزئیات زیاد لو میره) تا خیلی زیاد (که مبهم و کلی میشه). سطح درست وسط نیست همیشه، بستگی به نیاز داره. مثلاً اگه انتزاع زیادی بسازی، نمیدونی چی کار میکنی، همه چیز نامفهوم میشه. اگه کم باشه، پیچیدگیهای پشت صحنه لو میره و فایدهای نداره.
هدف انتزاع اینه که پیچیدگیها رو پنهان کنه، اما یه رابط کاربری مفید و کارآمد بده. مثلاً توی ذخیرهسازی داده، اگه از کلماتی مثل "ذخیرهسازی" استفاده کنی به جای "دیتابیس"، ممکنه مبهم بشه مگر اینکه نیاز واقعی برنامهت باشه. اگه تیمتون میدونه که ممکنه از دیتابیس، کش یا ذخیرهسازی ابری استفاده کنه، اون وقت این سطح انتزاع خوبه. اما اگه فقط برای آینده خیالی باشه، هزینهش زیاده: مردم گیج میشن و سیستم سخت فهم میشه.
یکی از مشکلات رایج، نشتی انتزاعه. یعنی جزئیات پیادهسازی لو میره و کاربر مجبور میشه بدونتشون. مثلاً اگه یه لایه انتزاعی برای دیتابیس بسازی، اما کاربر باید بدونه که پشتش Cosmos DB هست یا SQL Server، این نشتی داره. یه انتزاع خوب باید مثل جعبه سیاه باشه: ندونی داخلش چیه، فقط بدونی کار میکنه.
مثال از کد: لایه انتزاعی برای Cosmos DB
بیایم یه مثال واقعی بزنم. فرض کن یه کلاس داریم به اسم CosmosDbGateway که یه لایه انتزاعی برای ذخیرهسازی دادههاست. اما توجه کنید، امضای متدها نباید نشون بده که Cosmos DB هست. باید عمومی باشه، انگار میتونه هر چیزی باشه: دیتابیس، فایل سیستم یا حافظه.
اینجا یه کد نمونه از کلاس عمومی (interface یا abstract class):
public interface IDataGateway { Task CreateRecordAsync(VideoModel video); // ایجاد رکورد جدید Task<VideoModel> GetVideoByIdAsync(string id); // گرفتن ویدیو بر اساس ID Task UpdateVideoAsync(VideoModel video); // بروزرسانی ویدیو Task<List<VideoModel>> GetVideosByTagAsync(string tag); // گرفتن ویدیوها بر اساس تگ }
حالا پیادهسازیش با Cosmos DB (این جزئیات پشت صحنهست و کاربر نباید بدونه):
using Microsoft.Azure.Cosmos; using System.Collections.Generic; using System.Threading.Tasks; public class CosmosDbGateway : IDataGateway { private readonly CosmosClient _cosmosClient; private readonly Database _database; private readonly Container _container; public CosmosDbGateway(CosmosClient cosmosClient) { _cosmosClient = cosmosClient; _database = cosmosClient.GetDatabase("MyDatabase"); _container = _database.GetContainer("VideosContainer"); } public async Task CreateRecordAsync(VideoModel video) { await _container.CreateItemAsync(video, new PartitionKey(video.Id)); } public async Task<VideoModel> GetVideoByIdAsync(string id) { ItemResponse<VideoModel> response = await _container.ReadItemAsync<VideoModel>(id, new PartitionKey(id)); return response.Resource; } public async Task UpdateVideoAsync(VideoModel video) { await _container.UpsertItemAsync(video, new PartitionKey(video.Id)); } public async Task<List<VideoModel>> GetVideosByTagAsync(string tag) { var query = new QueryDefinition("SELECT * FROM c WHERE ARRAY_CONTAINS(c.Tags, @tag)") .WithParameter("@tag", tag); FeedIterator<VideoModel> iterator = _container.GetItemQueryIterator<VideoModel>(query); List<VideoModel> results = new List<VideoModel>(); while (iterator.HasMoreResults) { FeedResponse<VideoModel> response = await iterator.ReadNextAsync(); results.AddRange(response); } return results; } }
میبینید؟ کاربر فقط متدهایی مثل CreateRecord یا GetVideosByTag رو میبینه و فراخوانی میکنه. نمیدونه پشتش Cosmos DB هست، iteratorها یا queryها چیه. این انتزاع تمیزه چون جزئیات لو نمیره. اگه بخوام پیادهسازی رو عوض کنم به SQL Server یا حافظه، امضا تغییر نمیکنه.
حالا بیایم سراغ message brokers. توی سیستمهای pub-sub (انتشار-اشتراک)، بعضی برنامهها فقط منتشر میکنن (publish)، بعضی فقط مشترک میشن (subscribe)، و بعضی هر دو. سطح انتزاع باید مناسب باشه. مثلاً برای برنامهای که فقط publish میکنه، لازم نیست بدونه subscribe چیه.
توی ریپازیتوری گیتهابم (https://github.com/matlus/Adapter-Design-Pattern-Message-Brokers)، من الگوی Adapter Design Pattern رو برای message brokers نشون دادم. این الگو کمک میکنه از RabbitMQ برای تست محلی Azure Service Bus استفاده کنید، بدون اینکه کد اصلی تغییر کنه. مثلاً یه adapter بسازید که RabbitMQ رو مثل Service Bus رفتار کنه.
یه کد نمونه ساده برای publisher:
using System.Text; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using RabbitMQ.Client; public interface IPublisher { Task PublishMessageAsync(string topic, string message); } public class ServiceBusPublisher : IPublisher { private readonly ServiceBusClient _client; // پیادهسازی با Azure Service Bus public Task PublishMessageAsync(string topic, string message) { // Implementation would go here throw new System.NotImplementedException(); } } public class RabbitMqAdapter : IPublisher { private readonly IConnection _connection; public RabbitMqAdapter(IConnection connection) { _connection = connection; } // پیادهسازی با RabbitMQ به عنوان adapter public async Task PublishMessageAsync(string topic, string message) { using var channel = _connection.CreateModel(); channel.ExchangeDeclare(topic, "fanout"); channel.BasicPublish(topic, "", null, Encoding.UTF8.GetBytes(message)); await Task.CompletedTask; } }
اینجا، adapter اجازه میده از RabbitMQ محلی برای تست استفاده کنید، اما کد اصلی فکر میکنه با Service Bus کار میکنه. این یه انتزاع خوبه چون جزئیات broker لو نمیره. برای جزئیات بیشتر، ویدیوهای توی ریپو رو ببینید: معرفی Message Brokers، شروع با RabbitMQ و Azure Service Bus.
عجله نکنید برای ساخت انتزاع. اگه کد رو سه بار کپی-پیست کردید، اون وقت فکر کنید به کلی کردنش. پارامترهای زیاد (مخصوصاً بولی) اضافه نکنید، چون متد رو مبهم میکنه. مثلاً یه متد ساده بسازید، نه یه کارخانه همهکاره.
نتیجهگیری
انتزاع خوب سطح مناسبی داره، جزئیات لازم حوزه کاری رو حفظ میکنه، و مثل جعبه سیاه کار میکنه. برای برنامه خودتون بسازید، نه برای آینده خیالی. اگه این مقاله کمک کرد، لایک بدید و ویدیو رو ببینید. دفعه بعد درباره کی و چطور انتزاع بسازیم حرف میزنیم. موفق باشید!