Mojtaba Pourkhanlar
About meProjectsBlog

  • 👤About me
  • 🧰Projects
  • ✍️Blog

اصول SOLID در JS/React


S (Single Responsibility Principle):

  • هر کامپوننت یک وظیفه خاص داشته باشد

چرا مهمه؟

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

یعنی:

  • Component = فقط UI
  • Service = فقط logic
  • API = فقط اتصال به سرور
  • Store = فقط مدیریت state
// ❌ Bad: چند مسئولیت متفاوت در یک کامپوننت
function UserProfile({ user }) {
  // نمایش اطلاعات
  // مدیریت فرم
  // فراخوانی API
}

// ✅ Good: جداسازی مسئولیت‌ها
function UserProfile({ user }) {
  return (
    <div>
      <UserInfo user={user} />
      <UserSettingsForm userId={user.id} />
    </div>
  );
}

O (Open/Closed Principle):

  • کامپوننت‌ها باید قابل توسعه باشن، بدون اینکه نیاز باشه تغییرشون بدی.
//  استفاده از Composition
function Button({ children, variant = "primary", size = "medium", ...props }) {
  const baseClasses = "btn";
  const variantClasses = {
    primary: "btn-primary",
    secondary: "btn-secondary",
    danger: "btn-danger",
  };
  const sizeClasses = {
    small: "btn-sm",
    medium: "btn-md",
    large: "btn-lg",
  };

  return (
    <button
      className={`${baseClasses} ${variantClasses[variant]
} ${sizeClasses[size]
}`}
      {...props}
    >
      {children}
    </button>
  );
}

//  قابل توسعه بدون تغییر Button
function IconButton({ icon, children, ...props }) {
  return (
    <Button {...props}>
      <Icon name={icon} />
      {children}
    </Button>
  );
}

L (Liskov Substitution Principle):

  • کامپوننت‌های فرزند باید بتوانند جایگزین والد شوند.
//  Good  کامپوننت‌ها قابل تعویض هستن
function BaseInput({ label, ...props }) {
  return (
    <div className="input-group">
      {label && <label>{label}</label>}
      <input {...props} />
    </div>
  );
}

function EmailInput(props) {
  return <BaseInput type="email" {...props} />;
}

function PasswordInput(props) {
  return <BaseInput type="password" {...props} />;
}

// . قابل استفاده به جای یکدیگر
<BaseInput label="Username" />
<EmailInput label="Email" />
<PasswordInput label="Password" />

I (Interface Segregation):

  • کامپوننت‌ها نباید propsهای سنگین یا بی‌استفاده بگیرن.

چرا؟

چون تبدیل می‌شن به چاقوی سوئیسی React: همه‌چیز دارن ولی نصفش هیچ‌وقت استفاده نمیشه.

// ❌ Bad: props بزرگ و حجیم
function UserCard({ user, showEmail, showPhone, showAddress }) {
  return (
    <div>
      <h3>{user.name}</h3>
      {showEmail && <p>{user.email}</p>}
      {showPhone && <p>{user.phone}</p>}
      {showAddress && <p>{user.address}</p>}
    </div>
  );
}

// ✅ Good: کامپوننت‌های کوچک و تخصصی
function UserCard({ user, children }) {
  return (
    <div>
      <h3>{user.name}</h3>
      {children}
    </div>
  );
}

function UserCardWithEmail({ user }) {
  return (
    <UserCard user={user}>
      <p>{user.email}</p>
    </UserCard>
  );
}

D (Dependency Inversion):

  • کامپوننت باید به abstraction وابسته باشد، نه implementation.

این یعنی منطقت رو inject کن، hardcode نکن.

// ❌ Bad: وابستگی مستقیم به fetch
function UserList() {
  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(setUsers);
  }, []
);
}

// ✅ Good: abstraction
const getUserService = apiClient => ({
  getUsers: () => apiClient.get("/users"),
});

function UserList({ userService }) {
  useEffect(() => {
    userService.getUsers().then(setUsers);
  }, [userService]
);
}

جمع بندی

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

نکته طلایی: بهترین الگو، الگویی است که تیم شما آن را درک کند، نگهداری آن آسان باشد و نیازهای پروژه شما را برآورده سازد