پرش به محتوای اصلی
مسیر تخصصی جاوا و Spring
درخواست پروژهتدریس خصوصیارتباط با ماراهنما
جاواپرو v1.2.36یاد بگیر، تمرین کن، حرفه‌ای بساز
خانهدوره‌هاآموزش رایگانوبلاگمدرس‌هاخدمات
60% تخفیف روی تمامی دوره های جاواپروتا جمعه 15 آبان | کد تخفیف : BCT۰۰ : ۰۴ : ۰۲ : ۳۸مشاهده جشنواره
خانه10 نکته سریع برای نوشتن کد تمیز | جاواپرو
مقالات جاوا

10 نکته سریع برای نوشتن کد تمیز

یکی از مهم‌ترین ویژگی‌های سیستم‌هایی که طراحی خوبی دارن و عمر طولانی می‌کنن، اینه که بتونن با تغییر نیازمندی‌ها سازگار بشن و در عین حال نگهداری ساده‌ای…

۲۵ شهریور ۱۴۰۴۵ دقیقه مطالعهمقالات جاوارحمن زارعی
10 نکته سریع برای نوشتن کد تمیز
محتوای منتخب جاواپرو

خلاصه مطلب

یکی از مهم‌ترین ویژگی‌های سیستم‌هایی که طراحی خوبی دارن و عمر طولانی می‌کنن، اینه که بتونن با تغییر نیازمندی‌ها سازگار بشن و در عین حال نگهداری ساده‌ای…
نوع محتوامقاله آموزشی
موضوعمقالات جاوا
زمان مطالعه۵ دقیقه مطالعه
آخرین ویرایش۲۵ شهریور ۱۴۰۴

شیوه و اصول کدنویسی تمیز

۱۰ نکته سریع برای نوشتن کد تمیز

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

اینجا ۱۰ نکته سریع آوردم که بهت کمک می‌کنه کدی تمیزتر و حرفه‌ای‌تر بنویسی.

۱. متدها رو کوتاه نگه دار — ترجیحاً زیر ۱۰ خط

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

/ Original
public double calculatePrice(int customerId, int productId, int quantity) {
    Product product = productRepository.findById(productId);

    double singlePrice = product.getSinglePrice();
    double baseTotal = singlePrice * quantity;

    double taxRate = 0.07;
    double totalWithTax = baseTotal + (baseTotal * taxRate);

    double discount = 0.0;
    double loyaltyDiscount = 0.0;
    double bulkDiscount = 0.0;

    Customer customer = customerRepository.findById(customerId);

    if (customer.isSilver()) {
        discount = 0.10;
        if (quantity > 10) {
            bulkDiscount = 0.05;
        }
    } else if (customer.isGold()) {
        discount = 0.15;
        if (quantity > 5) {
            bulkDiscount = 0.10;
        }
    } else if (customer.isPlatinum()) {
        discount = 0.20;
        if (quantity > 3) {
            bulkDiscount = 0.15;
        }
    }

    if (customer.getYearsAsCustomer() > 5) {
        loyaltyDiscount = 0.03;
    }

    double totalDiscount = discount + bulkDiscount + loyaltyDiscount;
    if (totalDiscount > 0.30) {
        totalDiscount = 0.30;
    }

    double discountedPrice = totalWithTax - (totalWithTax * totalDiscount);
    double finalPrice = Math.round(discountedPrice * 100.0) / 100.0;

    return finalPrice;
}

// After Refactoring
public double calculatePrice(int customerId, int productId, int quantity) {
    Product product = productRepository.findById(productId);
    Customer customer = customerRepository.findById(customerId);

    double baseTotal = calculateBaseTotal(product, quantity);
    double totalWithTax = applyTax(baseTotal);
    double totalDiscount = calculateTotalDiscount(customer, quantity);
    double discountedPrice = applyDiscount(totalWithTax, totalDiscount);

    return roundToTwoDecimalPlaces(discountedPrice);
}

private double calculateBaseTotal(Product product, int quantity) {
    return product.getSinglePrice() * quantity;
}

private double applyTax(double baseTotal) {
    double taxRate = 0.07;
    return baseTotal + (baseTotal * taxRate);
}

private double calculateTotalDiscount(Customer customer, int quantity) {
    double tierDiscount = calculateTierDiscount(customer, quantity);
    double loyaltyDiscount = calculateLoyaltyDiscount(customer);
    double totalDiscount = tierDiscount + loyaltyDiscount;

    return Math.min(totalDiscount, 0.30);
}

private double calculateTierDiscount(Customer customer, int quantity) {
    double discount = 0.0;
    double bulkDiscount = 0.0;

    if (customer.isSilver()) {
        discount = 0.10;
        if (quantity > 10) bulkDiscount = 0.05;
    } else if (customer.isGold()) {
        discount = 0.15;
        if (quantity > 5) bulkDiscount = 0.10;
    } else if (customer.isPlatinum()) {
        discount = 0.20;
        if (quantity > 3) bulkDiscount = 0.15;
    }

    return discount + bulkDiscount;
}

private double calculateLoyaltyDiscount(Customer customer) {
    return customer.getYearsAsCustomer() > 5 ? 0.03 : 0.0;
}

private double applyDiscount(double total, double discountRate) {
    return total - (total * discountRate);
}

private double roundToTwoDecimalPlaces(double value) {
    return Math.round(value * 100.0) / 100.0;
}



۲. کلاس‌ها رو کوتاه نگه دار — ترجیحاً زیر ۲۰۰ خط

این نکته تقریباً واضحه. وقتی یک کلاس زیر ۲۰۰ خط باشه، معمولاً داری یکی از اصول مهم SOLID یعنی Single Responsibility رو رعایت می‌کنی. کلاس‌های کوچک‌تر چندتا مزیت خیلی مهم دارن:

  • دیباگ کردن ساده‌تر می‌شه
  • ریفکتور کردن راحت‌تر می‌شه
  • تست‌نویسی سریع‌تر انجام می‌شه
  • جایگزین کردنش با پیاده‌سازی‌های دیگه آسون‌تره

۳. تعداد متدهای عمومی رو به یک یا دو تا محدود کن

وقتی یک کلاس فقط برای انجام یک کار مشخص طراحی شده (طبق اصل Single Responsibility)، معمولاً نیازی نداره چندین متد عمومی داشته باشه. اگه بیشتر از یک متد پابلیک تعریف می‌کنی، باید دلیل منطقی پشتش باشه؛ مثلاً وقتی داری متدها رو Overload می‌کنی تا استفاده از کلاس برای Caller ساده‌تر بشه. مهم اینه که این کار رو آگاهانه انجام بدی، نه از روی عادت.

۴. از کامنت‌گذاری زیاد خودداری کن

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

// With Comments
private void validateProduct(Product product) {
    // Check if the product is null
    if (product == null) {
        throw new IllegalArgumentException("Product cannot be null");
    }

    // Check if the product has a valid name
    if (product.getName() == null || product.getName().trim().isEmpty()) {
        throw new IllegalArgumentException("Product must have a name");
    }

    // Check if the product price is greater than zero
    if (product.getSinglePrice() <= 0) {
        throw new IllegalArgumentException("Product price must be greater than zero");
    }

    // Check if the product category is valid
    if (!isValidCategory(product.getCategory())) {
        throw new IllegalArgumentException("Invalid product category: " + product.getCategory());
    }

    // Check if the product is active
    if (!product.isActive()) {
        throw new IllegalStateException("Product must be active");
    }
}

private boolean isValidCategory(String category) {
    return category != null && List.of("Electronics", "Books", "Clothing", "Food").contains(category);
}

// Without comments
private void validateProduct(Product product) {
    ensureProductIsNotNull(product);
    ensureProductHasName(product);
    ensurePriceIsPositive(product);
    ensureCategoryIsValid(product);
    ensureProductIsActive(product);
}

private void ensureProductIsNotNull(Product product) {
    if (product == null) {
        throw new IllegalArgumentException("Product cannot be null");
    }
}

private void ensureProductHasName(Product product) {
    if (product.getName() == null || product.getName().trim().isEmpty()) {
        throw new IllegalArgumentException("Product must have a name");
    }
}

private void ensurePriceIsPositive(Product product) {
    if (product.getSinglePrice() <= 0) {
        throw new IllegalArgumentException("Product price must be greater than zero");
    }
}

private void ensureCategoryIsValid(Product product) {
    if (!isValidCategory(product.getCategory())) {
        throw new IllegalArgumentException("Invalid product category: " + product.getCategory());
    }
}

private void ensureProductIsActive(Product product) {
    if (!product.isActive()) {
        throw new IllegalStateException("Product must be active");
    }
}

private boolean isValidCategory(String category) {
    return category != null && List.of("Electronics", "Books", "Clothing", "Food").contains(category);
}

5. ترجیح بده فقط یک Return داشته باشی

وجود چندین دستور return همیشه مشکل‌ساز نیست، اما می‌تونه باعث بروز خطا بشه؛ مخصوصاً وقتی که تست‌ها کامل و پوشش‌دهی خوبی نداشته باشن. داشتن یک نقطه خروجی مشخص باعث می‌شه منطق شرطی (branching logic) توی کدت شفاف‌تر بشه و قبل از انتشار راحت‌تر فرصت ریفکتور کردن داشته باشی.

// With multiple returns
private double calculateProductDiscount(Customer customer, int quantity) {
    if (customer.isPlatinum()) {
        if (quantity > 10) {
            return 0.25;
        }
        return 0.20;
    }

    if (customer.isGold()) {
        if (quantity > 10) {
            return 0.20;
        }
        return 0.15;
    }

    if (customer.isSilver()) {
        if (quantity > 10) {
            return 0.15;
        }
        return 0.10;
    }

    return 0.0;
}

کد بعدی

//With single return
private double calculateProductDiscount(Customer customer, int quantity) {
    double discount = 0.0;

    if (customer.isPlatinum()) {
        discount = (quantity > 10) ? 0.25 : 0.20;
    } else if (customer.isGold()) {
        discount = (quantity > 10) ? 0.20 : 0.15;
    } else if (customer.isSilver()) {
        discount = (quantity > 10) ? 0.15 : 0.10;
    }

    return discount;
}

6. از final برای متغیرها و پارامترها استفاده کن

در برنامه‌های مدرن، به‌خصوص وقتی با عملیات بیت‌محور حساس به عملکرد سروکار نداری، بهتره متغیرها و پارامترها رو final اعلام کنی. وقتی یک مقدار بهش اختصاص داده شد، دیگه نباید تغییر کنه. این کار باعث می‌شه اسم متغیر با هدفش همخوانی داشته باشه و احتمال اشتباه کمتر بشه.

البته گاهی متغیرها باید تغییر کنن (مثل همون چیزی که اسمشون نشون می‌ده)، اما این موارد باید استثنا باشن، نه قاعده.

۷. کلاس‌های Utility بدون Attribute

این نکته خودش واضحه. اگه به یک کلاس Utility ویژگی (attribute) اضافه می‌کنی، دیگه اون کلاس Utility محسوب نمی‌شه. کلاس‌های Utility باید فقط شامل عملیات استاتیک باشن که روی پارامترهایی که دریافت می‌کنن کار می‌کنن، مثل متدهای دستکاری رشته‌ها (toLowerCase، isEmpty) که تو خیلی از کتابخونه‌ها هستن.

۸. کلاس‌های Utility رو به کلاس‌های Instantiable منتقل کن

اگه چند کلاس Utility داری، یا یک کلاس Utility با متدهای زیاد، ممکنه وقتش رسیده باشه بعضی متدها یا حتی کل کلاس رو به کلاس‌های معمولی قابل نمونه‌سازی (instantiable) منتقل کنی. این روش نگهداری کد رو بهتر می‌کنه، تست واحد (unit testing) رو راحت‌تر می‌کنه، امکان Dependency Injection رو فراهم می‌کنه و با اصول طراحی شیءگرای SOLID همخوانی داره.

۹. پیچیدگی سایکلوماتیک (Cyclomatic Complexity) رو زیر ۵ نگه دار

پیچیدگی سایکلوماتیک یکی از معیارهای مهم کیفیت کده. نگه داشتن این عدد زیر ۵ باعث می‌شه کدت راحت‌تر فهمیده، تست و نگهداری بشه.

public String determineAccessLevel(User user, Resource resource) {
    if (user == null || resource == null) {
        return "DENIED";
    }

    if (!user.isActive()) {
        return "DENIED";
    }

    if (user.isAdmin()) {
        return "FULL_ACCESS";
    } else if (user.isManager() && resource.isInternal()) {
        return "limitED_ACCESS";
    } else if (user.isEmployee()) {
        switch (resource.getType()) {
            case "PUBLIC":
                return "READ_ONLY";
            case "CONFIDENTIAL":
                if (user.hasClearance()) {
                    return "limitED_ACCESS";
                } else {
                    return "DENIED";
                }
            default:
                return "DENIED";
        }
    }

    return "DENIED";
}


برای کاهش پیچیدگی، کد را به متدهای با نام مناسب تقسیم کن

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

public String determineAccessLevel(User user, Resource resource) {
    String accessLevel;

    if (isInvalid(user, resource)) {
        accessLevel = "DENIED";
    } else if (isAdmin(user)) {
        accessLevel = "FULL_ACCESS";
    } else if (isManagerAccess(user, resource)) {
        accessLevel = "limitED_ACCESS";
    } else if (isEmployee(user)) {
        accessLevel = getEmployeeAccessLevel(user, resource);
    } else {
        accessLevel = "DENIED";
    }

    return accessLevel;
}

private boolean isInvalid(User user, Resource resource) {
    return user == null || resource == null || !user.isActive();
}

private boolean isAdmin(User user) {
    return user.isAdmin();
}

private boolean isManagerAccess(User user, Resource resource) {
    return user.isManager() && resource.isInternal();
}

private boolean isEmployee(User user) {
    return user.isEmployee();
}

private String getEmployeeAccessLevel(User user, Resource resource) {
    switch (resource.getType()) {
        case "PUBLIC":
            return "READ_ONLY";
        case "CONFIDENTIAL":
            return user.hasClearance() ? "limitED_ACCESS" : "DENIED";
        default:
            return "DENIED";
    }
}


10. به جای Constants از Enums استفاده کن

استفاده از Enums به جای Constants چند مزیت مهم داره:

ایمنی نوع (Type safety): Enums در زمان کامپایل بررسی می‌شن و از اختصاص دادن مقادیر اشتباه جلوگیری می‌کنن.

محدوده‌بندی مقادیر (Scoped values): مقادیر Enum فقط در همون نوع Enum معتبر هستن و باعث نمی‌شن فضای نام جهانی آلوده بشه.

قابلیت توسعه (Extensibility): Enums می‌تونن فیلد، سازنده و متد داشته باشن و رفتارهای پیچیده‌تر مثل toString() یا fromString() رو ارائه بدن.

نگهداری آسان (Maintainability): اضافه کردن یا تغییر مقادیر Enum امن‌تر و واضح‌تر از ویرایش String Constants هست.

هماهنگی با ابزارها (Tool integration): Enums به خوبی با قابلیت‌های IDE مثل Autocomplete و Refactoring کار می‌کنن و با فریمورک‌هایی مثل Jackson یا JPA هم به راحتی ادغام می‌شن.

جلوگیری از استفاده نادرست (Prevents misuse): برخلاف Interface Constants، نمی‌تونی مقادیر Enumهای غیرمرتبط رو با هم قاطی کنی یا ازشون نمونه بسازی.

منبع: نوشته شده توسط Renan Schmitt

نام گالری فیلم و نام ویدیو
برچسب‌هامقالات جاوا
دسترسی سریع

فهرست مطلب

۱۱۰ نکته سریع برای نوشتن کد تمیز
ادامه مسیر

پیشنهاد جاواپرو

موضوع را با آموزش‌های مرتبط ادامه دهید و نکته‌های کاربردی را در مسیر خود نگه دارید

مشاهده دوره‌ها
مطالب پیشنهادی

ادامه مطالعه

xدسته‌بندی نشده
۱۱ دی ۱۳۹۸ مطالعه کوتاه

x

پادکست شماره 3 – راه های کسب درآمد از برنامه نویسی

مطالعه مطلب
پادکست شماره 3 - راه های کسب درآمد از برنامه نویسیپادکست
۲۹ اردیبهشت ۱۴۰۴ پادکست آموزشی

پادکست شماره 3 - راه های کسب درآمد از برنامه نویسی

در این پادکست برنامه نویسی به شیوه ها و راه های کسب درآمد از برنامه نویسی پرداخته ایم. یک کاغذ و قلم بردارید و به پادکست گوش بدید.

شنیدن پادکست
پادکست شماره 2 - چگونه وارد فضای برنامه نویسی شویم؟پادکست
۲۹ اردیبهشت ۱۴۰۴ پادکست آموزشی

پادکست شماره 2 - چگونه وارد فضای برنامه نویسی شویم؟

در این پادکست برنامه نویسی یاد میگیریم که چگونه وارد فضای برنامه نویسی شویم و تخصص برنامه نویسی را به دست آوریم.

شنیدن پادکست
جاواپرومرجع تخصصی یادگیری و توسعهٔ جاوا

تجربه‌ای منظم، ساده و حرفه‌ای برای یادگیری عمیق جاوا، Spring و مهندسی نرم‌افزار؛ قابل استفاده در موبایل، تبلت و دسکتاپ

یادگیری

همه دوره‌هاآموزش رایگانمسیر یادگیریمدرس‌ها

جاواپرو

درباره ماخدمات تخصصیدرخواست پروژهتدریس خصوصیبلاگ فنیرپورتاژ آگهی

پشتیبانی

تماس با ماپرسش‌های متداولقوانین و حریم خصوصیبروزرسانی‌های جاواپروثبت تیکت

مجوزها

نماد اعتمادنماد اعتماد
خرید امنپرداخت فقط در درگاه رسمیپشتیبانی آموزشیمسیر ارتباط شفاف و پیگیری‌پذیردسترسی پایداروب‌اپ واکنش‌گرا روی همه دستگاه‌ها
© ۱۴۰۵ جاواپرو؛ همه حقوق محفوظ استتوسعه توسط پایاپینگ
خانهدوره‌هاسبد