نحوه راه اندازی گوشی های هوشمند و رایانه های شخصی. پرتال اطلاعاتی

ایالتی. الگوی حالت

"الگوحالت"منبع .ru

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

شرایط، وظیفه، هدف

به یک شی اجازه می دهد تا رفتار خود را بسته به وضعیت داخلی آن تغییر دهد. از آنجایی که رفتار می تواند کاملاً خودسرانه و بدون هیچ محدودیتی تغییر کند، از بیرون به نظر می رسد که کلاس شی تغییر کرده است.

انگیزه

کلاس را در نظر بگیرید TCPConnection، که نشان دهنده یک اتصال شبکه است. یک شی از این کلاس می تواند در یکی از چندین حالت باشد: ایجاد(نصب شده است)، استماع(استماع)، بسته شد(بسته). وقتی یک شی TCPConnectionدرخواست هایی را از اشیاء دیگر دریافت می کند، بسته به وضعیت فعلی متفاوت پاسخ می دهد. به عنوان مثال، پاسخ به یک درخواست باز کن(باز) بستگی به این دارد که آیا اتصال در حالت باشد بسته شدیا ایجاد. الگوی حالت چگونگی یک شی را توصیف می کند TCPConnectionمی تواند در حالت های مختلف رفتار متفاوتی داشته باشد. source.ru

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

وضعیت. در این زیر کلاس ها TCPS Stateرفتار خاص حالت اجرا می شود. مثلا در کلاس ها TCPE تاسیس شدو TCP بسته شدرفتار خاص دولت اجرا شده است ایجادو بسته شدبه ترتیب. منبع اصلی وب سایت

سایت منبع اصلی سایت

کلاس TCPConnectionیک شیء حالت (نمونه ای از یک کلاس فرعی) را ذخیره می کند TCPS State) وضعیت فعلی اتصال را نشان می دهد و تمام درخواست های وابسته به حالت را به این شی واگذار می کند. TCPConnectionاز نمونه خود از زیر کلاس استفاده می کند TCPS Stateبسیار ساده: فراخوانی روش های یک رابط TCPS State، فقط بسته به اینکه چه زیر کلاس خاصی در حال حاضر ذخیره شده است TCPS State-a - نتیجه متفاوت است، یعنی. در واقع، عملیاتی که فقط مختص این حالت اتصال است انجام می شود. منبع original.ru

و هر بار که وضعیت اتصال تغییر می کندTCPConnectionشی حالت خود را تغییر می دهد. به عنوان مثال، هنگامی که یک اتصال برقرار شده بسته است، TCPConnectionنمونه ای از یک کلاس را جایگزین می کند TCPE تاسیس شدکپی 🀄 TCP بسته شد. سایت منبع اصلی سایت

علائم کاربرد، استفاده از الگوی حالت

از الگوی حالت در موارد زیر استفاده کنید: منبع original.ru
  1. وقتی رفتار یک شی به حالت آن بستگی دارد و باید در زمان اجرا تغییر کند. .ru
  2. زمانی که کد عملیات شامل عبارات شرطی متشکل از شاخه های زیادی است که در آن انتخاب شاخه به حالت بستگی دارد. به طور معمول در این حالت حالت با ثابت های شمارش شده نشان داده می شود. اغلب همان ساختار عبارت شرطی در چندین عملیات تکرار می شود.الگوی حالت پیشنهاد می کند که هر شاخه را در یک کلاس جداگانه قرار دهید. این به شما امکان می دهد وضعیت یک شی را به عنوان یک شی مستقل در نظر بگیرید که می تواند مستقل از دیگران تغییر کند. منبع original.ru

راه حل

منبع سایت سایت اصلی

original.ru

شرکت کنندگان الگوی دولتی

منبع original.ru
  1. متن نوشته(TCPConnection) - زمینه.
    یک رابط واحد برای مشتریان تعریف می کند.
    نمونه ای از زیر کلاس را ذخیره می کند Concrete State، که وضعیت فعلی را تعیین می کند. original.ru
  2. حالت(TCPState) - حالت.
    یک رابط را برای کپسوله کردن رفتار مرتبط با یک وضعیت Context خاص تعریف می کند. منبع original.ru
  3. زیر کلاس ها Concrete State(TCPEstablished، TCPListen، TCPClosed) - حالت خاص.
    هر زیر کلاس رفتار مرتبط با برخی از وضعیت های زمینه را پیاده سازی می کند متن نوشته. منبع اصلی وب سایت

طرحی برای استفاده از الگوی State

کلاس متن نوشتهدرخواست ها را به شی فعلی تفویض می کند Concrete State. منبع اصلی وب سایت

یک متن می تواند خود را به عنوان یک آرگومان به یک شی منتقل کند حالت، که درخواست را پردازش می کند. این به شیء حالت ( Concrete State) در صورت لزوم به متن دسترسی داشته باشید. سایت منبع اصلی سایت

متن نوشته- این رابط اصلی برای مشتریان است. کلاینت ها می توانند زمینه را با اشیاء حالت پیکربندی کنند حالت(دقیق تر Concrete State). پس از پیکربندی زمینه، کلاینت ها دیگر نیازی به برقراری ارتباط مستقیم با اشیاء حالت (فقط از طریق رابط مشترک ندارند) حالت). منبع سایت سایت اصلی

در این مورد، یا متن نوشته، یا خود زیر کلاس ها Concrete Stateمی تواند تصمیم بگیرد که تغییر حالت ها تحت چه شرایطی و به چه ترتیبی اتفاق می افتد. منبع .ru

سوالات مربوط به اجرای الگوی دولتی

سوالات مربوط به اجرای الگوی دولتی: منبع original.ru
  1. آنچه که انتقال بین حالت ها را تعیین می کند.
    الگوی حالت چیزی در مورد اینکه کدام شرکت کننده شرایط (معیارهای) گذار بین حالت ها را تعیین می کند، نمی گوید. اگر معیارها ثابت باشند، می‌توان آن‌ها را مستقیماً در کلاس پیاده‌سازی کرد متن نوشته. با این حال، به طور کلی، یک رویکرد منعطف تر و صحیح تر اجازه دادن به زیر کلاس های خود کلاس است حالتوضعیت و لحظه انتقال بعدی را تعیین کنید. برای انجام این کار در کلاس متن نوشتهما باید یک رابط اضافه کنیم که از اشیاء اجازه می دهد حالتوضعیت آن را تنظیم کنید
    این منطق انتقال غیرمتمرکز برای اصلاح و گسترش آسان تر است - فقط باید زیر کلاس های جدیدی را تعریف کنید. حالت. نقطه ضعف تمرکززدایی این است که هر زیر کلاس حالتباید حداقل در مورد یک زیر کلاس از حالت دیگر (که در واقع می تواند وضعیت فعلی را به آن تغییر دهد) "دانست"، که وابستگی های پیاده سازی را بین زیر کلاس ها معرفی می کند. source.ru

    منبع سایت سایت اصلی
  2. جایگزین جدولی.
    راه دیگری برای ساختار کدهای حالت محور وجود دارد. این اصل یک ماشین حالت محدود است. از یک جدول برای نگاشت ورودی ها به حالت انتقال استفاده می کند. با کمک آن، می توانید تعیین کنید که هنگام رسیدن داده های ورودی خاص به کدام وضعیت نیاز دارید. در اصل، ما کد شرطی را با جستجوی جدول جایگزین می کنیم.
    مزیت اصلی دستگاه منظم بودن آن است: برای تغییر معیارهای انتقال کافی است فقط داده ها را اصلاح کنید نه کد. اما معایبی نیز دارد:
    - جستجو در جدول اغلب کارآمدتر از فراخوانی یک تابع است.
    - ارائه منطق انتقال در قالب جدولی یکسان، معیارها را واضح تر و در نتیجه درک آن را دشوارتر می کند.
    - معمولاً اضافه کردن اقداماتی که با انتقال بین حالت ها همراه است دشوار است. روش جدولی حالت ها و انتقال های بین آنها را در نظر می گیرد، اما باید تکمیل شود تا بتوان با هر تغییر حالت، محاسبات دلخواه را انجام داد.
    تفاوت اصلی بین ماشین های حالت مبتنی بر جدول و حالت الگو را می توان به صورت زیر فرموله کرد: حالت الگو رفتار وابسته به حالت را مدل می کند و روش جدول بر تعریف انتقال بین حالت ها تمرکز دارد. منبع original.ru

    source.ru اصلی
  3. ایجاد و تخریب اشیاء حالت.
    در طول فرآیند توسعه معمولاً باید بین موارد زیر یکی را انتخاب کنید:
    - ایجاد اشیاء حالت در صورت نیاز و از بین بردن آنها بلافاصله پس از استفاده،
    - ایجاد آنها از قبل و برای همیشه.

    گزینه اول زمانی ارجحیت دارد که از قبل معلوم نباشد سیستم در چه حالت هایی قرار خواهد گرفت و زمینه نسبتاً به ندرت وضعیت را تغییر می دهد. در عین حال، ما اشیایی را ایجاد نمی کنیم که هرگز مورد استفاده قرار نگیرند، که اگر اطلاعات زیادی در اشیاء حالت ذخیره شود مهم است. هنگامی که تغییرات حالت به طور مکرر اتفاق می افتد، بنابراین نمی خواهید اشیایی که آنها را نشان می دهند از بین ببرید (زیرا ممکن است خیلی زود دوباره مورد نیاز باشند)، باید از رویکرد دوم استفاده کنید. زمان برای ایجاد اشیا فقط یک بار در همان ابتدا صرف می شود و زمان برای تخریب اصلاً صرف نمی شود. درست است، این رویکرد ممکن است ناخوشایند باشد، زیرا زمینه باید ارجاعات به همه حالت هایی را که سیستم از نظر تئوری در آن قرار می گیرد ذخیره کند. source.ru اصلی

    منبع سایت سایت اصلی
  4. استفاده از تغییرات پویا
    می توان با تغییر کلاس شی در زمان اجرا، رفتار درخواستی را تغییر داد، اما اکثر زبان های شی گرا این را پشتیبانی نمی کنند. استثنا پرل، جاوا اسکریپت و سایر زبان‌های مبتنی بر موتور برنامه‌نویسی است که چنین مکانیزمی را ارائه می‌کنند و بنابراین مستقیماً از Pattern State پشتیبانی می‌کنند. این به اشیا اجازه می دهد تا با تغییر کد کلاس خود رفتار خود را تغییر دهند. source.ru اصلی

    منبع اصلی.ru

نتایج

نتایج استفاده حالت الگو: منبع original.ru
  1. رفتار وابسته به دولت را بومی سازی می کند.
    و آن را به قسمت های مربوط به حالت ها تقسیم می کند. الگوی حالت تمام رفتارهای مرتبط با یک حالت خاص را در یک شی جداگانه قرار می دهد. زیرا کد وابسته به حالت به طور کامل در یکی از زیر کلاس های کلاس موجود است حالت، سپس می توانید حالت ها و انتقال های جدید را به سادگی با ایجاد زیر کلاس های جدید اضافه کنید.
    در عوض، می توان از اعضای داده برای تعریف حالت های داخلی و سپس عملیات شی استفاده کرد متن نوشتهاین داده ها را بررسی می کند. اما در این مورد، دستورات شرطی مشابه یا عبارات شاخه در سراسر کد کلاس پراکنده خواهند شد متن نوشته. با این حال، افزودن حالت جدید مستلزم تغییر چندین عملیات است که تعمیر و نگهداری را دشوار می کند. الگوی حالت این مشکل را حل می کند، اما مشکل دیگری را نیز ایجاد می کند، زیرا رفتار برای حالت های مختلف در نهایت بین چندین زیر کلاس توزیع می شود. حالت. این باعث افزایش تعداد کلاس ها می شود. البته، یک کلاس فشرده تر است، اما اگر حالت های زیادی وجود داشته باشد، چنین توزیعی کارآمدتر است، زیرا در غیر این صورت باید با گزاره های شرطی دست و پا گیر سر و کار داشت.
    داشتن عبارات مشروط دست و پا گیر نامطلوب است، و همچنین داشتن رویه های طولانی نامطلوب است. آنها بیش از حد یکپارچه هستند، به همین دلیل است که اصلاح و گسترش کد مشکل ساز می شود. الگوی حالت راه بهتری برای ساختار کد وابسته به حالت ارائه می دهد. منطقی که انتقال حالت را توصیف می کند، دیگر در گزاره های یکپارچه پیچیده نمی شود اگریا تعویض، اما بین زیر کلاس ها توزیع شده است حالت. با کپسوله کردن هر انتقال و عمل در یک کلاس، حالت به یک شیء تمام عیار تبدیل می شود. این ساختار کد را بهبود می بخشد و هدف آن را واضح تر می کند. منبع original.ru
  2. انتقال بین حالت ها را صریح می کند.
    اگر یک شی وضعیت فعلی خود را صرفاً بر اساس داده های داخلی تعریف کند، انتقال بین حالت ها هیچ نمایش صریحی ندارد. آنها فقط به عنوان تخصیص به متغیرهای خاص ظاهر می شوند. معرفی اشیاء مجزا برای حالت های مختلف، انتقال را واضح تر می کند. علاوه بر این، اشیاء حالتمی تواند از زمینه محافظت کند متن نوشتهاز عدم تطابق متغیرهای داخلی، زیرا انتقال از نقطه نظر زمینه، اقدامات اتمی است. برای انجام انتقال، باید مقدار تنها یک متغیر را تغییر دهید (متغیر شی حالتدر کلاس متن نوشته) به جای چندین. منبع original.ru
  3. اشیاء حالت را می توان به اشتراک گذاشت.
    اگر در یک شیء حالت حالتهیچ متغیر نمونه ای وجود ندارد، به این معنی که حالتی که نشان می دهد فقط توسط خود نوع کدگذاری می شود، سپس زمینه های مختلف می توانند یک شی را به اشتراک بگذارند. حالت. وقتی دولت ها به این شکل از هم جدا می شوند، اساساً اپورتونیست هستند (به الگوی اپورتونیستی مراجعه کنید) که هیچ حالت درونی ندارند، بلکه فقط رفتار دارند. منبع سایت سایت اصلی

مثال

بیایید به اجرای مثال از بخش "" نگاه کنیم، i.e. ساخت چند معماری ساده اتصال TCP این یک نسخه ساده شده از پروتکل TCP است؛ البته، کل پروتکل یا حتی تمام حالت های اتصالات TCP را نشان نمی دهد. منبع اصلی سایت

اول از همه کلاس را تعریف می کنیم TCPConnection، که یک رابط برای انتقال داده ارائه می دهد و درخواست های تغییر حالت را رسیدگی می کند: TCPConnection. source.ru اصلی

در یک متغیر عضو حالتکلاس TCPConnectionنمونه ای از کلاس ذخیره می شود TCPS State. این کلاس رابط تغییر حالت تعریف شده در کلاس را کپی می کند TCPConnection. منبع سایت سایت اصلی

منبع original.ru

TCPConnectionتمام درخواست های وابسته به حالت را به نمونه ذخیره شده در حالت تفویض می کند TCPS State. همچنین در کلاس TCPConnectionیک عملیات وجود دارد تغییر وضعیت، که با آن می توانید یک اشاره گر به یک شی دیگر در این متغیر بنویسید TCPS State. سازنده کلاس TCPConnectionمقداردهی اولیه می کند حالتاشاره گر به حالت بسته TCP بسته شد(در زیر آن را تعریف می کنیم). source.ru

منبع سایت سایت اصلی

هر عملیات TCPS Stateیک نمونه را می پذیرد TCPConnectionبه عنوان یک پارامتر، در نتیجه به شی اجازه می دهد TCPS Stateدسترسی به داده های شی TCPConnectionو وضعیت اتصال را تغییر دهید. .ru

در کلاس TCPS Stateرفتار پیش‌فرض را برای تمام درخواست‌های واگذار شده به آن پیاده‌سازی کرد. همچنین می تواند وضعیت یک شی را تغییر دهد TCPConnectionاز طریق یک عملیات تغییر وضعیت. TCPS Stateدر همان بسته قرار دارد TCPConnection، بنابراین به این عملیات نیز دسترسی دارد: TCPState. منبع اصلی وب سایت

source.ru

در زیر کلاس ها TCPS Stateرفتار وابسته به دولت اجرا شد. یک اتصال TCP می تواند در بسیاری از حالت ها باشد: ایجاد(نصب شده است)، استماع(استماع)، بسته شد(بسته) و غیره و هر کدام زیر کلاس مخصوص به خود را دارند TCPS State. برای سادگی، ما فقط 3 زیر کلاس را با جزئیات در نظر خواهیم گرفت - TCPE تاسیس شد, TCPListenو TCP بسته شد. منبع وب سایت اصلی

منبع اصلی.ru

در زیر کلاس ها TCPS Stateرفتار وابسته به حالت را برای آن دسته از درخواست هایی که در آن حالت معتبر هستند پیاده سازی می کند. منبع original.ru

منبع اصلی سایت

پس از انجام اقدامات خاص حالت، این عملیات سایت منبع اصلی سایت

علت تغییر وضعیتبرای تغییر وضعیت یک شی TCPConnection. او خودش هیچ اطلاعاتی در مورد پروتکل TCP ندارد. زیر کلاس ها است TCPS Stateانتقال بین حالت ها و اقدامات دیکته شده توسط پروتکل را تعریف کنید. منبع اصلی وب سایت

Ru اصلی

کاربردهای شناخته شده الگوی ایالتی

رالف جانسون و جاناتان تسوایگ الگوی حالت را مشخص می کنند و آن را در رابطه با پروتکل TCP توصیف می کنند.
اکثر برنامه های ترسیم تعاملی محبوب "ابزار" را برای انجام عملیات دستکاری مستقیم فراهم می کنند. به عنوان مثال، یک ابزار رسم خط به کاربر این امکان را می دهد که روی یک نقطه دلخواه با ماوس کلیک کند و سپس ماوس را برای کشیدن خطی از آن نقطه حرکت دهد. ابزار انتخاب به شما امکان می دهد برخی از اشکال را انتخاب کنید. به طور معمول، تمام ابزارهای موجود در پالت قرار می گیرند. کار کاربر انتخاب و اعمال یک ابزار است، اما در واقع رفتار ویرایشگر با تغییر ابزار متفاوت است: با ابزار ترسیم شکل ها را ایجاد می کنیم، با ابزار انتخاب آنها را انتخاب می کنیم و غیره. منبع original.ru

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

شما می توانید یک کلاس انتزاعی تعریف کنید ابزار، که زیر کلاس‌های آن رفتار خاص ابزار را پیاده‌سازی می‌کنند. ویرایشگر گرافیکی پیوندی را به شی فعلی ذخیره می کند هملو درخواست های دریافتی را به او تفویض می کند. هنگامی که یک ابزار را انتخاب می کنید، ویرایشگر از یک شی متفاوت استفاده می کند که باعث تغییر رفتار می شود. source.ru

این تکنیک در چارچوب ویرایشگرهای گرافیکی HotDraw و Unidraw استفاده می شود. این به مشتریان اجازه می دهد تا به راحتی انواع جدیدی از ابزارها را تعریف کنند. که در هات دراوکلاس DrawingControllerدرخواست ها را به شی فعلی ارسال می کند ابزار. که در Unidrawکلاس های مربوطه نامیده می شوند بینندهو ابزار. نمودار کلاس زیر یک نمایش شماتیک از رابط های کلاس ارائه می دهد ابزار

منبع سایت سایت اصلی

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

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

نقض اصل جایگزینی لیسکوف برای شخص. یارون مینسکی یک رویکرد جایگزین پیشنهاد کرد: دولت های غیرقانونی را غیرقابل نمایندگی کند. این امکان انتقال بررسی خطا از زمان اجرا به زمان کامپایل را فراهم می کند. با این حال، جریان کنترل در این مورد بر اساس تطبیق الگو، و نه با استفاده از چند شکلی سازماندهی می شود. خوشبختانه، پشتیبانی جزئی برای تطبیق الگو در C#7 ظاهر شد.

جزئیات بیشتر در مورد مثال F# موضوع دولت های غیرقانونی را غیرقابل نمایندگی کنددر وب سایت اسکات ولاشین فاش شد.

بیایید اجرای "state" را با استفاده از مثال یک سبد در نظر بگیریم. C# نوع اتحاد داخلی ندارد. بیایید داده ها و رفتار را از هم جدا کنیم. ما خود حالت را با استفاده از enum و رفتار را به عنوان یک کلاس جداگانه رمزگذاری می کنیم. برای راحتی، بیایید یک ویژگی را اعلام کنیم که enum و کلاس رفتار مربوطه را به هم متصل می‌کند، کلاس «state» پایه، و یک متد افزونه برای انتقال از enum به کلاس رفتار اضافه می‌کنیم.

زیر ساخت

کلاس عمومی StateAttribute: مشخصه (نوع عمومی StateType ( get; ) public StateAttribute(Type stateType) ( StateType = stateType ?? throw new ArgumentNullException(nameof(stateType));) ) کلاس انتزاعی عمومی State جایی که T: کلاس ( حالت محافظت شده( موجودیت T) ( موجودیت = موجودیت ?? پرتاب جدید ArgumentNullException(nameof(entity)); ToState (این Enum stateCode، موجودیت شی) که در آن T: کلاس // بله، بله بازتاب کند است. با درخت عبارت کامپایل شده // یا IL Emit جایگزین کنید و سریع خواهد بود => (State ) Activator.CreateInstance(stateCode.GetType() .GetCustomAttribute ().StateType، نهاد); )

موضوع

بیایید موجودیت "سبد" را اعلام کنیم:

رابط عمومی IHasState جایی که TEntity: کلاس ( TStateCode StateCode ( get; ) State State ( get; ) ) public partal class سبد خرید: IHasState (کاربر عمومی کاربر ( دریافت؛ مجموعه محافظت شده؛ ) عمومی CartStateCode StateCode ( دریافت؛ مجموعه محافظت شده؛ ) دولتی عمومی State => StateCode.ToState (این)؛ اعشاری عمومی مجموع ( دریافت؛ مجموعه محافظت شده؛ ) ICollection مجازی محافظت شده محصولات (دریافت؛ مجموعه؛ ) = فهرست جدید ()؛ // ORM فقط محافظت شده سبد خرید() ( ) عمومی سبد خرید (کاربر کاربر) (کاربر = کاربر ?? پرتاب جدید ArgumentNullException(نام(کاربر))؛ StateCode = StateCode = CartStateCode.Empty؛ ) عمومی سبد (کاربر کاربر، IEnumerable محصولات) : این (کاربر) ( StateCode = StateCode = CartStateCode.Empty؛ foreach (محصول var در محصولات) ( Products.Add(product); ) ) سبد عمومی (کاربر کاربر، IEnumerable محصولات، مجموع اعشاری): این (کاربر، محصولات) (اگر (کل<= 0) { throw new ArgumentException(nameof(total)); } Total = total; } }
ما یک کلاس را برای هر حالت سبد خرید پیاده سازی می کنیم: خالی، فعال و پولی، اما رابط مشترکی را اعلام نمی کنیم. اجازه دهید هر ایالت فقط رفتار مربوطه را اجرا کند. این بدان معنا نیست که کلاس‌های EmptyCartState، ActiveCartState و PaidCartState همگی نمی‌توانند یک رابط را پیاده‌سازی کنند. آنها می توانند، اما چنین رابطی فقط باید شامل متدهایی باشد که در هر ایالت موجود است. در مورد ما، متد Add در EmptyCartState و ActiveCartState موجود است، بنابراین می توانیم آنها را از AddableCartStateBase انتزاعی به ارث ببریم. با این حال، شما فقط می توانید موارد را به یک سبد خرید بدون پرداخت اضافه کنید، بنابراین یک رابط مشترک برای همه ایالت ها وجود نخواهد داشت. به این ترتیب ما تضمین می کنیم که در زمان کامپایل هیچ InvalidOperationException در کد ما وجود ندارد.

کلاس جزئی عمومی سبد خرید ( enum عمومی CartStateCode: بایت ( خالی، فعال، پولی ) رابط عمومی IAddableCartState ( ActiveCartState افزودن (محصول محصول)؛ IEnumerable محصولات ( دریافت ) ) رابط عمومی INotEmptyCartState ( IEnumerable محصولات ( دریافت؛ ) اعشاری مجموع ( دریافت؛ ) ) کلاس انتزاعی عمومی AddableCartState: State , IAddableCartState ( محافظت شده AddableCartState(Cart entity): base(entity) ( ) public ActiveCartState Add(Product product) ( Entity.Products.Add(product)؛ Entity.StateCode = CartStateCode.Active؛ بازگشت (ActiveStateCart;) عمومی IEnumerable محصولات => Entity.Products; ) کلاس عمومی EmptyCartState: AddableCartState ( عمومی EmptyCartState(Cart entity): base(entity) ( ) ) class public ActiveCartState: AddableCartState، INotEmptyCartState ( عمومی ActiveCartState(Cart entity)(مجموعه کارت) Entity.Total = مجموع؛ Entity.StateCode = CartStateCode.Paid؛ بازگشت (PaidCartState) Entity.State؛ ) دولت عمومی Remove(Product product) ( Entity.Products.Remove(product); if(!Entity.Products.Any()) ( Entity.StateCode = CartStateCode.Empty; ) return Entity.State; ) public EmptyCartState Clear() ( Entity. Products.Clear(); Entity.StateCode = CartStateCode.Empty؛ return (EmptyCartState)Entity.State؛ ) اعشاری عمومی Total => Products.Sum(x => x.Price); ) کلاس عمومی PaidCartState: State , INotEmptyCartState ( عمومی IEnumerable محصولات => Entity.Products; اعشار عمومی Total => Entity.Total; عمومی PaidCartState (ماهیت سبد خرید): پایه (موجود) ( ) )
ایالات تودرتو اعلام می شوند ( تو در تو) کلاس ها تصادفی نیستند. کلاس های تودرتو به اعضای محافظت شده کلاس Cart دسترسی دارند، به این معنی که ما مجبور نیستیم برای پیاده سازی رفتار، کپسوله سازی موجودیت را قربانی کنیم. برای جلوگیری از به هم ریختگی در فایل کلاس موجودیت، من اعلان را به دو بخش تقسیم کردم: Cart.cs و CartStates.cs با استفاده از کلمه کلیدی جزئی.

عمومی ActionResult GetViewResult(State cartState) ( سوئیچ (cartState) ( case Cart.ActiveCartState activeState: return View("Active", activeState)؛ case Cart.EmptyCartState valaState: return View("Empty"، whiteState)؛ case Cart.PaidCartState paidCartState: return View(" پرداخت شده، payCartState)؛ پیش فرض: پرتاب جدید InvalidOperationException(); ) )
بسته به وضعیت سبد از نماهای مختلفی استفاده خواهیم کرد. برای یک سبد خالی، پیام «سبد شما خالی است» را نمایش می دهیم. سبد فعال شامل لیستی از محصولات، امکان تغییر تعداد محصولات و حذف برخی از آنها، دکمه ثبت سفارش و کل مبلغ خرید خواهد بود.

سبد پولی مانند سبد خرید فعال خواهد بود، اما بدون امکان ویرایش چیزی. این واقعیت را می توان با برجسته کردن رابط INotEmptyCartState متوجه شد. بنابراین، ما نه تنها از نقض اصل جایگزینی Liskov خلاص شدیم، بلکه اصل جداسازی رابط را نیز اعمال کردیم.

نتیجه

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

با استفاده از سیستم؛ وضعیت فضای نام (///

/// وضعیت حساب مسدود شده است. ///کلاس عمومی مسدود شده: IState (/// /// حساب خود را شارژ کنید. /// /// حساب قابل شارژ مجدد /// مقدار پر کردن.سپرده خالی عمومی (کارت کارت، پول اعشاری) ( // آرگومان های ورودی را از نظر صحت بررسی کنید. if (کارت == تهی) (پرتاب ArgumentNullException جدید(نام (کارت))؛ ) if (پول<= 0) { throw new ArgumentException("Вносимая сумма должна быть больше нуля.", nameof(money)); } // Вычисляем сумму сверхлимитной задолженности. var overdraft = card.CreditLimit - card.Credit; // Вычисляем насколько сумма пополнения перекрывает задолженность. var difference = money - overdraft; if (difference < 0) { // Если сумма пополнения не перекрывает задолженность, // то просто уменьшаем сумму задолженности. card.Credit += money; // Вычисляем процент оставшейся суммы на счете. var limit = card.Credit / card.CreditLimit * 100; if (limit < 10) { // Если после пополнения на счете все еще меньше десяти процентов от лимита, // то просто сообщаем об этом пользователю. Console.WriteLine($"Ваш счет пополнен на сумму {money}. " + $"Сумма на вашем счете все еще составляет менее 10%. Ваш счет остался заблокирован. Пополните счет на большую сумму. {card.ToString()}"); } else if (limit >= 10 && محدود< 100) { // Если задолженность перекрыта не полностью, то переводим в состояние расходования кредитных средств. card.State = new UsingCreditFunds(); Console.WriteLine($"Ваш счет пополнен на сумму {money}. Задолженность частично погашена. " + $"Погасите задолженность в размере {Math.Abs(difference)} рублей. {card.ToString()}"); } else { // Иначе задолженность полностью погашена, переводим в состояние расходования собственных средств. card.State = new UsingOwnFunds(); Console.WriteLine($"Ваш счет пополнен на {money} рублей. Задолженность полностью погашена. {card.ToString()}"); } } else { // Иначе закрываем задолженность, а оставшиеся средства переводим в собственные средства. card.Credit = card.CreditLimit; card.Debit = difference; // Переводим карту в состояние использования собственных средств. card.State = new UsingOwnFunds(); Console.WriteLine($"Ваш счет пополнен на {money} рублей. " + $"Кредитная задолженность погашена. {card.ToString()}"); } } /// /// خرج از حساب. /// /// حساب رد شده /// هزینه خرید. /// موفقیت عملیات. public bool Spend(کارت کارت، قیمت اعشاری) ( // رد عملیات. Console.WriteLine($"حساب شما مسدود شده است. حساب خود را پر کنید. (card.ToString())"); return false; ) ) )

حالتیک الگوی رفتاری است که به شما این امکان را می دهد که به صورت پویا رفتار یک شی را هنگامی که حالت آن تغییر می کند تغییر دهید.

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

ویژگی های الگو در جاوا

پیچیدگی:

محبوبیت:

کاربرد:الگوی State اغلب در جاوا برای تبدیل ماشین های دست و پا گیر حالت ساخته شده بر روی دستورات سوئیچ به اشیا استفاده می شود.

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

  • javax.faces.lifecycle.LifeCycle#execute() (کنترل شده از FacesServlet: رفتار بستگی به فاز JSF فعلی دارد)

علائم استفاده از الگو:متدهای کلاس کار را به یک شی تودرتو واگذار می کنند.

پخش کننده صدا

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

ایالت ها

states/State.java:رابط حالت مشترک

بسته site.state.example..state.example.ui.Player; /** * رابط مشترک برای همه حالت ها. */ کلاس انتزاعی عمومی State ( Player Player; /** * Context خود را به سازنده state منتقل می کند تا دولت بتواند در صورت نیاز * به داده ها و روش های خود در آینده دسترسی داشته باشد. */ State(Player player) ( this.player = پخش کننده ; ) انتزاع عمومی String onLock()؛ رشته انتزاعی عمومی onPlay()؛ رشته انتزاعی عمومی onNext(؛ رشته انتزاعی عمومی onPrevious();)

states/LockedState.java:حالت "قفل شده"

بسته site.state.example..state.example.ui.Player; /** * حالت های بتن روش های حالت انتزاعی را به روش خود پیاده می کنند. */ کلاس عمومی LockedState حالت را گسترش می دهد ( LockedState(بازیکن پخش کننده) ( super(player); player.setPlaying(false); ) @Override public string onLock() ( if (player.isPlaying()) (player.changeState(RedyState جدید (پخش کننده))؛ بازگشت "توقف بازی"؛ ) else (بازگرداندن "قفل شده..."؛ ) ) @Override public String onPlay() (player.changeState(new ReadyState(player))؛ بازگشت "آماده"؛ ) @ Override public String onNext() ( بازگشت "Locked..."; ) @Override public String onPrevious() ( بازگشت "Locked..."; ) )

states/ReadyState.java:حالت آماده

بسته site.state.example..state.example.ui.Player; /** * آنها همچنین می توانند زمینه را به حالت های دیگر انتقال دهند. */ کلاس عمومی ReadyState حالت را گسترش می دهد ( Public ReadyState(Player Player) ( super(player); ) @Override public String onLock() (player.changeState(new LockedState(player)); بازگشت "Locked..."; Override public String onPlay() ( String action = player.startPlayback(); player.changeState(New PlayingState(player)); return action; ) @Override public String onNext() ( بازگشت "Locked..."; ) @Override Public String onPrevious() (بازگرداندن "قفل شده..."؛ ))

states/PlayingState.java:حالت "بازی".

بسته site.state.example..state.example.ui.Player; کلاس عمومی PlayingState حالت را گسترش می دهد ( PlayingState (بازیکن پخش کننده) ( فوق العاده (بازیکن)؛ ) @Override public String onLock() (player.changeState(New LockedState(player)); player.setCurrentTrackAfterStop(); بازگشت "توقف پخش"؛ ) @Override Public String onPlay() ( player.changeState(new ReadyState(player)); بازگشت "مکث..."؛ ) @Override public String onNext() ( return player.nextTrack(); ) @Override Public String onPrevious( ) (بازگشت بازیکن.previousTrack();))

رابط کاربری

ui/Player.java:بازیکن

بسته site.state.example..state.example.states.state.example.states.State; وارد کردن java.util.ArrayList; وارد کردن java.util.List. پخش کننده کلاس عمومی ( حالت دولتی خصوصی؛ پخش بولی خصوصی = نادرست؛ فهرست خصوصی لیست پخش = ArrayList جدید<>()؛ private int currentTrack = 0; public Player() ( this.state = new ReadyState(this)؛ setPlaying(true); for (int i = 1; i<= 12; i++) { playlist.add("Track " + i); } } public void changeState(State state) { this.state = state; } public State getState() { return state; } public void setPlaying(boolean playing) { this.playing = playing; } public boolean isPlaying() { return playing; } public String startPlayback() { return "Playing " + playlist.get(currentTrack); } public String nextTrack() { currentTrack++; if (currentTrack >playlist.size() - 1) (currentTrack = 0; ) return "Playing" + playlist.get(currentTrack); ) رشته عمومی previousTrack() (currentTrack--؛ if (currentTrack< 0) { currentTrack = playlist.size() - 1; } return "Playing " + playlist.get(currentTrack); } public void setCurrentTrackAfterStop() { this.currentTrack = 0; } }

UI/UI.java:رابط کاربری گرافیکی پلیر

بسته site.state.example.ui; واردات javax.swing.*; واردات java.awt.*; UI کلاس عمومی ( پخش کننده خصوصی؛ خصوصی استاتیک JTextField textField = new JTextField(); public UI (Player player) ( this.player = player; ) public void init() ( JFrame frame = new JFrame ("Test player"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)؛ زمینه JPanel = جدید JPanel()؛ context.setLayout (New BoxLayout(context, BoxLayout.Y_AXIS))؛ frame.getContentPane().add(context)؛ دکمه های JPanel(new JPanel) FlowLayout(FlowLayout.CENTER)؛ context.add(textField); context.add(buttons)؛ // زمینه باعث می شود که حالت به جای خودش به ورودی کاربر // پاسخ دهد. واکنش ممکن است بسته به حالت // متفاوت باشد. در حال حاضر فعال است. JButton play = new JButton("Play"); play.addActionListener(e -> textField.setText(player.getState().onPlay())); JButton stop = new JButton("Stop") stop.addActionListener (e -> textField.setText(player.getState().onLock())); JButton next = new JButton("Next"); next.addActionListener(e -> textField.setText(player.getState( ).onNext( ))); JButton prev = new JButton("Prev"); prev.addActionListener(e -> textField.setText(player.getState().onPrevious())); frame.setVisible(true); frame.setSize(300, 100); buttons.add(play); buttons.add(stop); buttons.add(next); buttons.add(prev); ))

Demo.java:کد مشتری

بسته refactoring_guru.state..state.example.ui..state.example.ui.UI; /** * کلاس دمو. اینجاست که همه چیز با هم جمع می شود. */ کلاس عمومی نسخه نمایشی ( عمومی استاتیک void main(string args) ( Player player = new Player(); UI UI = new UI (player); ui.init(); ))

حالت(انگلیسی) حالت) یک الگوی طراحی رفتاری است. در مواردی استفاده می شود که در طول اجرای برنامه، یک شی باید رفتار خود را بسته به حالت خود تغییر دهد.

هدف از الگوی دولت

    الگوی State به یک شی اجازه می دهد تا رفتار خود را بسته به حالت داخلی خود تغییر دهد. به نظر می رسد که شی کلاس خود را تغییر داده است.

    الگوی State یک پیاده سازی شی گرا از یک ماشین حالت است.

مشکلی که باید حل شود

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

بحث در مورد الگوی دولت

الگوی State این مشکل را به صورت زیر حل می کند:

    یک کلاس Context را معرفی می کند که یک رابط را برای دنیای خارج تعریف می کند.

    کلاس State Abstract را معرفی می کند.

    «وضعیت‌های» مختلف یک ماشین دولتی را به عنوان زیر کلاس‌های دولت نشان می‌دهد.

    کلاس Context یک اشاره گر به وضعیت فعلی دارد که با تغییر وضعیت ماشین حالت تغییر می کند.

الگوی State مشخص نمی کند که دقیقاً کجا شرط انتقال به حالت جدید تعیین می شود. دو گزینه وجود دارد: کلاس Context یا زیر کلاس های State. مزیت گزینه دوم این است که به راحتی می توان کلاس های مشتق شده جدید را اضافه کرد. نقطه ضعف این است که هر زیر کلاس ایالتی باید در مورد همسایگان خود بداند تا به یک حالت جدید منتقل شود، که وابستگی بین زیر کلاس ها را معرفی می کند.

ساختار الگوی حالت

کلاس Context رابط خارجی را برای کلاینت ها تعریف می کند و ارجاعی به وضعیت فعلی شی State را ذخیره می کند. رابط کلاس پایه انتزاعی State رابط Context را به استثنای یک پارامتر اضافی تکرار می کند - یک اشاره گر به یک نمونه از Context. کلاس های مشتق از حالت رفتار خاص حالت را تعریف می کنند. کلاس Context wrapper تمام درخواست‌های دریافت‌شده را به یک شی «وضعیت فعلی» واگذار می‌کند، که می‌تواند از پارامتر اضافی دریافت‌شده برای دسترسی به نمونه Context استفاده کند.

نمودار کلاس UML از الگوی حالت

نمونه الگوی حالت

الگوی State به یک شی اجازه می دهد تا رفتار خود را بسته به حالت داخلی خود تغییر دهد. تصویر مشابهی را می توان در عملکرد یک دستگاه فروش خودکار مشاهده کرد. ماشین آلات بسته به در دسترس بودن کالا، میزان سکه های دریافتی، قابلیت تبادل پول و غیره می توانند حالت های مختلفی داشته باشند. پس از انتخاب و پرداخت محصول توسط خریدار، شرایط (حالت) زیر امکان پذیر است:

    کالا را به خریدار بدهید، نیازی به تغییر نیست.

    کالا را به خریدار بدهید و تعویض کنید.

    خریدار به دلیل نداشتن پول کافی کالا را دریافت نخواهد کرد.

    خریدار کالا را به دلیل عدم وجود آن دریافت نمی کند.

پیاده سازی

شکل 1. نمودار کلاس الگوی حالت.

این الگو از 3 بلوک تشکیل شده است:

    ویجت کلاسی است که اشیاء آن بسته به حالتشان باید رفتارشان را تغییر دهند.

    IState یک رابط است که باید هر یک از حالت های خاص را پیاده سازی کند. از طریق این رابط، شی ویجت با تفویض فراخوانی متد به آن، با وضعیت تعامل دارد. رابط باید حاوی ابزاری برای ارائه بازخورد به شیئی باشد که رفتار آن باید تغییر کند. برای این کار از یک رویداد (Publisher - Subscriber Pattern) استفاده می شود. این به منظور جایگزینی شیء حالت در حین اجرای برنامه هنگام وقوع رویدادها ضروری است. ممکن است مواردی وجود داشته باشد که خود ویجت به صورت دوره‌ای از شی وضعیت برای وجود یک انتقال نظرسنجی می‌کند.

    StateA ... StateZ – کلاس های حالت های خاص. باید حاوی اطلاعاتی در مورد اینکه یک شی تحت چه شرایطی و به چه حالت هایی می تواند از وضعیت فعلی خود تغییر کند. به عنوان مثال، از StateA یک شی می تواند به StateB و StateC برود و از StateB به StateA برگردد و غیره. شیء یکی از آنها هنگام ایجاد باید حاوی ویجت باشد.

رابط عمومی IState

رویداد StateHandler NextState.

void SomeMethod();

رابط عمومی IWidget

void SomeMethod();

کلاس عمومی StateA: IState

public void SomeMethod()

Console.WriteLine("StateA.SomeMethod");

if (NextState != null)

NextState(New StateB());

کلاس عمومی StateB: IState

رویداد عمومی StateHandler NextState.

public void SomeMethod()

Console.WriteLine("StateB.SomeMethod");

if (NextState != null)

NextState(New StateA());

نماینده عمومی void StateHandler(IState state);

ویجت کلاس عمومی: IWidget

ویجت عمومی (ایالت IS)

OnNextState(state);

خلأ خصوصی OnNextState (وضعیت IState)

اگر (حالت == تهی)

پرتاب جدید ArgumentNullException("state");

اگر (دولت != حالت)

State.NextState += New StateHandler(OnNextState);

دولت خصوصی IS;

public void SomeMethod()

state.SomeMethod();

دولت ایالتی عمومی

دریافت (وضعیت بازگشت؛)

مجموعه (وضعیت = مقدار؛)

بیایید به یک مثال نگاه کنیم:

IWidget widget = new widget(new StateA());

widget.SomeMethod();

widget.SomeMethod();

هنگامی که یک شیء ویجت ایجاد می شود، یک شیء که حالت را محصور می کند از پارامتر سازنده عبور داده می شود. این حالت حالت فعلی خواهد بود؛ رویداد NextState آن با متد OnNextState() مشترک می شود که وضعیت را با شیء حالت ارسال شده جایگزین می کند. اولین باری که متد SomeMethod() فراخوانی می شود، شی ویجت فراخوانی را به شی StateA واگذار می کند. پس از اجرای متد StateA.SomeMethod()، شی رویداد NextState را بالا می برد و یک شی StateB را به عنوان پارامتر ارسال می کند که جایگزین حالت فعلی StateA می شود. فراخوانی SomeMethod() برای بار دوم، StateB.SomeMethod() را فراخوانی می کند. یعنی به طور رسمی همان روش نامیده می شود، اما رفتار آن متفاوت است.

توجه به این نکته مهم است که فقط شیء حالت فعلی در یک زمان معین وجود دارد. یعنی هنگام انتقال از یک حالت به حالت دیگر، شی قبلی حذف می شود و شی جدید جای آن را می گیرد.

بهترین مقالات در این زمینه