امروز : ۱۹ اردیبهشت ۱۴۰۴ (2025/05/09)

راهنمای Clean Code: اصول و تکنیک‌های کدنویسی تمیز

راهنمای Clean Code: اصول و تکنیک‌های کدنویسی تمیز
19 اردیبهشت 1404

clean code چیست و چرا اهمیت دارد؟

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

در clean code، از نام‌های مناسب برای متغیرها، فانکشن‌ها و … استفاده می‌شود. این کار علاوه بر اینکه سطح کیفیت کد را بالا می‌برد، باعث کاهش خطاها و صرفه‌جویی در زمان برای بررسی و یا ویرایش می‌شود.

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

 

 

اصول طلایی clean code که هر برنامه‌نویسی باید بداند

در سطح حرفه‌ای، اصول Clean Code نه‌تنها به خوانایی کد کمک می‌کند، بلکه به صورت مستقیم بر روی بازدهی کار تیمی، کیفیت نهایی اثر می گذارند. در این قسمت از این پست آموزشی، به اصول‌ اساسی clean code می‌پردازیم:

 

موثر، کارآمد و ساده

هر زمان که قصد داریم یک قابلیت جدید را به کدی اضافه کنیم یا راه‌حلی برای یک مساله‌ی خاص پیاده‌سازی کنیم، همیشه سه اصل ساده را در اولویت قرار می‌دهیم: موثر بودن، کارآمد بودن و سادگی

در قدم اول، کد ما باید موثر باشد. یعنی دقیقا مساله‌ای را که باید حل کند، به‌درستی حل کند. این ابتدایی‌ترین انتظار ما از کد است.
زمانی که مطمئن شدیم کدهایمان مساله را حل می‌کند، باید بررسی کنیم که این کدها چقدر بهینه، کار را  انجام می‌دهند.

آیا برنامه با مصرف منطقی منابع،  زمان و حافظه اجرا می‌شود؟

آیا می‌توان آن را سریع‌تر یا و در مصرف منابع بهینه‌تر کرد؟

برای ارزیابی کارایی یک کد، باید با مفاهیم پیچیدگی زمانی و مکانی Time & Space Complexity آشنا باشید. این مفاهیم مشخص می‌کنند که الگوریتم شما با افزایش ورودی چگونه رفتار می‌کند.

به عنوان مثال، فرض کنید بخواهیم مجموع اعداد یک لیست را محاسبه کنیم:

مثال ناکارآمد:

def sum_array_inefficient(arr):
    total = 0
    for i in range(len(arr)):
        total += arr[i]
    return total

مثال بهینه:

def sum_array_efficient(arr):
    return sum(arr)

در این نسخه، از تابع داخلی و بهینه‌ی ()sum استفاده شده است که به صورت مستقیم عملیات جمع را انجام می‌دهد. این هم کد را ساده‌تر کرده و هم در بسیاری از مواقع سریع‌تر اجرا می‌شود.

در نهایت، به مهم‌ترین و شاید چالش‌ برانگیزترین این سه اصل می‌رسیم: سادگی.

سادگی یک مفهوم ذهنی است، اما با بررسی بعضی از سوالات می‌توانیم به آن نزدیک شویم:

آیا می‌توان به‌سادگی فهمید هر خط از کد چه کاری انجام می‌دهد؟

آیا نام‌گذاری توابع و متغیرها به‌خوبی نشان می‌دهد که وظیفه‌شان چیست؟

آیا کد به‌درستی indentation دارد و از یک فرمت یکنواخت در سراسر پروژه پیروی می‌کند؟

آیا مستنداتی برای بخش‌های پیچیده‌ی برنامه وجود دارد؟ آیا کامنت‌گذاری به درستی انجام شده است؟

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

آیا می‌توان قابلیت جدیدی را اضافه کرد یا چیزی را حذف کرد بدون اینکه نیاز باشد بخش‌های زیادی از کد تغییر کند؟

آیا کد به‌ صورت ماژولار طراحی شده است و هر بخش مسئولیت خاصی دارد؟

آیا از کدهای مشترک و تکرارشونده جلوگیری شده است؟

با اولویت دادن به این سه اصل: موثر بودن، کارآمد بودن و سادگی، همیشه می‌توانیم چارچوبی برای نوشتن راه‌حل‌های بهتر در برنامه‌نویسی داشته باشیم.

 

فرمت و syntax

استفاده از فرمت و syntax یکدست در کل کد، یکی ازنکات اصلی در clean code است. چون یکپارچگی در قالب و نگارش کد باعث افزایش خوانایی، درک بهتر و کاهش خطاهای احتمالی می‌شود.

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

در اینجا، برخی از موارد مهم در خصوص فرمت و syntax را بررسی می‌کنیم:

ایندنت و فاصله‌گذاری (Indentation and Spacing)

نمونه اشتباه:

def my_func(a,b):
 result=a+b
 return result

نمونه درست:

def my_func(a, b):
    result = a + b
    return result

در این مثال ساده، هر دو نسخه از تابع یک عملکرد دارند، اما نسخه‌ی دوم با رعایت اصول فاصله‌گذاری و ایندنت، بسیار خواناتر و قابل‌فهم‌تر است.

 

 

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

syntax یکپارچه (consistent syntax)

تابع معمولی:

def multiply_by_three(x):
    return x * 3

تابع خلاصه:

multiply_by_two = lambda x: x * 2

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

در پروژه‌های بزرگ، تغییر مداوم سبک نگارش بین lambda و توابع معمولی می‌تواند خواندن و نگهداری کد را برای سایر اعضای تیم و یا کاربران دشوار کند.

برای اطمینان از رعایت فرمت و syntax استاندارد در پروژه‌ها، می‌توانید از ابزارهایی مانند linters و code formatters استفاده کنید.

یکدست بودن در سبک نام‌گذاری (consistent case conventions)

# camelCase
myName = "Ali"

# PascalCase
MyName = "Ali"

# snake_case
my_name = "Ali"

همه‌ی این روش‌ها در تعریف متغیر قابل قبول هستند، اما باید در سراسر پروژه یک روش مشخص انتخاب و از آن پیروی شود.
در زبان پایتون، طبق PEP8، توصیه می‌شود برای متغیرها و توابع از سبک snake_case استفاده شود و کلاس‌ها را با PascalCase بنویسیم.

 

نام‌گذاری (naming)

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

در ادامه، دو مثال ساده در پایتون داریم که اهمیت نام‌گذاری را به‌خوبی نشان می‌دهد:

مثال نام‌گذاری نامناسب:

def ab(a, b):
    x = 10
    y = a + b + x
    print(y)

ab(5, 3)

در این مثال، تابعی داریم که دو عدد را با مقدار ۱۰ جمع می‌کند و نتیجه را print می‌کند.
اما نام تابع (ab) و متغیرهای (x و y) هیچ اطلاعات مفیدی درباره عملکرد آن‌ها نمی‌دهد.
خواننده کد باید وارد جزئیات شود تا بفهمد دقیقا این تابع چه کاری انجام می‌دهد.

 

مثال نام‌گذاری واضح و معنادار:

def calculate_total_with_tax(base_price, tax_rate):
    BASE_TAX = 10
    total_with_tax = base_price + (base_price * (tax_rate / 100)) + BASE_TAX
    print(total_with_tax)

calculate_total_with_tax(50, 20)

در این نسخه، نام تابع و متغیرها به‌گونه‌ای انتخاب شده‌اند که دقیقا مشخص می‌کنند هر بخش از کد چه کاری انجام می‌دهد:

  • calculate_total_with_tax: مشخص است که محاسبه مبلغ کل همراه با مالیات است.
  • base_price, tax_rate, total_with_tax: نام‌هایی هستند که بیانگر نقش متغیر در کد هستند.
  • حتی BASE_TAX نیز با حروف بزرگ نوشته شده تا به‌عنوان یک مقدار ثابت مشخص باشد (بر اساس استانداردهای پایتون).

 

اختصار در برابر واضح بودن

در clean code، دستیابی به تعادل بین اختصار و واضح بودن  از اهمیت زیادی دارد.
در حالی که مختصر نگه داشتن کد می‌تواند به خوانایی کد کمک کند، واضح بودن نیز به همان اندازه اهمیت دارد.
نوشتن کدی بیش از حد فشرده و خلاصه میتواند باعث سردرگمی، خطا و درک سخت‌تر آن توسط دولوپر‌های دیگر شود.

در ادامه با دو مثال اهمیت تعادل بین اختصار و واضح بودن را نشان داده‌ایم:

مثال تابع مختصر:

count_vowels = lambda s: len([c for c in s.lower() if c in 'aeiou'])
print(count_vowels("hello world"))

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

مثال تابع واضح:

def count_vowels(s):
    vowels = 'aeiou'
    count = 0
    for char in s.lower():
        if char in vowels:
            count += 1
    return count

print(count_vowels("hello world"))

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

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

 

قابلیت استفاده مجدد (Reusability)

قابلیت استفاده مجدد از کد، یکی از مفاهیم مهم در مهندسی نرم‌افزار است که به توانایی استفاده چند باره از یک قطعه کد بدون نیاز به تغییر اشاره دارد.

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

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

مثال بدون قابلیت استفاده‌ی مجدد:

def calculate_circle_area(radius):
    PI = 3.14
    return PI * radius * radius

def calculate_rectangle_area(length, width):
    return length * width

def calculate_triangle_area(base, height):
    return (base * height) / 2

circle_area = calculate_circle_area(5)
rectangle_area = calculate_rectangle_area(4, 6)
triangle_area = calculate_triangle_area(3, 7)

print(circle_area, rectangle_area, triangle_area)

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

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

مثال پیاده‌سازی قابلیت استفاده‌ی مجدد:

def calculate_area(shape, *args):
    if shape == 'circle':
        radius, = args
        PI = 3.14
        return PI * radius * radius
    elif shape == 'rectangle':
        length, width = args
        return length * width
    elif shape == 'triangle':
        base, height = args
        return (base * height) / 2
    else:
        raise ValueError(f"Shape '{shape}' not supported.")

circle_area = calculate_area('circle', 5)
rectangle_area = calculate_area('rectangle', 4, 6)
triangle_area = calculate_area('triangle', 3, 7)

print(circle_area, rectangle_area, triangle_area)

در این مثال، تنها یک تابع با نام calculate_area تعریف شده است که با دریافت نوع شکل و تعدادی از پارامتر‌های متغیر، محسابه مربوطه را انجام می‌دهد.

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

 

جریان اجرای واضح (Clear Flow of Execution)

clear flow of execution برای کدنویسی تمیز ضروری است، زیرا باعث می‌شود کد خواناتر قابل فهم‌تر باشد. کدی که از ساختار مشخصی دارد، احتمال خطا در ویرایش آن کم‌تر می‌شود و تغییرات در آن به آسانی قابل انجام است.

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

مثال clear flow of execution:

def calculate_discount(price, discount_percentage):
    discount_amount = price * (discount_percentage / 100)
    discounted_price = price - discount_amount
    return discounted_price

original_price = 100
discount_percentage = 20
final_price = calculate_discount(original_price, discount_percentage)

print(final_price)

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

مثال spaghetti code:

original_price = 100
discount_percentage = 20

discounted_price = None
discount_amount = None

if original_price and discount_percentage:
    discount_amount = original_price * (discount_percentage / 100)
    discounted_price = original_price - discount_amount

if discounted_price:
    print(discounted_price)

مثال دوم بسایر در هم ریخته است. متغیرها بیرون از هر تابعی تعریف شده‌اند و چندین if برای بررسی اجرای موفق کد استفاده شده است. این روند باعث کاهش خوانایی کد و افزایش احتمال خطا در ویرایش آن می‌شود.

 

اصل تک‌مسئولیتی (single responsibility principle)

اصل تک مسئولیتی یکی از اصول clean code است که بیان کننده این است که هر کلاس، ماژول و یا هر قسمتی از کد باید تنها یک وظیفه مشخص داشته باشد.

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

مثال عدم رعایت SRP (Single Responsibility Principle):

class Database:
    def connect(self):
        print("Connecting to database...")

    def save_order(self, order, total):
        print(f"Saving order with total: {total}")

def process_order(order):
    if len(order['items']) == 0:
        print("خطا: سفارش هیچ آیتمی ندارد.")
        return

    total = 0
    for item in order['items']:
        total += item['price'] * item['quantity']

    if order['customer'] == "vip":
        total *= 0.9

    db = Database()
    db.connect()
    db.save_order(order, total)

در این مثال، تابع process_order چندین مسئولیت را به طور همزمان انجام می‌دهد: اعتبارسنجی، محاسبه مبلغ، اعمال تخفیف و ذخیره در دیتابیس. این باعث پیچیده شدن کد و ویرایش آن می‌شود.

مثال رعایت SRP (Single Responsibility Principle):

class OrderValidator:
    def __init__(self, order):
        self.order = order

    def is_valid(self):
        if len(self.order['items']) == 0:
            print("error")
            return False
        return True

class OrderCalculator:
    def __init__(self, order):
        self.order = order

    def calculate_total(self):
        total = 0
        for item in self.order['items']:
            total += item['price'] * item['quantity']
        return total

    def apply_discounts(self, total):
        if self.order['customer'] == "vip":
            total *= 0.9
        return total

class OrderSaver:
    def __init__(self, order, total):
        self.order = order
        self.total = total

    def save(self):
        db = Database()
        db.connect()
        db.save_order(self.order, self.total)

order = {
    'customer': 'vip',
    'items': [{'price': 100, 'quantity': 2}, {'price': 50, 'quantity': 1}]
}

validator = OrderValidator(order)
if validator.is_valid():
    calculator = OrderCalculator(order)
    total = calculator.calculate_total()
    total_with_discount = calculator.apply_discounts(total)

    saver = OrderSaver(order, total_with_discount)
    saver.save()

در این نسخه، وظایف به کلاس‌های جداگانه تقسیم شده‌اند:

  • OrderValidator: وظیفه‌ی اعتبارسنجی سفارش
  • OrderCalculator: وظیفه‌ی محاسبه‌ی مبلغ و اعمال تخفیف
  • OrderSaver: ذخیره‌ی سفارش در پایگاه داده

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

 

داشتن یک سورس مبدا برای اطلاعات (single source of truth)

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

فرض می‌کنیم که یک پروژه داریم که شرایط آب و هوای فعلی شهر را نمایش می‌دهد. این قابلیت را می‌توان به دو شکل پیاده سازی کرد:

مثال عدم استفاده از سورس مبدا:

# weather_api.py
api_key = '12345abcde'

def get_current_weather(city):
    url = f"https://api.weather.com/conditions/v1/{city}?apiKey={api_key}"
    return f"Weather data for {city}"
# weather_component.py
api_key = '12345abcde'

from weather_api import get_current_weather

def display_weather(city):
    weather_data = get_current_weather(city)
    print(weather_data)

در این روش، مقدار api_key در دو فایل جداگانه تعریف شده است. اگر بخواهیم api_key را تغییر دهیم باید در دو مرحله آن را ویرایش کنیم.این موضوع می‌تواند باعث ایجاد خطا شود.

مثال استفاده از سورس مبدا:

# config.py
API_KEY = '12345abcde'
# weather_api.py
from config import API_KEY

def get_current_weather(city):
    url = f"https://api.weather.com/conditions/v1/{city}?apiKey={API_KEY}"
    return f"Weather data for {city}"
# weather_component.py
from weather_api import get_current_weather

def display_weather(city):
    weather_data = get_current_weather(city)
    print(weather_data)

در این نسخه، API_KEY فقط در یک فایل config.py تعریف شده است. هر فایل دیگری هم که به API_KEY لازم داشته باشد می‌تواند آن را ایمپورت و استفاده کند. اگر زمانی نیاز به ویرایش API_KEY بود، کافیست از یک فایل آن را ویرایش کرد.

استفاده از سورس مبدا در پروژه‌بزرگ بسیار اهمیت دارد. مثلا در پروژه هایی که در آن API یا متغیرهایی تعریف شده‌اند که در سراسر پروژه در حال استفاده هستند، تغییر این متغیر‌ها در کل پروژه، کار سختی می‌تواند باشد. اما با استفاده از سورس مبدا تنها این متغیرها را از یک فایل تغییر می‌کنند.

 

ماژولار کردن (Modularization)

ماژولار کردن کد یکی از مفاهیم اصلی در clean code می‌باشد. منظور از ماژولار کردن، تقسیم کدهای پیچیده و بزرگ به بخش‌های کوچک‌تر و قابل مدیریت‌تر است. این کار باعث می‌شود کد خواناتر و تست پذیرتر شود.

مزایای ماژولار کردن:

  1. Re-usability: توابع و ماژول‌ها را می‌توان در بخش‌های دیگر پروژه یا در پروژه‌های مختلف استفاده کرد.
  2. Encapsulation: جزییات داخلی یک تابع یا کلاس پنهان می‌شوند و فقط رابط موردنیاز ارائه می‌شود. این کار وابستگی بین اجزای مختلف کد را کاهش می‌دهد.
  3. Scalability: با تقسیم کد به اجزای کوچک‌تر، افزودن یا حذف قابلیت‌ها بدون تاثیر روی کل پروژه ممکن می‌شود.

مثال غیر ماژولار:

def calculate_price(quantity, price, tax):
    subtotal = quantity * price
    total = subtotal + (subtotal * tax)
    return total

quantity = int(input("Enter quantity: "))
price = float(input("Enter price: "))
tax = float(input("Enter tax rate: "))

total = calculate_price(quantity, price, tax)
print(f"Total: ${total:.2f}")

در این مثال، تابع calculate_price چند وظیفه را به‌طور هم‌زمان انجام می‌دهد: محاسبه قیمت، افزودن مالیات و ارتباط مستقیم با ورودی و خروجی. که باعث می‌شود فهم و ویرایش کد سخت‌تر شود.

مثال ماژولار:

def calculate_subtotal(quantity, price):
    return quantity * price

def calculate_total(subtotal, tax):
    return subtotal + (subtotal * tax)

quantity = int(input("Enter quantity: "))
price = float(input("Enter price: "))
tax = float(input("Enter tax rate: "))

subtotal = calculate_subtotal(quantity, price)
total = calculate_total(subtotal, tax)
print(f"Total: ${total:.2f}")

در مثال ماژولار شده، هر تابع مسئول انجام یک وظیفه‌ی خاص است. این ساختار باعث می‌شود کد قابل‌فهم‌تر باشد.

همچنین، ماژولار کردن فقط به توابع محدود نمی‌شود؛ می‌توان فایل‌های مختلفی را ایجاد کرد (مثلا یک فایل برای محاسبات، یک فایل برای ورودی، خروجی و یک فایل برای تنظیمات) و در صورت نیاز آن‌ها را در یکدیگر ایمپورت کرد. این اصل clean code در پروژه‌های بزرگ‌تر به‌طور گسترده استفاده می‌شود.

 

ساختار فولدرها

انتخاب یک فولدر بندی مناسب، بخش مهمی از clean code است. داشتن ساختاری منظم برای پروژه کمک می‌کند تا برنامه نویسان راحت‌تر کد را پیدا و ویرایش کنند و توسعه‌پذیری بیشتری پیدا کند.

در مقابل، فولدر بندی نامناسب می‌تواند باعث سردرگمی برنامه نویس شود.

مثال فولدر بندی نامناسب:

my_project/
├── main.py
├── utils.py
├── db.py
├── user.py
├── product.py
├── user_routes.py
└── product_routes.py

در این مثال، فایل‌ها بر اساس (type-based) سازمان‌دهی شده‌اند، اما رابطه‌ منطقی مشخصی بین آن‌ها وجود ندارد. مثلا فایل‌های مربوط به کاربر (user.py, user_routes.py) از هم جدا شده‌اند، که در پروژه‌های بزرگ باعث سردرگمی می‌شود.

مثال فولدر بندی مناسب:

my_project/
├── app/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── product.py
│   ├── routes/
│   │   ├── __init__.py
│   │   ├── user_routes.py
│   │   └── product_routes.py
│   ├── services/
│   │   ├── __init__.py
│   │   ├── user_service.py
│   │   └── product_service.py
│   ├── utils/
│   │   ├── __init__.py
│   │   └── helpers.py
│   └── db/
│       ├── __init__.py
│       └── connection.py
├── tests/
│   ├── test_user.py
│   └── test_product.py
├── main.py
└── requirements.txt

در این مثال، ساختار فولدرها بر اساس ویژگی‌ها و ماژول‌ها (feature-based) شکل گرفته است:

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

مزایای این ساختار:

جست‌و‌جوی فایلی در پروژه: به راحتی می‌توان فایل‌های موردنظر را پیدا کرد.

کاهش پیچیدگی: از پراکندگی فایل‌ها جلوگیری می‌شود.

مقیاس‌پذیری: افزودن ویژگی‌های جدید فقط با اضافه‌کردن ماژول‌های جدید در فولدرهای مناسب انجام می‌شود.

در پروژه‌های بزرگ، مخصوصا در فریم‌ورک‌هایی مثل Django یا FastAPI، فولدر بندی واضح تأثیر زیادی در عیب یابی و صرفه‌جویی در وقت دارد.

 

ایجاد داکیومنت

ایجاد داکیومنت برای پروژه، بخشی جدایی‌ناپذیر از clean code است. داکیومنت مناسب نه‌تنها به برنامه نویس کمک می‌کند تا در آینده کدش را بهتر درک کند، بلکه فهم کد را برای دیگر اعضای تیم نیز ساده‌تر می‌سازد. کدی که به‌درستی مستندسازی شده باشد، در زمان عیب‌یابی و توسعه، صرفه‌جویی زیادی در زمان و انرژی ایجاد می‌کند.

ایجاد داکیومنت در موارد زیر اهمیت بیشتری پیدا می‌کند:

  • زمانی‌که راه‌حل ساده‌ای برای یک مشکل وجود ندارد.
  • زمانی‌که منطق کسب‌وکار پیچیده است.
  • زمانی‌که برنامه نویس‌هایی که با کد آشنا نیستند باید با آن کار کنند.

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

مثال کامنت‌های بی‌فایده و غیرضروری:

# Define a function
def add(a, b):  # This function adds two numbers
    return a + b  # It returns the sum

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

مثال کامنت گذاری مناسب:

def calculate_discounted_price(price, customer_type):
    """
    Calculates the final price after applying discounts based on customer type.
    """
    if customer_type == "vip":
        # VIP customers get a 20% discount
        return price * 0.8
    elif customer_type == "regular":
        # Regular customers get a 10% discount
        return price * 0.9
    else:
        # No discount for other customer types
        return price

در این مثال، ایجاد داکیومنت با docstring برای تابع انجام شده که کارکرد کلی تابع را شرح می‌دهد. همچنین برای بخش های مختلف کد نیز، کامنت‌های هدفمند نوشته شده‌ است.

 

 

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

اصول clean code شامل رعایت سادگی، کارایی و اثرگذاری، استفاده از فرمت و syntax یکسان، نام‌گذاری معنادار، حفظ قابلیت استفاده مجدد کد، رعایت اصول SOLID و ماژولار بودن کد هستند. این اصول باعث می‌شوند که کدهای نوشته‌شده نه تنها کارآمد و بهینه باشند، بلکه تغییرات و افزودن ویژگی‌های جدید در آینده نیز به راحتی امکان‌پذیر باشد.

5/5 - (6 امتیاز)