کتابخونهٔ معروف Requests توی Python عملاً استانداردِ کار با HTTP شده؛ بهجای درگیر شدن با urllib و جزئیات خستهکننده، با چند خط کد میشه API صدا زد و JSON گرفت و فرستاد. 😉
Requests مشکل «کد شلوغ و پر از boilerplate برای request ساده» رو حل میکنه و خوندن/دیباگکردن کدهای مرتبط با شبکه رو خیلی راحتتر میکنه.
ویژگیهای مهم:
• سینتکس خیلی خوانا و نزدیک به زبان طبیعی 🧠
• پشتیبانی راحت از headers، query params، timeout، session و cookie
• هندل سادهٔ JSON و status code ها
• مناسب برای کار با REST API، اسکریپتهای اتوماسیون و crawlerهای سبک
برای پروژههایی مثل مانیتورینگ سرویسها، نوشتن bot، یا اتصال سریع به سرویسهای third-party، Requests انتخاب امن و پرکاربردیه. 🤝
Docs رسمی:
requests.readthedocs.io
GitHub:
github.com/psf/requests
امتحانش کن و کمکم هر جا اسم HTTP تو Python بیاد، یاد Requests میافتی. 🚀
🔖 #Programming #برنامه_نویسی #Python #HTTP #Requests #Web #API
👤 Developix
💎 Channel: @DevelopixChannel
Requests مشکل «کد شلوغ و پر از boilerplate برای request ساده» رو حل میکنه و خوندن/دیباگکردن کدهای مرتبط با شبکه رو خیلی راحتتر میکنه.
ویژگیهای مهم:
• سینتکس خیلی خوانا و نزدیک به زبان طبیعی 🧠
• پشتیبانی راحت از headers، query params، timeout، session و cookie
• هندل سادهٔ JSON و status code ها
• مناسب برای کار با REST API، اسکریپتهای اتوماسیون و crawlerهای سبک
pip install requests
import requests
resp = requests.get("https://api.github.com/repos/psf/requests")
if resp.status_code == 200:
data = resp.json()
print(data["full_name"], data["stargazers_count"])
برای پروژههایی مثل مانیتورینگ سرویسها، نوشتن bot، یا اتصال سریع به سرویسهای third-party، Requests انتخاب امن و پرکاربردیه. 🤝
Docs رسمی:
requests.readthedocs.io
GitHub:
github.com/psf/requests
امتحانش کن و کمکم هر جا اسم HTTP تو Python بیاد، یاد Requests میافتی. 🚀
🔖 #Programming #برنامه_نویسی #Python #HTTP #Requests #Web #API
👤 Developix
💎 Channel: @DevelopixChannel
اصل Single Responsibility (SRP) از اصول معروف SOLID میگوید:
«هر ماژول، کلاس یا تابع باید فقط یک دلیل برای تغییر داشته باشد.» ✅
یعنی چی؟ یعنی یک کلاس نباید هم منطق کسبوکار را انجام دهد، هم لاگ بنویسد، هم با دیتابیس صحبت کند، هم ایمیل بفرستد. این دقیقاً همان جایی است که کد شروع به بو دادن میکند (Code Smell) 😅
نمونهی بد: کلاس همهکاره
اینجا
• validation ✅
• ذخیره در دیتابیس 🗄️
• ارسال ایمیل 📧
• logging 📜
تست کردنش سخت میشود، تغییر هر بخش روی بقیه اثر میگذارد، و هر تغییری ریسک Bug را بالا میبرد.
بازطراحی با SRP
حالا:
• هر کلاس یک مسئولیت واضح دارد ✔️
• تست واحد برای هر بخش سادهتر است ✔️
• تغییر ایمیل یا دیتابیس،
چند نکتهی کاربردی 🔍
• اگر در توضیح یک کلاس مجبورید از «و» زیاد استفاده کنید (این کلاس 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
«هر ماژول، کلاس یا تابع باید فقط یک دلیل برای تغییر داشته باشد.» ✅
یعنی چی؟ یعنی یک کلاس نباید هم منطق کسبوکار را انجام دهد، هم لاگ بنویسد، هم با دیتابیس صحبت کند، هم ایمیل بفرستد. این دقیقاً همان جایی است که کد شروع به بو دادن میکند (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
🚀 ساخت یک REST API ساده با FastAPI (ولی حرفهای!)
FastAPI اینقدر سریع و تمیزه که برای ساخت REST APIهای کوچک تا سرویسهای جدی تولید (Production) عالی جواب میده. توی این پست، بر اساس داک رسمی FastAPI (https://fastapi.tiangolo.com)، یک API واقعی و قابل استفاده برای مدیریت "تسکها" میسازیم؛ چیزی شبیه todo-service کوچیک، ولی تمیز و قابل توسعه. 😎
ایده اینه:
• یک API میسازیم که لیست تسکها رو بده، تسک جدید بگیره و وضعیتش رو عوض کنه.
• از Pydantic models برای اعتبارسنجی ورودی استفاده میکنیم.
• خروجی JSON تمیز و قابل مصرف برای Front-end یا mobile app داریم.
اول با نصب شروع کن:
حالا یک فایل به اسم
اجرا روی لوکال:
بعد از اجرا، دو چیز خیلی کاربردی داری:
1️⃣ خود API روی:
2️⃣ داکیومنت خودکار با Swagger UI روی:
همونجا میتونی
نکتههای ریز ولی مهم:
• استفاده از
• با
• چون
این اسکلت ساده، پایه خیلی از APIهای واقعیه: فقط کافیه endpointها و مدلهات رو گسترش بدی. 👨💻👩💻
امتحانش کن، یک mini todo-service برای خودت بساز و روی پروژههای شخصی یا تمرینیات سوارش کن. اگر مفید بود برای بقیه دولوپرها هم بفرست تا از FastAPI لذت ببرن. 😄
🔖 #Programming #برنامه_نویسی #FastAPI #Python #REST_API #Backend #Web
👤 Developix
💎 Channel: @DevelopixChannel
FastAPI اینقدر سریع و تمیزه که برای ساخت REST APIهای کوچک تا سرویسهای جدی تولید (Production) عالی جواب میده. توی این پست، بر اساس داک رسمی FastAPI (https://fastapi.tiangolo.com)، یک API واقعی و قابل استفاده برای مدیریت "تسکها" میسازیم؛ چیزی شبیه todo-service کوچیک، ولی تمیز و قابل توسعه. 😎
ایده اینه:
• یک API میسازیم که لیست تسکها رو بده، تسک جدید بگیره و وضعیتش رو عوض کنه.
• از Pydantic models برای اعتبارسنجی ورودی استفاده میکنیم.
• خروجی JSON تمیز و قابل مصرف برای Front-end یا mobile app داریم.
اول با نصب شروع کن:
pip install "fastapi[all]" uvicorn
حالا یک فایل به اسم
main.py بساز و این کد رو بذار داخلش:from typing import List, Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="Tasks API")
class Task(BaseModel):
id: int
title: str
done: bool = False
description: Optional[str] = None
# دیتابیس موقتی در حافظه
TASKS_DB: List[Task] = []
@app.get("/tasks", response_model=List[Task])
async def list_tasks():
return TASKS_DB
@app.post("/tasks", response_model=Task, status_code=201)
async def create_task(task: Task):
for t in TASKS_DB:
if t.id == task.id:
raise HTTPException(status_code=400, detail="Task id already exists")
TASKS_DB.append(task)
return task
@app.patch("/tasks/{task_id}", response_model=Task)
async def mark_done(task_id: int):
for t in TASKS_DB:
if t.id == task_id:
t.done = True
return t
raise HTTPException(status_code=404, detail="Task not found")
اجرا روی لوکال:
uvicorn main:app --reload
بعد از اجرا، دو چیز خیلی کاربردی داری:
1️⃣ خود API روی:
http://127.0.0.1:8000/tasks
2️⃣ داکیومنت خودکار با Swagger UI روی:
http://127.0.0.1:8000/docs
همونجا میتونی
POST /tasks رو تست کنی، JSON بفرستی و خروجی رو ببینی. این همون چیزییه که تو سرویسهای واقعی برای Front-end، موبایل یا حتی microserviceهای دیگه فرستاده میشه.نکتههای ریز ولی مهم:
• استفاده از
response_model باعث میشه خروجی همیشه شکل مشخصی داشته باشه و Pydantic اون رو validate کنه.• با
HTTPException خطاها رو استاندارد و قابل پیشبینی برمیگردونی.• چون
TASKS_DB فقط تو حافظهست، برای محیط واقعی باید به یک دیتابیس واقعی مثل PostgreSQL وصلش کنی (مثلاً با SQLModel یا SQLAlchemy).این اسکلت ساده، پایه خیلی از APIهای واقعیه: فقط کافیه endpointها و مدلهات رو گسترش بدی. 👨💻👩💻
امتحانش کن، یک mini todo-service برای خودت بساز و روی پروژههای شخصی یا تمرینیات سوارش کن. اگر مفید بود برای بقیه دولوپرها هم بفرست تا از FastAPI لذت ببرن. 😄
🔖 #Programming #برنامه_نویسی #FastAPI #Python #REST_API #Backend #Web
👤 Developix
💎 Channel: @DevelopixChannel
👍3❤1
📌 سوال برنامهنویسی (Intermediate/Hard)
خروجی اجرای کد زیر در زبان Python چیست؟
دقت کنید که یک متغیر mutable بین فراخوانیهای تابع بهاشتراک گذاشته شده و فقط برای بعضی ورودیها تغییر میکند.
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
خروجی اجرای کد زیر در زبان Python چیست؟
دقت کنید که یک متغیر mutable بین فراخوانیهای تابع بهاشتراک گذاشته شده و فقط برای بعضی ورودیها تغییر میکند.
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
👍1
Forwarded from ابر ویراک
ویراک کلود | تجربهی ابری بدون محدودیت ☁️🚀
🎁 ۲۰٪ شارژ هدیه روی اولین واریزی
مزیتهایی که واقعاً فرق میسازن:
⚡️ پهنای باند 980 مگ
🌐با IP مازاد
⏱️ راهاندازی فوری
💳 پرداخت ساعتی
🛡 پشتیبانی ۲۴/۷
💬 هر سوالی داشتی، تیم ما کنارتـه
✅ مناسب استارتاپها، تیمهای فنی، تا سازمانهای بزرگ
همین الان شروع کن و با حداکثر منابع، سریعتر رشد کن 👇🏻
📞 تماس سریع: 02191555530
🔗 https://virakcloud.com
🎁 ۲۰٪ شارژ هدیه روی اولین واریزی
مزیتهایی که واقعاً فرق میسازن:
⚡️ پهنای باند 980 مگ
🌐با IP مازاد
⏱️ راهاندازی فوری
💳 پرداخت ساعتی
🛡 پشتیبانی ۲۴/۷
💬 هر سوالی داشتی، تیم ما کنارتـه
✅ مناسب استارتاپها، تیمهای فنی، تا سازمانهای بزرگ
همین الان شروع کن و با حداکثر منابع، سریعتر رشد کن 👇🏻
📞 تماس سریع: 02191555530
🔗 https://virakcloud.com
📌 یک سوال برای توسعهدهندگان Python:
به خروجی این کد دقت کنید. رفتار mutable default argument در Python اینجا کمی غافلگیرکننده است.
بهنظر شما اجرای این کد چه خروجیای در کنسول چاپ میکند؟
پاسخ را بهصورت دقیق و مطابق با خروجی واقعی Python بنویسید.
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
به خروجی این کد دقت کنید. رفتار mutable default argument در Python اینجا کمی غافلگیرکننده است.
بهنظر شما اجرای این کد چه خروجیای در کنسول چاپ میکند؟
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1))
print(add_item(2))
print(add_item(3, []))
print(add_item(4))
پاسخ را بهصورت دقیق و مطابق با خروجی واقعی Python بنویسید.
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
🧪 استفاده از
خیلی وقتها یک فرض توی کد داریم که "مطمئنیم" همیشه برقرار است؛ مثلا اینکه ورودی خالی نیست یا مقدار نال نمیآید. اینجور جاها assert دوست خوب ماست 😄
با
یک مثال ساده در Python:
نکته مهم 👇
- در Python اگر با فلگ
امروز یکی از فرضهای مهم کدت را با یک
📚 مرجع: Python Docs - assert statement
🔖 #Programming #برنامه_نویسی #assert #debugging #python #clean_code #defensive_programming
👤 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
خروجی اجرای این کد در Python چیست؟
به تفاوت بین alias شدن لیست و ساختن کپی با slicing دقت کنید.
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
به تفاوت بین alias شدن لیست و ساختن کپی با slicing دقت کنید.
a = [[1], [2], [3]]
b = a
c = a[:]
b[0].append(99)
a[1] = [42]
c[2].append(7)
print("a =", a)
print("b =", b)
print("c =", c)
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
👍1
در کد بالا در نسخهٔ اشتباه، همهٔ lambda ها به یک متغیّر مشترک
🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
i (بهدلیل Late Binding در Python) اشاره میکنند و در نتیجه در زمان اجرا همگی آخرین مقدار حلقه را برمیگردانند؛ در حالیکه در نسخهٔ درست با بستن مقدار i داخل پارامتر پیشفرض i=i برای هر iteration یک closure مستقل ساخته میشود و رفتار تابعها قابل پیشبینی و درست خواهد بود. در چنین شرایطی استفاده از این تکنیک یا حتی تعریف تابع جداگانه بهجای تکیه بر متغیر حلقه، هم خوانایی و هم پایداری کد را در پروژههای متوسط و بزرگ بهطور محسوسی بهبود میدهد.🔖 #Programming #برنامه_نویسی
👤 Developix
💎 Channel: @DevelopixChannel
👍2
گاهی وسط دیباگ سریع، کلی
با لاگ، هم سطح اهمیت داریم (info, warning, error)، هم میتوانیم بهراحتی فیلتر و جستجو کنیم و هم در محیطهای Production بهدرد میخورد.
مثال ساده در Python 👇
✅ مزیتها:
• لاگها قابل فیلتر بر اساس سطح هستند و میشود فقط warning/error دید.
• در سرویسهای Microservice و ابزارهایی مثل ELK یا Grafana Loki خیلی تمیزتر تحلیل میشوند.
برای پروژه بعدی، از همان روز اول یک Logger درست و حسابی تنظیم شود تا Debug و Monitoring آینده خیلی راحتتر پیش برود 🚀
مرجع: Python logging (Official Docs)
🔖 #Programming #برنامه_نویسی #logging #debugging #best_practices #python #structured_logging
👤 Developix
💎 Channel: @DevelopixChannel
print میریزیم تو کد و بعداً یادمان میرود پاکشان کنیم 😅 بهتر است از اول سراغ لاگ ساختیافته (Structured Logging) برویم.با لاگ، هم سطح اهمیت داریم (info, warning, error)، هم میتوانیم بهراحتی فیلتر و جستجو کنیم و هم در محیطهای Production بهدرد میخورد.
مثال ساده در Python 👇
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s - %(message)s",
)
logger = logging.getLogger("payment-service")
user_id = 42
amount = 199_000
logger.info("starting payment", extra={"user_id": user_id, "amount": amount})
try:
1 / 0
except ZeroDivisionError:
logger.exception("payment failed")
✅ مزیتها:
• لاگها قابل فیلتر بر اساس سطح هستند و میشود فقط warning/error دید.
• در سرویسهای Microservice و ابزارهایی مثل ELK یا Grafana Loki خیلی تمیزتر تحلیل میشوند.
برای پروژه بعدی، از همان روز اول یک Logger درست و حسابی تنظیم شود تا Debug و Monitoring آینده خیلی راحتتر پیش برود 🚀
مرجع: Python logging (Official Docs)
🔖 #Programming #برنامه_نویسی #logging #debugging #best_practices #python #structured_logging
👤 Developix
💎 Channel: @DevelopixChannel
👍2❤1
اصل Single Responsibility (SRP)؛ وقتی کلاس همهکاره خستگیات میکند 😅
یکی از مهمترین اصول SOLID، اصل Single Responsibility است:
هر کلاس یا ماژول باید فقط یک دلیل برای تغییر داشته باشد.
یعنی چی؟ یعنی یک کلاس نباید هم منطق دامنه را نگه دارد، هم log بنویسد، هم با دیتابیس کار کند، هم ایمیل بفرستد. این کلاسها هم سخت تست میشوند، هم کوچکترین تغییر، نصف سیستم را میترکاند 🔥
❌ مثال بد: کلاس خدا (God Class)
این کلاس حداقل ۵ مسئولیت دارد. هر تغییری در validation، ایمیل، لاگ یا ساختار دیتابیس، این کلاس را درگیر میکند. تست واحد هم کابوس میشود 🤯
✅ بازطراحی با SRP
ایده این است که هر ماژول روی یک چیز متمرکز شود:
حالا هر کلاس یک نقش نسبتاً واضح دارد. تغییر منطق قیمت، ایمیل یا دیتابیس، فقط همان بخش را تحت تاثیر قرار میدهد. تست واحد هم راحتتر است، چون میشود هر جزء را جداگانه mock و تست کرد ✅
چطور در عمل از SRP استفاده شود؟
• وقتی متدی خیلی طولانی میشود، معمولاً یعنی چند مسئولیت دارد.
• وقتی کلاس مدام به خاطر انواع تغییرها ویرایش میشود، یعنی باید تفکیک شود.
• نام کلاس اگر شبیه "Manager"، "Helper"، "Util" باشد، اغلب بوی نقض SRP میدهد 😄
مرجع خوب برای مطالعه عمیقتر:
refactoring.guru/design-principles/srp
قدمهای کوچک در جهت SRP، در طول زمان کد را تمیزتر، قابلتستتر و قابلتغییرتر میکند. از ریفکتور کردن کلاسهای همهکاره امروز شروع شود ✨
🔖 #Programming #برنامه_نویسی #SRP #SOLID #Clean_Code #Refactoring #Design_Principles #Python
👤 Developix
💎 Channel: @DevelopixChannel
یکی از مهمترین اصول 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
Forwarded from Developix Support
This media is not supported in your browser
VIEW IN TELEGRAM
🏆آفرهای جام جهانی ۲۰۲۶ هاستایران فعال شد!
با کمپین «ترکیب برنده سایتت»، زیرساخت سایتت رو برای روزهای پرترافیک آماده کن:
🔥 ۲۵٪ تخفیف روی پلنهای ۳ ماهه هاست اشتراکی ایران
⚡ ۵۰٪ تخفیف روی پلنهای یکماهه سرویس ابری در دیتاسنترهای تهران، مشهد و اهواز
🧠 مشاوره رایگان زیرساخت توسط کارشناسان هاستایران
فرصت استفاده از تخفیفها و ثبت فرم مشاوره محدود است.
📌 برای کپی کدهای تخفیف و ثبتنام در فرم مشاوره، روی لینک زیر کلیک کنید:
📌صفحه فرم مشاوره
📌تلگرام هاستایران
📌بله هاستایران
#هاست_ایران #سرور_ابری_هاست_ایران #هاست_اشتراکی #دیتاسنتر #مشاوره_زیرساخت #جام_جهانی2026
با کمپین «ترکیب برنده سایتت»، زیرساخت سایتت رو برای روزهای پرترافیک آماده کن:
🔥 ۲۵٪ تخفیف روی پلنهای ۳ ماهه هاست اشتراکی ایران
⚡ ۵۰٪ تخفیف روی پلنهای یکماهه سرویس ابری در دیتاسنترهای تهران، مشهد و اهواز
🧠 مشاوره رایگان زیرساخت توسط کارشناسان هاستایران
فرصت استفاده از تخفیفها و ثبت فرم مشاوره محدود است.
📌 برای کپی کدهای تخفیف و ثبتنام در فرم مشاوره، روی لینک زیر کلیک کنید:
📌صفحه فرم مشاوره
📌تلگرام هاستایران
📌بله هاستایران
#هاست_ایران #سرور_ابری_هاست_ایران #هاست_اشتراکی #دیتاسنتر #مشاوره_زیرساخت #جام_جهانی2026
❤1
ساخت یک REST API ساده با FastAPI 🚀
اگر دنبال راهی سریع و تمیز برای ساختن API هستی، FastAPI یکی از بهترین گزینههاست. روی Python ۳ کار میکنه، تایپهینتها رو جدی میگیره و همزمان هم داکیومنت اتوماتیک میسازه، هم performance خیلی خوبی داره.
سناریو: یه سرویس خیلی ساده «مدیریت تسکها» میخوایم، چیزی شبیه TODO، که بعداً راحت بشه روش frontend یا موبایل سوار کرد.
منبع اصلی این آموزش از داکیومنت رسمی FastAPI هست:
https://fastapi.tiangolo.com/tutorial/
✅ ایده کلی
FastAPI روی Starlette و Pydantic ساخته شده. یعنی:
• تعریف مدل داده با Pydantic (مثلاً مدل Task)
• تعریف endpointها با decoratorهای ساده مثل
• اجرا روی Uvicorn برای performance بالا
این مدل برای ساخت microservice، backend برای SPA، یا API موبایل خیلی practical و واقعیست.
🔧 نصب و راهاندازی سریع
۱) ساخت venv (اختیاری ولی پیشنهاد میشه)
۲) نصب FastAPI و Uvicorn:
👨💻 پیادهسازی یک TODO API کوچک
فایل
▶️ اجرای سرور
روی ترمینال:
حالا API روی آدرس زیر در حال اجراست:
و مستندات خودکار (Swagger UI) اینجاست:
اونجا میشه مستقیم request فرستاد، body داد، response دید و راحت API رو تست کرد. برای تست سریع هم میشه از
💡 نکات کاربردی
• بهتره از همون اول type-hintها رو جدی بگیری؛ FastAPI ازشون برای validation و schema استفاده میکنه.
• در پروژه واقعی، بهجای
•
یک بار این API ساده رو بساز، چند تا درخواست POST و GET بزن و ببین چقدر سریع میشه یک backend تمیز بالا آورد. اگر مفید بود، برای بقیه تیم هم بفرست تا برای پروتوتایپهای بعدی از FastAPI استفاده کنند. 🚀
🔖 #Programming #برنامه_نویسی #FastAPI #Python #REST_API #Backend #Web #Programming #Tutorial
👤 Developix
💎 Channel: @DevelopixChannel
اگر دنبال راهی سریع و تمیز برای ساختن API هستی، FastAPI یکی از بهترین گزینههاست. روی Python ۳ کار میکنه، تایپهینتها رو جدی میگیره و همزمان هم داکیومنت اتوماتیک میسازه، هم performance خیلی خوبی داره.
سناریو: یه سرویس خیلی ساده «مدیریت تسکها» میخوایم، چیزی شبیه TODO، که بعداً راحت بشه روش frontend یا موبایل سوار کرد.
منبع اصلی این آموزش از داکیومنت رسمی FastAPI هست:
https://fastapi.tiangolo.com/tutorial/
✅ ایده کلی
FastAPI روی Starlette و Pydantic ساخته شده. یعنی:
• تعریف مدل داده با Pydantic (مثلاً مدل Task)
• تعریف endpointها با decoratorهای ساده مثل
@app.get• اجرا روی Uvicorn برای performance بالا
این مدل برای ساخت microservice، backend برای SPA، یا API موبایل خیلی practical و واقعیست.
🔧 نصب و راهاندازی سریع
۱) ساخت venv (اختیاری ولی پیشنهاد میشه)
python -m venv venv
source venv/bin/activate # روی ویندوز: venv\Scripts\activate
۲) نصب FastAPI و Uvicorn:
pip install "fastapi[all]" uvicorn
👨💻 پیادهسازی یک TODO API کوچک
فایل
main.py رو بساز و کد زیر رو داخلش قرار بده:from typing import List, Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="Todo API")
class Task(BaseModel):
id: int
title: str
done: bool = False
description: Optional[str] = None
fake_db: List[Task] = []
@app.get("/tasks", response_model=List[Task])
async def list_tasks():
return fake_db
@app.post("/tasks", response_model=Task)
async def create_task(task: Task):
for t in fake_db:
if t.id == task.id:
raise HTTPException(status_code=400, detail="Task id already exists")
fake_db.append(task)
return task
@app.get("/tasks/{task_id}", response_model=Task)
async def get_task(task_id: int):
for t in fake_db:
if t.id == task_id:
return t
raise HTTPException(status_code=404, detail="Task not found")
@app.patch("/tasks/{task_id}", response_model=Task)
async def toggle_done(task_id: int):
for t in fake_db:
if t.id == task_id:
t.done = not t.done
return t
raise HTTPException(status_code=404, detail="Task not found")
▶️ اجرای سرور
روی ترمینال:
uvicorn main:app --reload
حالا API روی آدرس زیر در حال اجراست:
http://127.0.0.1:8000
و مستندات خودکار (Swagger UI) اینجاست:
http://127.0.0.1:8000/docs
اونجا میشه مستقیم request فرستاد، body داد، response دید و راحت API رو تست کرد. برای تست سریع هم میشه از
curl یا Postman استفاده کرد.💡 نکات کاربردی
• بهتره از همون اول type-hintها رو جدی بگیری؛ FastAPI ازشون برای validation و schema استفاده میکنه.
• در پروژه واقعی، بهجای
fake_db از دیتابیس (مثل PostgreSQL با SQLAlchemy) استفاده میشه، اما همین in-memory مدل فکری رو خوب جا میاندازه.•
@app.patch برای update جزئی خیلی بهدردبخوره؛ مخصوصاً وقتی روی front با formهای تیکدار کار میکنی.یک بار این API ساده رو بساز، چند تا درخواست POST و GET بزن و ببین چقدر سریع میشه یک backend تمیز بالا آورد. اگر مفید بود، برای بقیه تیم هم بفرست تا برای پروتوتایپهای بعدی از FastAPI استفاده کنند. 🚀
🔖 #Programming #برنامه_نویسی #FastAPI #Python #REST_API #Backend #Web #Programming #Tutorial
👤 Developix
💎 Channel: @DevelopixChannel
👍4
💠 نسخه جدید محیط توسعه ویژوال استودیو (Visual Studio) منتشر شده است
🔸 ارتقا از نسخه 18.6.3 به 18.7.1
🔖 #visual_studio, #chat, #csharp, #release
💎 @DevelopixChannel
🔸 ارتقا از نسخه 18.6.3 به 18.7.1
🔖 #visual_studio, #chat, #csharp, #release
💎 @DevelopixChannel
👍1
💠 نسخه جدید محیط توسعه VSCode منتشر شده است
🔸 ارتقا از نسخه 1.125.0 به 1.126.0
🔖 #vscode, #chat, #release
💎 @DevelopixChannel
🔸 ارتقا از نسخه 1.125.0 به 1.126.0
🔖 #vscode, #chat, #release
💎 @DevelopixChannel
👍4
💠 نسخه جدید محیط توسعه Zed منتشر شده است
🔸 ارتقا از نسخه 1.7.2 به 1.8.2
🔖 #zed, #chat, #release
💎 @DevelopixChannel
🔸 ارتقا از نسخه 1.7.2 به 1.8.2
🔖 #zed, #chat, #release
💎 @DevelopixChannel
👍6
💠 نسخه جدید محیط توسعه VSCode منتشر شده است
🔸 ارتقا از نسخه 1.126.0 به 1.127.0
🔖 #vscode, #chat, #release
💎 @DevelopixChannel
🔸 ارتقا از نسخه 1.126.0 به 1.127.0
🔖 #vscode, #chat, #release
💎 @DevelopixChannel
👍1
راز کندی کدهای مالتیترد: کابوسی به نام False Sharing
تا حالا برات پیش اومده یه کد مالتیترد بنویسی، هیچ Lockای هم استفاده نکنی، تردها کاملاً مستقل از هم کار کنن، ولی آخرش ببینی از نسخهی تکترد هم کندتره؟
اگر جواب مثبته، احتمالاً با یکی از عجیبترین و پنهانترین مشکلات دنیای سختافزار روبهرو شدی: False Sharing.
پشت صحنهی CPU چه خبره؟
پردازندههای امروزی دادهها رو دونهدونه از RAM نمیخونن. اونها دادهها رو در بستههایی به اسم Cache Line (معمولاً ۶۴ بایت) داخل حافظهی کش (L1/L2/L3) نگه میدارن تا دسترسی بهشون خیلی سریعتر باشه.
حالا فرض کن این ساختار رو داریم:
type CoreData struct {
CounterA int64 // ۸ بایت
CounterB int64 // ۸ بایت
}
چون این دو متغیر کنار هم قرار گرفتن، احتمالاً هر دو داخل یک Cache Line قرار میگیرن.
مشکل از کجا شروع میشه؟
فرض کن:
ترد اول روی هستهی اول CPU مدام CounterA رو تغییر میده.
ترد دوم روی هستهی دوم CPU هم مدام CounterB رو آپدیت میکنه.
از دید برنامهنویس، این دو متغیر هیچ ارتباطی با هم ندارن. اما از دید CPU داستان فرق میکنه.
هر بار که هستهی اول CounterA رو تغییر میده، کل اون Cache Line برای هستهی دوم نامعتبر (Invalid) میشه. هستهی دوم مجبور میشه دوباره داده رو از کش سطح بالاتر یا حتی RAM بخونه، مقدار خودش رو تغییر بده و دوباره باعث نامعتبر شدن کش هستهی اول بشه.
این رفتوبرگشت دائمی بین هستهها رو Cache Line Bouncing میگن.
نتیجه چیه؟ پردازنده به جای اینکه وقتش رو صرف اجرای کد تو کنه، مدام مشغول هماهنگ کردن حافظهی کش بین هستههاست!
راهحل؟ Padding
خوشبختانه راهحلش خیلی پیچیده نیست.
کافیه کاری کنیم این دو متغیر داخل یک Cache Line قرار نگیرن. یعنی بینشون کمی فاصله (Padding) ایجاد کنیم تا هر کدوم روی یک Cache Line جدا باشن.
مثلاً در Go میتونیم از cpu.CacheLinePad استفاده کنیم:
import "golang.org/x/sys/cpu"
type OptimizedData struct {
CounterA int64
_ cpu.CacheLinePad
CounterB int64
}
با همین فاصلهی ساده، هر متغیر روی یک Cache Line جدا قرار میگیره و دو هسته بدون اینکه مزاحم هم بشن، همزمان کار میکنن.
جالبتر اینکه در بعضی بنچمارکهای واقعی، همین تغییر کوچک تونسته عملکرد کدهای موازی رو چند برابر و حتی تا ۱۰ برابر بهتر کنه.
حرف آخر
خیلی وقتها مشکل کند بودن کد، نه از الگوریتمه، نه از زبان برنامهنویسی و نه حتی از تعداد Threadها؛ بلکه از جزئیاتی میاد که داخل خود CPU اتفاق میافته.
هرچقدر درک بهتری از معماری سختافزار داشته باشیم، راحتتر میتونیم کدهایی بنویسیم که علاوه بر درست بودن، واقعاً سریع هم باشن.
🔖 #Programming #برنامه_نویسی
👤 Ruhollah
💎 Channel: @DevelopixChannel
تا حالا برات پیش اومده یه کد مالتیترد بنویسی، هیچ Lockای هم استفاده نکنی، تردها کاملاً مستقل از هم کار کنن، ولی آخرش ببینی از نسخهی تکترد هم کندتره؟
اگر جواب مثبته، احتمالاً با یکی از عجیبترین و پنهانترین مشکلات دنیای سختافزار روبهرو شدی: False Sharing.
پشت صحنهی CPU چه خبره؟
پردازندههای امروزی دادهها رو دونهدونه از RAM نمیخونن. اونها دادهها رو در بستههایی به اسم Cache Line (معمولاً ۶۴ بایت) داخل حافظهی کش (L1/L2/L3) نگه میدارن تا دسترسی بهشون خیلی سریعتر باشه.
حالا فرض کن این ساختار رو داریم:
type CoreData struct {
CounterA int64 // ۸ بایت
CounterB int64 // ۸ بایت
}
چون این دو متغیر کنار هم قرار گرفتن، احتمالاً هر دو داخل یک Cache Line قرار میگیرن.
مشکل از کجا شروع میشه؟
فرض کن:
ترد اول روی هستهی اول CPU مدام CounterA رو تغییر میده.
ترد دوم روی هستهی دوم CPU هم مدام CounterB رو آپدیت میکنه.
از دید برنامهنویس، این دو متغیر هیچ ارتباطی با هم ندارن. اما از دید CPU داستان فرق میکنه.
هر بار که هستهی اول CounterA رو تغییر میده، کل اون Cache Line برای هستهی دوم نامعتبر (Invalid) میشه. هستهی دوم مجبور میشه دوباره داده رو از کش سطح بالاتر یا حتی RAM بخونه، مقدار خودش رو تغییر بده و دوباره باعث نامعتبر شدن کش هستهی اول بشه.
این رفتوبرگشت دائمی بین هستهها رو Cache Line Bouncing میگن.
نتیجه چیه؟ پردازنده به جای اینکه وقتش رو صرف اجرای کد تو کنه، مدام مشغول هماهنگ کردن حافظهی کش بین هستههاست!
راهحل؟ Padding
خوشبختانه راهحلش خیلی پیچیده نیست.
کافیه کاری کنیم این دو متغیر داخل یک Cache Line قرار نگیرن. یعنی بینشون کمی فاصله (Padding) ایجاد کنیم تا هر کدوم روی یک Cache Line جدا باشن.
مثلاً در Go میتونیم از cpu.CacheLinePad استفاده کنیم:
import "golang.org/x/sys/cpu"
type OptimizedData struct {
CounterA int64
_ cpu.CacheLinePad
CounterB int64
}
با همین فاصلهی ساده، هر متغیر روی یک Cache Line جدا قرار میگیره و دو هسته بدون اینکه مزاحم هم بشن، همزمان کار میکنن.
جالبتر اینکه در بعضی بنچمارکهای واقعی، همین تغییر کوچک تونسته عملکرد کدهای موازی رو چند برابر و حتی تا ۱۰ برابر بهتر کنه.
حرف آخر
خیلی وقتها مشکل کند بودن کد، نه از الگوریتمه، نه از زبان برنامهنویسی و نه حتی از تعداد Threadها؛ بلکه از جزئیاتی میاد که داخل خود CPU اتفاق میافته.
هرچقدر درک بهتری از معماری سختافزار داشته باشیم، راحتتر میتونیم کدهایی بنویسیم که علاوه بر درست بودن، واقعاً سریع هم باشن.
🔖 #Programming #برنامه_نویسی
👤 Ruhollah
💎 Channel: @DevelopixChannel
👍4❤1
💠 نسخه جدید محیط توسعه Zed منتشر شده است
🔸 ارتقا از نسخه 1.8.2 به 1.9.0
🔖 #zed, #chat, #release
💎 @DevelopixChannel
🔸 ارتقا از نسخه 1.8.2 به 1.9.0
🔖 #zed, #chat, #release
💎 @DevelopixChannel
👍3
قانون Demeter (Law of Demeter) چیه و چرا اینقدر مهمه؟ 🤐
یکی از اصول قدیمی ولی فوقالعاده کاربردی در طراحی شیگرا، Law of Demeter یا همون "Talk only to your friends" ـه.
به زبان ساده: هر آبجکت فقط باید با "همسایههای نزدیکش" حرف بزنه، نه با نصف سیستم.
قاعدهٔ اصلی:
هر متد فقط باید با اینها تعامل داشته باشه:
• خودِ شی (this)
• پارامترهای ورودی متد
• فیلدهای خودش
• آبجکتهایی که خودش میسازه
هر چیزی غیر از این، بوی Coupling بالا و دردسر در ریفکتور میده 😄
یک مثال بد 👎 (Law of Demeter نقض شده)
توی کد واقعی (مثال Python):
اینجا
• اگر ساختار
• اگر
نسخهٔ بهتر ✅ (رعایت Law of Demeter)
مسیر طولانی رو "کپسوله" میکنیم:
حالا
چرا این قانون در عمل خیلی کمک میکنه؟ 💡
• کاهش Coupling: کلاسها کمتر از ساختار داخلی هم خبر دارن.
• انعطاف در ریفکتور: راحت میتونی مدل داده رو عوض کنی بدون اینکه نصف سرویسها رو دست بزنی.
• بهبود تستپذیری: با Mock کردن چند آبجکت محدود میتونی یونیتتست بنویسی، بدون زنجیرههای عجیب.
بوی بد معروف: "Train Wreck" 🚂
هر وقت این الگو رو دیدی:
تقریباً مطمئنی که Law of Demeter نقض شده. این همون Train Wreck ـه؛ قطار نقطهها.
چند نکتهٔ عملی برای کدنویسی روزمره
• اگر
• به جای expose کردن کل آبجکتهای داخلی، متدهای سطح بالاتر ارائه کن (مثل
• توی 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
یکی از اصول قدیمی ولی فوقالعاده کاربردی در طراحی شیگرا، 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