Software Engineering

تسلط بر اصول SOLID: ساخت نرم‌افزارهای انعطاف‌پذیر و مقیاس‌پذیر

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

۱. اصل مسئولیت واحد (SRP)

یک کلاس باید تنها یک دلیل برای تغییر داشته باشد. اگر یک کلاس هم منطق کسب‌وکار و هم ماندگاری داده را مدیریت کند، از SRP تخلف کرده است. جداسازی نگرانی‌ها تضمین می‌کند که تغییرات در یک جنبه از سیستم، به‌طور تصادفی جنبه دیگری را خراب نکند.

// بد: یک کلاس که چندین وظیفه را انجام می‌دهد
public class User {
    public void save() { /* منطق پایگاه داده */ }
    public void sendEmail() { /* منطق ایمیل */ }
}

// خوب: جداسازی مسئولیت‌ها
public class UserRepository {
    public void save(User user) { /* منطق پایگاه داده */ }
}

public class EmailService {
    public void sendWelcome(User user) { /* منطق ایمیل */ }
}

۲. اصل باز/بسته (OCP)

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

// بد: اصلاح کد موجود
public class Discount {
    public double calculate(Invoice invoice) {
        if (invoice.getType() == "VIP") return invoice.getTotal() * 0.9;
        // افزودن انواع جدید بدون ریسک دشوار است
    }
}

// خوب: گسترش از طریق الگوی استراتژی
public interface DiscountStrategy {
    double calculate(Invoice invoice);
}

public class VipDiscount implements DiscountStrategy {
    public double calculate(Invoice invoice) {
        return invoice.getTotal() * 0.9;
    }
}

۳. اصل جایگزینی لیسکوف (LSP)

اشیاء یک کلاس والد باید بتوانند بدون خراب کردن برنامه، با اشیاء یک کلاس فرزند جایگزین شوند. این امر تضمین می‌کند که ارث‌بری به درستی استفاده شده و زیرنوع‌ها واقعاً رفتارهای سوپرتایپ‌های خود هستند.

برای مثال، یک Square (مربع) نباید از Rectangle (مستطیل) ارث ببرد، زیرا تنظیم عرض یک مربع به همان شکلی که برای یک مستطیل منطقی است، معنایی ندارد و انتظارات رفتاری را نقض می‌کند.

۴. اصل جداسازی رابط (ISP)

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

// بد: رابط بزرگ
public interface Worker {
    void work();
    void eat();
    void sleep();
}

// خوب: رابط‌های جداسازی‌شده
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

۵. اصل وارونگی وابستگی (DIP)

ماژول‌های سطح بالا نباید به ماژول‌های سطح پایین وابسته باشند. هر دو باید به انتزاعات وابسته باشند. این امر کوپلینگ ضعیف را ترویج می‌کند و با امکان Mock کردن یا تعویض وابستگی‌ها، تست‌پذیری را آسان‌تر می‌سازد.

// بد: وابستگی مستقیم
public class OrderService {
    private MySqlDatabase db = new MySqlDatabase();
}

// خوب: وابستگی به انتزاع
public class OrderService {
    private Database db;
    public OrderService(Database db) {
        this.db = db;
    }
}

نتیجه‌گیری

اعمال اصول SOLID به این معنی نیست که باید در هر خط کد به‌طور خشک و خالی به آن‌ها پایبند باشید. به جای آن، از آن‌ها به‌عنوان راهنما برای تشخیص زمانی که طراحی شما شکننده می‌شود، استفاده کنید. با تمرکز بر مسئولیت واحد، باز/بسته، جایگزینی لیسکوف، جداسازی رابط و وارونگی وابستگی، می‌توانید نرم‌افزاری بسازید که نه تنها امروز کارآمد است، بلکه برای چالش‌های فردا نیز انعطاف‌پذیر خواهد بود.

Share: