MATIN MOLKARA
SERIES: JavaScript — PART 87 OF 108

87- آموزش جاوااسکریپت | State Pattern

در این بخش با State Pattern در JavaScript آشنا می‌شویم؛ الگویی برای مدیریت وضعیت‌های مختلف در برنامه. با یک مثال ساده، نحوه تغییر state و نمایش محتوای متفاوت بر اساس وضعیت فعلی را بررسی می‌کنیم.

در این بخش، الگوی State Pattern را در JavaScript بررسی می‌کنیم. این الگو یکی از Behavioral Patternها است و برای مدیریت رفتارهای مختلف یک آبجکت بر اساس وضعیت فعلی آن استفاده می‌شود.

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

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

State Pattern چیست؟

در State Pattern، به‌جای اینکه با تعداد زیادی شرط if و switch رفتارهای مختلف را کنترل کنیم، هر وضعیت را در قالب یک ساختار جداگانه تعریف می‌کنیم.

به این ترتیب:

  • هر state مسئول رفتار مربوط به خودش است
  • تغییر بین stateها ساده‌تر می‌شود
  • کد خواناتر و قابل‌نگهداری‌تر خواهد شد
  • افزودن وضعیت‌های جدید آسان‌تر می‌شود

سناریوی مثال

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

  • Home
  • About
  • Contact

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

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


ساخت PageState

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

const PageState = function () {
  let currentState = new HomeState(this);

  this.init = function () {
    this.change(new HomeState());
  };

  this.change = function (state) {
    currentState = state;
  };
};

بررسی ساختار PageState

در این بخش سه قسمت مهم داریم:

1. متغیر currentState

این متغیر وضعیت فعلی صفحه را نگه می‌دارد:

let currentState = new HomeState(this);

2. متد init

این متد برای مقداردهی اولیه استفاده می‌شود و state اولیه را روی HomeState قرار می‌دهد:

this.init = function () {
  this.change(new HomeState());
};

3. متد change

این متد وظیفه تغییر state فعلی را بر عهده دارد:

this.change = function (state) {
  currentState = state;
};

تعریف HomeState

اکنون اولین state را تعریف می‌کنیم:

const HomeState = function (page) {
  document.querySelector('#heading').textContent = '';
  document.querySelector('#content').innerHTML = `
    <div class="jumbotron">
      <h1 class="display-4">Welcome</h1>
      <p class="lead">This is the home page</p>
    </div>
  `;
};

عملکرد HomeState

در این state:

  • عنوان صفحه خالی می‌شود
  • محتوای اصلی صفحه با یک بخش خوش‌آمدگویی جایگزین می‌شود

یعنی هر زمان HomeState فعال شود، رابط کاربری محتوای صفحه اصلی را نمایش می‌دهد.


تعریف AboutState

حالا state مربوط به بخش About را می‌سازیم:

const AboutState = function (page) {
  document.querySelector('#heading').textContent = 'About Us';
  document.querySelector('#content').innerHTML = `
    <p>This is the about page</p>
  `;
};

عملکرد AboutState

در این state:

  • عنوان صفحه روی About Us تنظیم می‌شود
  • یک پاراگراف ساده به‌عنوان محتوای این بخش نمایش داده می‌شود

تعریف ContactState

در ادامه، state مربوط به بخش Contact را پیاده‌سازی می‌کنیم:

const ContactState = function (page) {
  document.querySelector('#heading').textContent = 'Contact Us';
  document.querySelector('#content').innerHTML = `
    <form>
      <div class="form-group">
        <label>Name</label>
        <input type="text" class="form-control" />
      </div>
      <div class="form-group">
        <label>Email</label>
        <input type="email" class="form-control" />
      </div>
      <div class="form-group">
        <label>Message</label>
        <textarea class="form-control"></textarea>
      </div>
      <button type="submit" class="btn btn-primary mt-3">Send</button>
    </form>
  `;
};

عملکرد ContactState

در این وضعیت:

  • عنوان صفحه روی Contact Us قرار می‌گیرد
  • یک فرم تماس در بخش محتوا نمایش داده می‌شود

ساخت نمونه‌ای از PageState

پس از تعریف stateها، باید یک نمونه از PageState بسازیم:

const page = new PageState();
page.init();

با اجرای init()، وضعیت اولیه صفحه روی HomeState قرار می‌گیرد.


انتخاب عناصر رابط کاربری

برای کنترل لینک‌های منو، ابتدا آن‌ها را از DOM انتخاب می‌کنیم:

const home = document.getElementById('home');
const about = document.getElementById('about');
const contact = document.getElementById('contact');

در این مثال فرض می‌کنیم هر لینک دارای id مشخصی است:

  • home
  • about
  • contact

افزودن Event Listener برای تغییر State

اکنون باید با کلیک روی هر لینک، state صفحه را تغییر دهیم.

رویداد مربوط به Home

home.addEventListener('click', (e) => {
  page.change(new HomeState());
  e.preventDefault();
});

رویداد مربوط به About

about.addEventListener('click', (e) => {
  page.change(new AboutState());
  e.preventDefault();
});

رویداد مربوط به Contact

contact.addEventListener('click', (e) => {
  page.change(new ContactState());
  e.preventDefault();
});

چرا از preventDefault() استفاده می‌کنیم؟

چون این لینک‌ها به‌صورت پیش‌فرض رفتار ناوبری مرورگر را اجرا می‌کنند.
با استفاده از preventDefault() جلوی این رفتار گرفته می‌شود تا بتوانیم تغییر صفحه را فقط با JavaScript مدیریت کنیم.


نسخه کامل کد JavaScript

در ادامه، کل پیاده‌سازی را یک‌جا می‌بینید:

const PageState = function () {
  let currentState = new HomeState(this);

  this.init = function () {
    this.change(new HomeState());
  };

  this.change = function (state) {
    currentState = state;
  };
};

const HomeState = function (page) {
  document.querySelector('#heading').textContent = '';
  document.querySelector('#content').innerHTML = `
    <div class="jumbotron">
      <h1 class="display-4">Welcome</h1>
      <p class="lead">This is the home page</p>
    </div>
  `;
};

const AboutState = function (page) {
  document.querySelector('#heading').textContent = 'About Us';
  document.querySelector('#content').innerHTML = `
    <p>This is the about page</p>
  `;
};

const ContactState = function (page) {
  document.querySelector('#heading').textContent = 'Contact Us';
  document.querySelector('#content').innerHTML = `
    <form>
      <div class="form-group">
        <label>Name</label>
        <input type="text" class="form-control" />
      </div>
      <div class="form-group">
        <label>Email</label>
        <input type="email" class="form-control" />
      </div>
      <div class="form-group">
        <label>Message</label>
        <textarea class="form-control"></textarea>
      </div>
      <button type="submit" class="btn btn-primary mt-3">Send</button>
    </form>
  `;
};

const page = new PageState();
page.init();

const home = document.getElementById('home');
const about = document.getElementById('about');
const contact = document.getElementById('contact');

home.addEventListener('click', (e) => {
  page.change(new HomeState());
  e.preventDefault();
});

about.addEventListener('click', (e) => {
  page.change(new AboutState());
  e.preventDefault();
});

contact.addEventListener('click', (e) => {
  page.change(new ContactState());
  e.preventDefault();
});

ساختار ساده HTML

برای اینکه این مثال کار کند، به ساختار HTML ساده‌ای شبیه نمونه زیر نیاز داریم:

<nav>
  <a id="home" href="#">Home</a>
  <a id="about" href="#">About</a>
  <a id="contact" href="#">Contact</a>
</nav>

<div class="container">
  <h1 id="heading"></h1>
  <div id="content"></div>
</div>

مزایای State Pattern

استفاده از این الگو مزایای مهمی دارد:

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

کاربردهای رایج State Pattern

این الگو در سناریوهای مختلفی کاربرد دارد، از جمله:

  • مدیریت وضعیت صفحات در رابط کاربری
  • فرم‌های چندمرحله‌ای
  • ویزاردها و فرآیندهای مرحله‌به‌مرحله
  • مدیریت وضعیت دکمه‌ها و کامپوننت‌ها
  • سیستم‌های دارای حالت‌هایی مثل loading، success، error
  • پنل‌های مدیریتی و اپلیکیشن‌های تعاملی

نکته مهم

مثالی که در این بخش دیدیم، یک نمونه ساده برای درک مفهوم State Pattern است. در پروژه‌های واقعی، معمولاً stateها کاربردهای عملی‌تری دارند؛ برای مثال:

  • loadingState
  • editState
  • listState
  • errorState

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


جمع‌بندی

در این بخش با State Pattern در JavaScript آشنا شدیم. این الگو به ما کمک می‌کند رفتار برنامه را بر اساس وضعیت فعلی آن مدیریت کنیم.

در مثال این آموزش دیدیم که:

  • هر بخش از صفحه یک state جداگانه است
  • با تغییر state، محتوای UI نیز تغییر می‌کند
  • همه این رفتارها بدون نیاز به صفحات HTML جداگانه انجام می‌شوند

اگر در پروژه‌ای با وضعیت‌های مختلف روبه‌رو هستید و می‌خواهید منطق هر وضعیت را به‌صورت جداگانه و تمیز مدیریت کنید، State Pattern یکی از الگوهای بسیار کاربردی در JavaScript است.

// 0 comments
#JavaScript#الگوهای طراحی#آموزش جاوااسکریپت#Bootstrap#UI State#Behavioral Patterns#JavaScript Patterns#Design Patterns#State Pattern

نظرات (0)

نظر خود را بنویسید