| کانال توسعه‌دهندگان |
1.75K subscribers
55 photos
1 video
30 links
⭕️ کانال توسعه‌دهندگان دولوپیکس

💠 دولوپیکس | جامعه توسعه‌دهندگان ایرانی

💎 @Developix
🚀 Developix.ir

📌 پشتیبانی و تبلیغات:
@DevelopixSupport
Download Telegram
اصل Single Responsibility: هر کلاس فقط یک دلیل برای تغییر 💡

یکی از مهم‌ترین اصول SOLID که واقعاً در پروژهٔ روزمره جواب می‌دهد، Single Responsibility Principle (SRP) است:

هر ماژول، کلاس یا فانکشن باید فقط یک مسئولیت داشته باشد.

یعنی چه؟ یعنی اگر فردا قرار باشد این کلاس تغییر کند، فقط یک «دلیل منطقی» برای آن وجود داشته باشد، نه چند دلیل مختلف و نامربوط.

مثال ساده ⬇️

class OrderService:
def create_order(self, data):
# 1) اعتبارسنجی دیتا
# 2) ذخیره در دیتابیس
# 3) ارسال ایمیل تایید
pass


این کلاس ۳ مسئولیت دارد:

• منطق بیزینسی سفارش 🧠
• دسترسی به دیتابیس 💾
• ارسال ایمیل 📧

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

ری‌فکتور با SRP ✅

class OrderRepository:
def save(self, order):
pass

class EmailService:
def send_order_confirmation(self, order):
pass

class OrderService:
def __init__(self, repo, email_service):
self.repo = repo
self.email_service = email_service

def create_order(self, data):
# منطق سفارش
order = self._build_order(data)
self.repo.save(order)
self.email_service.send_order_confirmation(order)


حالا:

• تغییر دیتابیس فقط OrderRepository را درگیر می‌کند.
• تغییر متن/قوانین ایمیل فقط در EmailService انجام می‌شود.
• خود OrderService روی منطق سفارش تمرکز می‌کند.

چرا این کار در عمل ارزش دارد؟

• تست کردن راحت‌تر می‌شود؛ چون می‌توان EmailService و OrderRepository را mock کرد و فقط منطق سفارش را تست گرفت ✅
• تغییرات بیزینسی، UI یا زیرساخت، بخش‌های جداگانه را لمس می‌کند، نه کل سیستم را 🔧
• خوانایی بالاتر: هر کلاس را که باز می‌کنی، تقریباً می‌توانی با خواندن نامش حدس بزنی چه کاری انجام می‌دهد 👀

نشانه‌های نقض SRP (Code Smell) 🚨

• کلاس‌هایی با نام‌های مبهم مثل Manager، Helper، Util که همه کار می‌کنند.
• فانکشن‌هایی که ۱۰۰+ خط کد دارند و هم validate می‌کنند، هم log، هم call به API.
• هر بار که فیچر جدیدی می‌آید، مجبور می‌شوی همان فایل‌های قدیمی «همه‌فن‌حریف» را باز کنی.

لینک‌های مفید برای مطالعهٔ بیشتر:
Refactoring.Guru - Single Responsibility Principle
Martin Fowler - Single Responsibility Principle

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

🔖 #Programming #برنامه_نویسی #Clean_Code #SOLID #SRP #Software_Architecture #Refactoring #Python

👤 Developix

💎 Channel: @DevelopixChannel
❤4🔥2
اصل Single Responsibility در عمل 🧩

یکی از مهم‌ترین اصول SOLID که عملاً هر روز سر و کار داریم، Single Responsibility Principle (SRP) است:

«هر کلاس یا ماژول باید فقط یک دلیل برای تغییر داشته باشد.»

این یعنی:

• هر قطعه کد روی یک کار مشخص تمرکز کند.
• وقتی نیاز به تغییر یک رفتار داریم، فقط به یک جای مشخص سر بزنیم.

چرا مهم است؟

• کد قابل فهم‌تر می‌شود.
• Bugها کمتر در جاهای نامربوط نشت می‌کنند.
• تست‌ها ساده‌تر و Refactor کردن کم‌ریسک‌تر می‌شود. ✅

نمونه کد بدون رعایت SRP 😵

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

def process_order(request):
# 1) validate input
if not request.get("user_id"):
print("[ERROR] missing user_id")
return

# 2) calculate price
total = 0
for item in request["items"]:
total += item["price"] * item["qty"]

# 3) log result
print(f"[INFO] user={request['user_id']} total={total}")

# 4) save to db (fake)
db["orders"].append({"user": request["user_id"], "total": total})


این تابع حداقل چهار مسئولیت دارد؛ هر تغییر کوچکی (مثلاً فرمت لاگ یا ساختار دیتابیس) باعث تغییر در همین تابع می‌شود.

تقسیم به مسئولیت‌های جدا ✂️

با SRP، مسئولیت‌ها را جدا می‌کنیم:

def validate_request(request):
if not request.get("user_id"):
raise ValueError("missing user_id")


def calculate_total(items):
return sum(i["price"] * i["qty"] for i in items)


def log_order(user_id, total, logger):
logger.info(f"user={user_id} total={total}")


def save_order(user_id, total, repo):
repo.save({"user": user_id, "total": total})


def process_order(request, logger, repo):
validate_request(request)
total = calculate_total(request["items"])
log_order(request["user_id"], total, logger)
save_order(request["user_id"], total, repo)


حالا:

• منطق محاسبه قیمت جداست؛ اگر تخفیف یا Tax اضافه شود، فقط همان بخش را تغییر می‌دهیم.
• تغییر سیستم لاگ یا دیتابیس، منطق دامنه را دست‌کاری نمی‌کند.
• تست کردن calculate_total و validate_request به‌صورت واحد بسیار ساده است. 🧪

نکتهٔ مهم

SRP را افراطی تفسیر نکنید؛ منظور این نیست برای هر خط یک تابع بسازیم. معیار خوب این است:

• وقتی می‌گویید «این کلاس/تابع کارش چیه؟»، بتوانید با یک جملهٔ کوتاه جواب بدهید.

برای مطالعهٔ بیشتر:

• کتاب Clean Code – Robert C. Martin
• توضیح SRP در Refactoring.Guru: refactoring.guru/design-patterns/solid

این اصل به‌ظاهر ساده، اگر واقعاً در هر Pull Request به آن فکر شود، کیفیت بلندمدت کدبیس را عوض می‌کند. 🌱

🔖 #Programming #برنامه_نویسی #SRP #SOLID #clean_code #refactoring #architecture #Python

👤 Developix

💎 Channel: @DevelopixChannel
🔥4
اصل Single Responsibility (SRP) از اصول معروف SOLID می‌گوید:

«هر ماژول، کلاس یا تابع باید فقط یک دلیل برای تغییر داشته باشد.» ✅

یعنی چی؟ یعنی یک کلاس نباید هم منطق کسب‌وکار را انجام دهد، هم لاگ بنویسد، هم با دیتابیس صحبت کند، هم ایمیل بفرستد. این دقیقاً همان جایی است که کد شروع به بو دادن می‌کند (Code Smell) 😅

نمونه‌ی بد: کلاس همه‌کاره
class UserService:
def register(self, user_data):
# validate
if not user_data["email"]:
raise Exception("email required")

# save to db
db.save(user_data)

# send welcome email
email_client.send(user_data["email"], "welcome")

# log
logger.info("user registered")


اینجا UserService چند مسئولیت دارد:

• validation ✅
• ذخیره در دیتابیس 🗄️
• ارسال ایمیل 📧
• logging 📜

تست کردنش سخت می‌شود، تغییر هر بخش روی بقیه اثر می‌گذارد، و هر تغییری ریسک Bug را بالا می‌برد.

بازطراحی با SRP
class UserValidator:
def validate(self, user_data):
if not user_data["email"]:
raise Exception("email required")

class UserRepository:
def save(self, user_data):
db.save(user_data)

class EmailNotifier:
def send_welcome(self, email):
email_client.send(email, "welcome")

class UserService:
def __init__(self, validator, repo, notifier, logger):
self.validator = validator
self.repo = repo
self.notifier = notifier
self.logger = logger

def register(self, user_data):
self.validator.validate(user_data)
self.repo.save(user_data)
self.notifier.send_welcome(user_data["email"])
self.logger.info("user registered")


حالا:

• هر کلاس یک مسئولیت واضح دارد ✔️
• تست واحد برای هر بخش ساده‌تر است ✔️
• تغییر ایمیل یا دیتابیس، UserService را نمی‌شکند ✔️

چند نکته‌ی کاربردی 🔍

• اگر در توضیح یک کلاس مجبورید از «و» زیاد استفاده کنید (این کلاس X و Y و Z انجام می‌دهد)، احتمالاً SRP نقض شده.
• متدهایی که بیش از حد طولانی‌اند و کارهای ناهمگن انجام می‌دهند، نامزد جدی Refactor هستند.
• SRP کمک می‌کند معماری تمیزتر داشته باشید و Design Patternها (مثل Strategy، Repository) را راحت‌تر پیاده کنید.

برای مطالعه‌ی بیشتر می‌توان به Refactoring.Guru - SRP یا فصل‌های ابتدایی کتاب Clean Code سر زد.

اجرای مستمر SRP روی کدهای روزمره، در بلندمدت پروژه‌ را قابل‌تغییرتر، تست‌پذیرتر و کم‌استرس‌تر می‌کند. از همین الان روی یکی از فایل‌های شلوغ پروژه شروع شود و مسئولیت‌ها را جدا کنید ✨

🔖 #Programming #برنامه_نویسی #Clean_Code #SOLID #SRP #Refactoring #Architecture #Design_Principles #Python

👤 Developix

💎 Channel: @DevelopixChannel
🧪 استفاده از assert برای گرفتن باگ‌ها، قبل از دیر شدن

خیلی وقت‌ها یک فرض توی کد داریم که "مطمئنیم" همیشه برقرار است؛ مثلا این‌که ورودی خالی نیست یا مقدار نال نمی‌آید. این‌جور جاها assert دوست خوب ماست 😄

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

یک مثال ساده در Python:
def average(nums: list[float]) -> float:
assert len(nums) > 0, "nums must not be empty"
return sum(nums) / len(nums)

print(average([1, 2, 3])) # 2.0
print(average([])) # AssertionError


نکته مهم 👇
- در Python اگر با فلگ -O اجرا شود، assertها حذف می‌شوند. پس برای validation ورودی کاربر از assert استفاده نمی‌شود؛ فقط برای چک‌کردن منطق داخلی و فرضیات برنامه‌نویس مناسب است.

امروز یکی از فرض‌های مهم کدت را با یک assert ساده محکم‌تر کن و نتیجه‌اش را در باگ‌های آینده ببین 🤝

📚 مرجع: Python Docs - assert statement

🔖 #Programming #برنامه_نویسی #assert #debugging #python #clean_code #defensive_programming

👤 Developix

💎 Channel: @DevelopixChannel
👍3🔥1
‏اصل Single Responsibility (SRP)؛ وقتی کلاس‌ همه‌کاره خستگی‌ات می‌کند 😅

یکی از مهم‌ترین اصول SOLID، اصل Single Responsibility است:

هر کلاس یا ماژول باید فقط یک دلیل برای تغییر داشته باشد.

یعنی چی؟ یعنی یک کلاس نباید هم منطق دامنه را نگه دارد، هم log بنویسد، هم با دیتابیس کار کند، هم ایمیل بفرستد. این کلاس‌ها هم سخت تست می‌شوند، هم کوچک‌ترین تغییر، نصف سیستم را می‌ترکاند 🔥


❌ مثال بد: کلاس خدا (God Class)
class OrderService:
def create_order(self, data):
# 1) اعتبارسنجی ورودی
if not data.get("user_id"):
raise Exception("user_id required")

# 2) محاسبه قیمت
total = 0
for item in data["items"]:
total += item["price"] * item["qty"]

# 3) ذخیره در دیتابیس
db.save({"user_id": data["user_id"], "total": total})

# 4) ارسال ایمیل
EmailClient().send(
to=data["email"],
subject="Order created",
body=f"Total = {total}"
)

# 5) لاگ کردن
Logger().info("Order created", extra={"total": total})


این کلاس حداقل ۵ مسئولیت دارد. هر تغییری در validation، ایمیل، لاگ یا ساختار دیتابیس، این کلاس را درگیر می‌کند. تست واحد هم کابوس می‌شود 🤯


✅ بازطراحی با SRP

ایده این است که هر ماژول روی یک چیز متمرکز شود:

class OrderValidator:
def validate(self, data):
if not data.get("user_id"):
raise Exception("user_id required")

class PriceCalculator:
def calculate(self, items):
total = 0
for item in items:
total += item["price"] * item["qty"]
return total

class OrderRepository:
def save(self, user_id, total):
db.save({"user_id": user_id, "total": total})

class OrderNotifier:
def send_order_created(self, email, total):
EmailClient().send(
to=email,
subject="Order created",
body=f"Total = {total}"
)

class OrderService:
def __init__(self, validator, calculator, repo, notifier, logger):
self.validator = validator
self.calculator = calculator
self.repo = repo
self.notifier = notifier
self.logger = logger

def create_order(self, data):
self.validator.validate(data)
total = self.calculator.calculate(data["items"])
self.repo.save(data["user_id"], total)
self.notifier.send_order_created(data["email"], total)
self.logger.info("Order created", extra={"total": total})


حالا هر کلاس یک نقش نسبتاً واضح دارد. تغییر منطق قیمت، ایمیل یا دیتابیس، فقط همان بخش را تحت تاثیر قرار می‌دهد. تست واحد هم راحت‌تر است، چون می‌شود هر جزء را جداگانه mock و تست کرد ✅


چطور در عمل از SRP استفاده شود؟

• وقتی متدی خیلی طولانی می‌شود، معمولاً یعنی چند مسئولیت دارد.
• وقتی کلاس‌ مدام به خاطر انواع تغییرها ویرایش می‌شود، یعنی باید تفکیک شود.
• نام کلاس اگر شبیه "Manager"، "Helper"، "Util" باشد، اغلب بوی نقض SRP می‌دهد 😄

مرجع خوب برای مطالعه عمیق‌تر:
refactoring.guru/design-principles/srp

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

🔖 #Programming #برنامه_نویسی #SRP #SOLID #Clean_Code #Refactoring #Design_Principles #Python

👤 Developix

💎 Channel: @DevelopixChannel
👍1
‏قانون Demeter (Law of Demeter) چیه و چرا اینقدر مهمه؟ 🤐

یکی از اصول قدیمی ولی فوق‌العاده کاربردی در طراحی شی‌گرا، Law of Demeter یا همون "Talk only to your friends" ـه.

به زبان ساده: هر آبجکت فقط باید با "همسایه‌های نزدیکش" حرف بزنه، نه با نصف سیستم.

قاعدهٔ اصلی:
هر متد فقط باید با این‌ها تعامل داشته باشه:

• خودِ شی (this)

• پارامترهای ورودی متد

• فیلدهای خودش

• آبجکت‌هایی که خودش می‌سازه

هر چیزی غیر از این، بوی Coupling بالا و دردسر در ریفکتور می‌ده 😄

یک مثال بد 👎 (Law of Demeter نقض شده)
order.customer.address.city


توی کد واقعی (مثال Python):
class OrderService:
def get_customer_city(self, order):
return order.customer.address.city


اینجا OrderService داره نصف مدل دامنه رو می‌گرده:

• اگر ساختار Customer عوض بشه، اینجا هم می‌ترکه.

• اگر Address حذف بشه یا تغییر کنه، باز این کلاس می‌شکنه.

نسخهٔ بهتر ✅ (رعایت Law of Demeter)

مسیر طولانی رو "کپسوله" می‌کنیم:
class Customer:
def get_city(self):
return self.address.city

class Order:
def get_customer_city(self):
return self.customer.get_city()

class OrderService:
def get_customer_city(self, order):
return order.get_customer_city()


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

چرا این قانون در عمل خیلی کمک می‌کنه؟ 💡

• کاهش Coupling: کلاس‌ها کمتر از ساختار داخلی هم خبر دارن.

• انعطاف در ریفکتور: راحت می‌تونی مدل داده رو عوض کنی بدون اینکه نصف سرویس‌ها رو دست بزنی.

• بهبود تست‌پذیری: با Mock کردن چند آبجکت محدود می‌تونی یونیت‌تست بنویسی، بدون زنجیره‌های عجیب.

بوی بد معروف: "Train Wreck" 🚂

هر وقت این الگو رو دیدی:
a.b().c.d().e


تقریباً مطمئنی که Law of Demeter نقض شده. این همون Train Wreck ـه؛ قطار نقطه‌ها.

چند نکتهٔ عملی برای کدنویسی روزمره

• اگر . زیاد پشت سر هم می‌بینی، معمولاً یعنی لازمه چندتا متد کوچک "واسط" اضافه کنی.

• به جای expose کردن کل آبجکت‌های داخلی، متدهای سطح بالاتر ارائه کن (مثل getCustomerCity()).

• توی Serviceها از Facade-های کوچک استفاده کن تا از جزئیات Domain دور نگه‌شون داری.

منابع بیشتر 📚

• Martin Fowler — Law of Demeter
• Refactoring Guru — Message Chains

از همین امروز، هر جا "قطار نقطه‌ها" دیدی، یک مکث کوتاه و یک ریفکتور کوچک می‌تونه معماری کدت رو حسابی تمیزتر کنه 🌱

🔖 #Programming #برنامه_نویسی #Law_of_Demeter #Clean_Code #Coupling #Design_Principles #OOP #Refactoring #Architecture

👤 Developix

💎 Channel: @DevelopixChannel
👍6
نام‌گذاری تمیز؛ اولین خط دفاع در Clean Code 🧹

یکی از عملی‌ترین ایده‌های کتاب Clean Code این است که «اسم خوب، نصف راه خوانایی کد است». خیلی از بدهی‌های فنی فقط با نام‌گذاری درست اصلاً به‌وجود نمی‌آیند.

👀 نشانه‌های نام‌گذاری بد

وقتی متغیر و تابع این‌طوری هستند:

def p(u):
if len(u) > 0:
for x in u:
if x[3] == 1:
send(x)


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

✅ چطور بهترش کنیم؟

۱️⃣ اسم باید «بگوید» چه‌کار می‌کند

def send_active_users(users):
if len(users) == 0:
return

for user in users:
if user.is_active():
send(user)


نام تابع، ورودی و شرط‌ها واضح شده‌اند. نیاز کمتری به کامنت داریم، چون خود کد توضیح‌دهنده است.

۲️⃣ از اسم‌های عمومی مثل data, info, tmp دوری کن

# بد
result = process(data)

# بهتر
normalized_orders = normalize_orders(raw_orders)


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

۳️⃣ اسم تابع را فعل، اسم متغیر را اسم بگذار

# تابع با اسم اسمی (بدفهم)
user_status = user()


# واضح‌تر
status = get_user_status()


این الگوی ساده کمک می‌کند بدون نگاه به بدنه، از روی اسم‌ها بفهمیم هر چیزی «چیز است» یا «کار».

۴️⃣ از abbreviation بی‌معنی خودداری کن

# بد
cfg = read_cfg(pth)

# بهتر
config = read_config_file(path)


خودمان امروز معنای cfg را می‌فهمیم، ۶ ماه بعد یا برای همکار جدید، این تبدیل می‌شود به رمز.

۵️⃣ اسم‌ها را بر اساس domain انتخاب کن

استفاده از واژه‌های همان دامنه (Order, Invoice, Payment, Discount) کد را به زبان بیزینس نزدیک می‌کند و فهم سیستم را برای همه‌ی تیم آسان‌تر می‌کند.

📚 برای مطالعه بیشتر:
Clean Code, Chapter 2: Meaningful Names
Refactoring Guru - Code Smells

در هر refactor کوچک، فقط با بهتر کردن اسم‌ها می‌شود کیفیت طراحی را بالا برد؛ بدون تغییر رفتار کد. از همین commit بعدی، روی هر اسم چند ثانیه بیشتر فکر شود، بازدهش در بلندمدت شگفت‌انگیز است ✨

🔖 #Programming #برنامه_نویسی #clean_code #naming #meaningful_names #refactoring #readability #architecture

👤 Developix

💎 Channel: @DevelopixChannel
❤4