Davlat davlati. Java-da Strategiya va Davlat naqshlari o'rtasidagi farqlar

11.06.2024
Noyob kelinlar qaynonasi bilan teng va do'stona munosabatda bo'lishlari bilan maqtanishlari mumkin. Odatda buning aksi bo'ladi

E'tirof etish vaqti keldi: men bu asosiy narsadan biroz oshib ketdim. Bu GoF State dizayn namunasi haqida bo'lishi kerak edi. Lekin men kontseptsiyaga tegmasdan o'yinlarda foydalanish haqida gapira olmayman chekli holat mashinalari(yoki "FSM"). Lekin men bunga kirganimdan keyin eslashim kerakligini angladim ierarxik holat mashinasi yoki ierarxik avtomat Va jurnal xotirasi bo'lgan avtomatik mashina (pastga tushirish avtomati).

Bu juda keng mavzu, shuning uchun bu bobni iloji boricha qisqa tutish uchun men ba'zi aniq kod misollarini qoldiraman va siz ba'zi bo'shliqlarni o'zingiz to'ldirishingiz kerak bo'ladi. Umid qilamanki, bu ularni tushunarli qilmaydi.

Cheklangan holat mashinalari haqida hech qachon eshitmagan bo'lsangiz, xafa bo'lishingiz shart emas. Ular AI ishlab chiquvchilari va kompyuter xakerlariga yaxshi ma'lum, ammo boshqa sohalarda kam ma'lum. Menimcha, ular ko'proq e'tirofga loyiqdir, shuning uchun men sizga ular hal qiladigan bir nechta muammolarni ko'rsatmoqchiman.

Bularning barchasi sun'iy intellektning eski dastlabki kunlarining aks-sadolari. 50-60-yillarda sun'iy intellekt asosan til tuzilmalarini qayta ishlashga qaratilgan. Zamonaviy kompilyatorlarda qo'llaniladigan ko'plab texnologiyalar inson tillarini tahlil qilish uchun ixtiro qilingan.

Biz hammamiz u erda bo'lganmiz

Aytaylik, biz kichik yonma-yon aylantiruvchi platforma ustida ishlayapmiz. Bizning vazifamiz o'yin dunyosida o'yinchining avatariga aylanadigan qahramonni modellashtirishdir. Bu foydalanuvchi kiritishiga javob berishi kerakligini anglatadi. B tugmasini bosing va u sakrab chiqadi. Juda oddiy:

void Heroine::handleInput(Kirish kiritish) ( agar (kirish == PRESS_B) ( yVelocity_ = JUMP_VELOCITY; setGraphics(IMAGE_JUMP); ) )

Xatoni sezdingizmi?

Bu erda "havoga sakrash" ning oldini olish uchun kod yo'q; u havoda bo'lganida B tugmasini bosishni davom eting va u yana va yana uchib ketadi. Buni hal qilishning eng oson yo'li bu qahramonga isJumping_ mantiqiy bayrog'ini qo'shishdir, u qahramon qachon sakrab chiqqanini kuzatib boradi:

void Heroine::handleInput(Kirish kiritish) ( if (kirish == PRESS_B) ( if (!isJumping_) ( isJumping_ = true ; // Jump... ) ) )

Qahramon yana erga tegsa, bizga isJumping_ ni yolg'onga qaytaradigan kod ham kerak. Oddiylik uchun men ushbu kodni o'tkazib yuboraman.

void Heroine::handleInput(Kirish kiritish) ( agar (kirish == PRESS_B) ( // Agar sakrab chiqmagan bo'lsak...) else if (kirish == PRESS_DOWN) ( if (!isJumping_) ( setGraphics(IMAGE_DUCK); ) ) else if (kirish == RELEASE_DOWN) ( setGraphics(IMAGE_STAND); ) )

Bu yerda xatolikni sezdingizmi?

Ushbu kod yordamida o'yinchi:

  1. Cho'kish uchun pastga bosing.
  2. O'tirgan joydan sakrash uchun B tugmasini bosing.
  3. Havoda bo'lganingizda qo'yib yuboring.

Shu bilan birga, qahramon havoda turish grafikasiga o'tadi. Biz boshqa bayroq qo'shishimiz kerak...

void Heroine::handleInput(Kiritish kiritish) ( agar (kirish == PRESS_B) ( agar (!isJumping_ && !isDucking_) ( // O'tish... ) ) boshqacha bo'lsa (kirish == PRESS_DOWN) ( agar (!isJumping_) ( isDucking_ = true ; setGraphics(IMAGE_DUCK ) ) else if (kirish == RELEASE_DOWN) ( if (isDucking_) ( isDucking_ = false ; setGraphics(IMAGE_STAND); ) )

Endi o'yinchi havoda bosganida, qahramonga hujum qilish qobiliyatini qo'shish ajoyib bo'lar edi:

void Heroine::handleInput(Kiritish kiritish) ( agar (kirish == PRESS_B) ( agar (!isJumping_ && !isDucking_) ( // O'tish... ) ) boshqacha bo'lsa (kirish == PRESS_DOWN) ( agar (!isJumping_) ( isDucking_ = true ; setGraphics(IMAGE_DUCK ) else ( isJumping_ = false ; setGraphics(IMAGE_DIVE); ) ) else if (input == RELEASE_DOWN) ( if (isDucking_) ( // Standing... ) )

Yana xatolar qidirilmoqda. Topdingizmi?

Bizda havoda sakrashning iloji bo'lmasligi uchun tekshiruvimiz bor, lekin kurash paytida emas. Boshqa bayroq qoʻshilmoqda...

Bu yondashuvda noto'g'ri narsa bor. Har safar kodni bosganimizda, biror narsa buziladi. Yana bir qancha harakat qo'shishimiz kerak, bizda hatto yo'q yurish yo'q, lekin bu yondashuv bilan biz yana bir qancha xatolarni engishimiz kerak bo'ladi.

Biz hammamiz ideallashtirgan va ajoyib kod yaratadigan dasturchilar umuman supermen emas. Ular shunchaki xatolar kiritish bilan tahdid qiladigan kod uchun instinktni ishlab chiqdilar va iloji boricha undan qochishga harakat qilishadi.

Murakkab tarmoqlanish va o'zgaruvchan holatlar aynan siz qochishingiz kerak bo'lgan kod turlaridir.

Cheklangan holat mashinalari bizning najotimizdir

Hafsalangiz pir bo'lib, stolingizdan qalam va qog'ozdan tashqari hamma narsani olib tashlab, oqim sxemasini chizishni boshlaysiz. Biz qahramon qila oladigan har bir harakat uchun to'rtburchaklar chizamiz: tik turish, sakrash, cho'zilish va dumalash. U har qanday holatda tugmachalarni bosishga javob berishi uchun biz ushbu to'rtburchaklar orasiga o'qlarni chizamiz, ularning ustidagi tugmachalarni belgilaymiz va holatlarni bir-biriga bog'laymiz.

Tabriklaymiz, siz hozirgina yaratdingiz davlat mashinasi (chekli holat mashinasi). Ular kompyuter fanlari deb ataladigan sohadan keladi avtomatlar nazariyasi (avtomatlar nazariyasi), uning tuzilmalari oilasiga mashhur Tyuring mashinasi ham kiradi. FSM bu oilaning eng oddiy a'zosidir.

Xulosa shu:

    Bizda belgilangan to'plam bor davlatlar, unda pulemyot bo'lishi mumkin. Bizning misolimizda bular tik turish, sakrash, egilish va dumalash.

    Mashina faqat ichida bo'lishi mumkin bitta istalgan vaqtda davlat. Bizning qahramonimiz bir vaqtning o'zida sakrab turolmaydi. Aslida, FSM birinchi navbatda buni oldini olish uchun ishlatiladi.

    Keyingi ketma-ketlik kiritish yoki voqealar, mashinaga uzatiladi. Bizning misolimizda bu tugmalarni bosish va qo'yib yuborishdir.

    Har bir davlat bor o'tish to'plami, ularning har biri kirish bilan bog'langan va holatni bildiradi. Foydalanuvchi kiritish sodir bo'lganda, agar u joriy holatga mos kelsa, mashina o'z holatini o'q ko'rsatgan joyga o'zgartiradi.

    Misol uchun, tik turgan holda pastga bossangiz, u cho'kish holatiga o'tadi. Sakrash paytida pastga bosish hal qilish holatini o'zgartiradi. Agar joriy holatda kiritish uchun hech qanday o'tish ta'minlanmagan bo'lsa, hech narsa sodir bo'lmaydi.

Eng sof shaklda bu butun banan: holatlar, kirish va o'tishlar. Siz ularni blok diagramma shaklida tasvirlashingiz mumkin. Afsuski, kompilyator bunday yozuvlarni tushunmaydi. Xo'sh, qanday qilib amalga oshirish chekli holat mashinasi? To'rtlik to'dasi o'z versiyasini taklif qiladi, ammo biz undan ham oddiyroqdan boshlaymiz.

Mening sevimli FSM analogiyam eski matn qidiruvi Zork. Sizda o'tish joylari bilan bog'langan xonalardan iborat dunyo bor. Va siz ularni "shimolga borish" kabi buyruqlarni kiritish orqali o'rganishingiz mumkin.

Bunday xarita chekli holat mashinasining ta'rifiga to'liq mos keladi. Siz joylashgan xona hozirgi holat. Xonadan har bir chiqish o'tishdir. Navigatsiya buyruqlari - kiritish.

Ro'yxatlar va kalitlar

Bizning eski Heroine sinfimiz bilan bog'liq muammolardan biri shundaki, u mantiqiy kalitlarning noto'g'ri kombinatsiyasiga ruxsat beradi: isJumping_ va isDucking_, ular bir vaqtning o'zida haqiqiy bo'lishi mumkin emas. Va agar sizda bir nechta mantiqiy bayroqlar bo'lsa, ulardan faqat bittasi rost bo'lishi mumkin, ularning barchasini enum bilan almashtirgan ma'qul emasmi?

Bizning holatda, enum yordamida biz FSM ning barcha holatlarini shu tarzda to'liq tasvirlashimiz mumkin:

enum holati (STATE_STANDING, STATE_JUMPING, STATE_DUCKING, STATE_DIVING );

Qahramonda bir nechta bayroqlar o'rniga faqat bitta davlat maydoni mavjud. Shuningdek, biz dallanish tartibini o'zgartirishimiz kerak bo'ladi. Oldingi kod misolida biz avval kirishga, keyin esa holatga qarab tarmoqlandik. Bunda biz kodni bosilgan tugma bo'yicha guruhlashtirdik, lekin holatlar bilan bog'liq kodni xiralashtirdik. Endi biz teskarisini qilamiz va holatga qarab kirishni almashtiramiz. Biz nimaga erishamiz:

void Heroine::handleInput(Kirish kiritish) ( switch (holat_) (holat STATE_STANDING: agar (kirish == PRESS_B) (holat_ = STATE_JUMPING; yVelocity_ = JUMP_VELOCITY; setGraphics(IMAGE_JUMP); ) boshqacha (kirish =_)RESS = STATE_DUCKING; setGraphics(IMAGE_DUCK) ; IMAGE_STAND tanaffus ;

Bu juda ahamiyatsiz ko'rinadi, ammo shunga qaramay, bu kod avvalgisidan ancha yaxshi. Bizda hali ham shartli tarmoqlanish mavjud, ammo biz o'zgaruvchan holatni bitta maydonga soddalashtirdik. Bitta holatni boshqaradigan barcha kodlar bir joyda to'planadi. Bu cheklangan holat mashinasini amalga oshirishning eng oddiy usuli va ba'zida bu juda etarli.

Endi qahramon boshqa kira olmaydi noaniq holat. Mantiqiy bayroqlardan foydalanganda ba'zi kombinatsiyalar mumkin edi, ammo mantiqiy emas edi. Enumdan foydalanganda barcha qiymatlar to'g'ri.

Afsuski, sizning muammoingiz bu yechimdan oshib ketishi mumkin. Aytaylik, biz qahramonimizga maxsus hujum qo'shmoqchi edik, buning uchun qahramon zaryadlash uchun o'tirishi va keyin to'plangan energiyani chiqarishi kerak. Va biz o'tirganimizda, biz zaryadlash vaqtini kuzatishimiz kerak.

Zaryadlash vaqtini saqlash uchun Qahramonga chargeTime_ maydonini qo'shing. Aytaylik, bizda allaqachon har bir kadrda chaqirilgan update() usuli mavjud. Keling, unga quyidagi kodni qo'shamiz:

void Heroine ::update() ( if (state_ == STATE_DUCKING) (chargeTime_++; if (chargeTime_ > MAX_CHARGE)) ( superBomb(); ) ) )

Yangilash usuli namunasini taxmin qilgan bo'lsangiz, siz sovrin yutib oldingiz!

Har safar yana cho'kib ketganimizda, bu taymerni qayta o'rnatishimiz kerak. Buning uchun handleInput() ni o'zgartirishimiz kerak:

void Heroine::handleInput(Kirish kiritish) ( switch (holat_) (holat STATE_STANDING: agar (kirish == PRESS_DOWN) (holat_ = STATE_DUCKING; chargeTime_ = 0 ; setGraphics(IMAGE_DUCK); ) // Qolgan ma'lumotlarni qayta ishlash... sindirish; // Boshqa davlatlar ... } }

Oxir-oqibat, ushbu zaryad hujumini qo'shish uchun biz ikkita usulni o'zgartirishimiz va Heroine-ga chargeTime_ maydonini qo'shishimiz kerak edi, garchi u faqat egilgan holatda qo'llaniladi. Men ushbu kod va ma'lumotlarning barchasini bir joyda saqlashni xohlayman. Bu borada bizga “To‘rtlik to‘dasi” yordam berishi mumkin.

Shablon holati

Ob'ektga yo'naltirilgan paradigmani yaxshi biladigan odamlar uchun har bir shartli filial dinamik dispetcherlikdan foydalanish imkoniyatidir (boshqacha qilib aytganda, C++ da virtual usulni chaqirish). Menimcha, biz bu quyon teshigidan yanada chuqurroq tushishimiz kerak. Ba'zan bizga kerak bo'lgan narsa bo'lsa.

Buning tarixiy asoslari bor. Ob'ektga yo'naltirilgan paradigmaning ko'plab eski havoriylari, masalan, "To'rtlik to'dasi" va ularning Dasturlash naqshlari va Martin Fuller o'zi bilan Refaktoring Smalltalkdan kelgan. Va u erda ifThen shartni qayta ishlash uchun foydalanadigan va haqiqiy va noto'g'ri ob'ektlar uchun boshqacha tarzda amalga oshiriladigan usul.

Bizning misolimizda biz ob'ektga yo'naltirilgan narsaga e'tibor berishimiz kerak bo'lgan juda muhim nuqtaga yetib keldik. Bu bizni davlat namunasiga olib keladi. To'rtlik to'dasidan iqtibos keltirish uchun:

Ob'ektlarning ichki holatidagi o'zgarishlarga muvofiq xatti-harakatlarini o'zgartirishga imkon beradi. Bunday holda, ob'ekt boshqa sinf kabi harakat qiladi.

Bu juda aniq emas. Oxir-oqibat, switch bu bilan shug'ullanadi. Qahramon bilan bizning misolimizga nisbatan shablon quyidagicha ko'rinadi:

Status interfeysi

Birinchidan, davlat uchun interfeysni aniqlaymiz. Har bir holatga bog'liq xatti-harakat - ya'ni. switch yordamida biz ilgari amalga oshirgan barcha narsalar ushbu interfeysning virtual usuliga aylanadi. Bizning holatlarimizda bu handleInput() va update() .

sinf HeroineState (ommaviy: virtual ~HeroineState() () virtual bo'sh tutqich kiritish{} {} };

Har bir davlat uchun darslar

Har bir holat uchun biz interfeysni amalga oshiradigan sinfni aniqlaymiz. Uning usullari qahramonning bu holatdagi xatti-harakatlarini aniqlaydi. Boshqacha qilib aytganda, biz oldingi misoldagi kalitdan barcha variantlarni olamiz va ularni holat sinfiga aylantiramiz. Masalan:

sinf DuckingState: ommaviy HeroineState ( ommaviy : DuckingState() : chargeTime_(0 ) () virtual bo'sh tutqich kiritish (Qahramon va qahramon, Kirish kiritish)( agar (kirish == RELEASE_DOWN) ( // Doimiy holatga o'tish... heroine.setGraphics(IMAGE_STAND); )) virtual bo'shliqni yangilash (qahramon va qahramon)(chargeTime_++; if (chargeTime_ > MAX_CHARGE) ( heroine.superBomb(); ) ) private : int chargeTime_; );

E'tibor bering, biz chargeTime_ ni qahramonning o'z sinfidan DuckingState sinfiga o'tkazdik. Va bu juda yaxshi, chunki bu ma'lumot faqat shu holatda ma'noga ega va bizning ma'lumotlar modelimiz buni aniq ko'rsatadi.

Davlat delegatsiyasi

sinf qahramoni (ommaviy: virtual bo'shliq tutqichi kiritish (Kirish kiritish)(state_->handleInput(*bu , kiritish); ) virtual bo'shliqni yangilash()(holat_->yangilash(*bu ); ) // Boshqa usullar ... xususiy: HeroineState* state_; );

"Holatni o'zgartirish" uchun biz faqat state_ nuqtasini boshqa HeroineState obyektiga ko'rsatishimiz kerak. Davlat namunasi aslida shundan iborat.

GoF ning Strategy va Type Object shablonlariga juda o'xshaydi. Uchalasida ham bizda qulga topshiruvchi asosiy ob'ekt mavjud. Farqi shundaki maqsad.

  • Strategiyaning maqsadi ulanishning pasayishi asosiy sinf va uning xatti-harakati o'rtasidagi (ajralish).
  • Tur ob'ektining maqsadi umumiy turdagi ob'ektni o'zaro almashish orqali bir xil harakat qiladigan bir qancha ob'ektlarni yaratishdir.
  • Davlatning maqsadi asosiy ob'ektning xatti-harakatini o'zi topshiradigan ob'ektni o'zgartirish orqali o'zgartirishdir.

Bu davlat ob'ektlari qayerda?

Men sizga aytmagan narsam bor. Holatni o'zgartirish uchun biz state_ ga yangi holatga ishora qiluvchi yangi qiymat belgilashimiz kerak, lekin bu obyekt qayerdan keladi? Bizning enum misolimizda, o'ylash uchun hech narsa yo'q: enum qiymatlari raqamlar kabi ibtidoiydir. Ammo hozir bizning shtatlarimiz sinflar bilan ifodalanadi, ya'ni bizga haqiqiy misollar uchun ko'rsatgichlar kerak. Eng keng tarqalgan ikkita javob mavjud:

Statik holatlar

Agar holat ob'ektida boshqa maydonlar bo'lmasa, u saqlaydigan yagona narsa bu usullarni chaqirish uchun ichki virtual usullar jadvaliga ko'rsatgichdir. Bunday holda, sinfning bir nechta nusxasiga ega bo'lishning hojati yo'q: har bir misol hali ham bir xil bo'ladi.

Agar sizning davlatingizda maydonlar bo'lmasa va faqat bitta virtual usul bo'lsa, naqshni yanada soddalashtirishingiz mumkin. Biz har birini almashtiramiz Sinf davlat funktsiyasi davlat - muntazam yuqori darajadagi funktsiya. Va shunga mos ravishda maydon davlat_ bizning asosiy sinfimizda oddiy funktsiya ko'rsatkichiga aylanadi.

Faqat bittasi bilan o'tish juda mumkin statik nusxa ko'chirish. Agar sizda bir vaqtning o'zida bir xil holatda bo'lgan bir nechta FSMlar bo'lsa ham, ularning barchasi bir xil statik misolga ishora qilishi mumkin, chunki bunda davlat mashinasiga xos hech narsa yo'q.

Statik misolni qaerga joylashtirishingiz sizga bog'liq. To'g'ri keladigan joyni toping. Keling, misolimizni asosiy sinfga joylashtiramiz. Sababsiz.

sinf HeroineState ( ommaviy : statik StandingState turish; statik DuckingState ducking; Static JumpingState sakrash; Static DivingState sho'ng'in; // Kodning qolgan qismi... };

Ushbu statik maydonlarning har biri o'yin tomonidan ishlatiladigan holatning namunasidir. Qahramonni sakrash uchun tik turgan holat shunday qiladi:

agar (kirish == PRESS_B) ( heroine.state_ = &HeroineState::jumping; heroine.setGraphics(IMAGE_JUMP); )

Davlat holatlari

Ba'zan oldingi variant ishlamaydi. Statik holat egilgan holatga mos kelmaydi. Unda chargeTime_ maydoni bor va u cho'kkalayotgan qahramonga xosdir. Bu bizning holatlarimizda yanada yaxshi ishlaydi, chunki bizda faqat bitta qahramon bor, lekin agar biz ikkita o'yinchi uchun kooperativni qo'shmoqchi bo'lsak, bizda katta muammolar bo'ladi.

Bunday holda, biz unga o'tganimizda davlat ob'ektini yaratishimiz kerak. Bu har bir FSMga o'z davlat instansiyasiga ega bo'lish imkonini beradi. Albatta, agar biz xotirani ajratsak yangi shart, bu biz kerak degan ma'noni anglatadi ozod qilish hozirgisining band xotirasi. Biz ehtiyot bo'lishimiz kerak, chunki o'zgarishlarni keltirib chiqaradigan kod usulning joriy holatida. Biz buni o'zimiz ostidan olib tashlashni xohlamaymiz.

Buning o'rniga, HeroineState'da handleInput() ga ixtiyoriy ravishda yangi holatni qaytarishga ruxsat beramiz. Bu sodir bo'lganda, qahramon eski holatni olib tashlaydi va uni yangisiga almashtiradi, masalan:

void Heroine::handleInput(Kirish kiritish) ( HeroineState* holati = state_->handleInput(*this , input); if (holat != NULL ) (holatni oʻchirish_; state_ = state; ) )

Shunday qilib, biz o'z uslubimizdan qaytmagunimizcha oldingi holatni olib tashlamaymiz. Endi turgan holat yangi misol yaratish orqali sho'ng'in holatiga o'tishi mumkin:

HeroineState* StandingState::handleInput(Qahramon va qahramon, Kirish kiritish) ( agar (kirish == PRESS_DOWN) ( // Boshqa kod... yangi DuckingState(); ) qaytaring // Shu holatda qoling. NULL ; )

Iloji bo'lsa, men statik holatlardan foydalanishni afzal ko'raman, chunki ular har safar holat o'zgarganda ob'ektlarni ajratib, xotira va protsessor davrlarini egallamaydi. Faqatgina ko'p bo'lmagan shartlar uchun davlat- bu sizga kerak bo'lgan narsa.

Albatta, xotirani dinamik ravishda holatga ajratganingizda, mumkin bo'lgan xotira parchalanishi haqida o'ylashingiz kerak. Object Pool shabloni yordam berishi mumkin.

Kirish va chiqish bosqichlari

Davlat namunasi barcha xatti-harakatlar va tegishli ma'lumotlarni bitta sinf ichida qamrab olish uchun mo'ljallangan. Biz juda yaxshi ishlayapmiz, lekin hali ham noaniq tafsilotlar mavjud.

Qahramon o'z holatini o'zgartirganda, biz uning spritesini ham almashtiramiz. Hozirda bu kod davlatga tegishli, bilan kim u almashtiradi. Shtat sho'ng'indan tik turishga o'tganda, sho'ng'in o'z qiyofasini o'rnatadi:

HeroineState* DuckingState::handleInput(Qahramon va qahramon, Kirish kiritish) ( agar (kirish == RELEASE_DOWN) ( heroine.setGraphics(IMAGE_STAND); yangi StandingState(); ) // Boshqa kodni qaytaring... )

Biz haqiqatan ham har bir shtat o'z grafikasini boshqarishini xohlaymiz. Davlatga qo'shish orqali bunga erishishimiz mumkin kiritish harakati (kirish harakati):

sinf StandingState: ommaviy HeroineState (ommaviy: virtual bo'shliqqa kirish (qahramon va qahramon)( heroine.setGraphics(IMAGE_STAND); ) // Boshqa kod... );

Qahramonga qaytsak, biz kodni o'zgartiramiz, bu holat o'zgarishi yangi shtatning kirish harakati funktsiyasiga qo'ng'iroq bilan birga bo'lishini ta'minlash uchun:

void Heroine::handleInput(Kirish kiritish) ( HeroineState* holati = state_->handleInput(*this , input); if (holat != NULL ) (holatni o'chirish_; state_ = holat; // Yangi holatning kiritish harakatini chaqiring. state_->Enter(*bu ); ))

Bu DuckingState kodini soddalashtiradi:

HeroineState* DuckingState::handleInput(Qahramon va qahramon, Kirish kiritish) ( agar (kirish == RELEASE_DOWN) (yangi StandingState(); ) // Boshqa kodni qaytaring... )

Bularning barchasi tik turishga o'tish va tik turgan holat grafikaga g'amxo'rlik qiladi. Endi bizning shtatlarimiz chinakamiga qamrab olingan. Bunday kiritish harakatining yana bir yoqimli xususiyati shundaki, u qanday holatda bo'lishidan qat'i nazar, davlatga kirishda ishga tushiriladi. qaysi biz u yerda edik.

Aksariyat real hayot holati grafiklarida bir xil holatga bir nechta o'tishlar mavjud. Misol uchun, bizning qahramonimiz tik turgan, o'tirgan yoki sakrab turgan holda qurol otishi mumkin. Bu shuni anglatadiki, bu sodir bo'lgan joyda bizda kod takrorlanishi mumkin. Kirish harakati uni bir joyda to'plash imkonini beradi.

Siz buni analogiya orqali qilishingiz mumkin chiqish harakati (chiqish harakati). Bu shunchaki biz avval davlatga murojaat qiladigan usul bo'ladi ketish va yangi holatga o'ting.

Va biz nimaga erishdik?

Men senga FSMni sotish uchun qancha vaqt sarfladim, endi esa tagingdan gilamni tortib olmoqchiman. Hozirgacha aytganlarim haqiqat va muammoni hal qiluvchi ajoyib vosita. Ammo shunday bo'ladiki, cheklangan holat mashinalarining eng muhim afzalliklari ularning eng katta kamchiliklari hamdir.

Shtat mashinasi kodingizni juda qattiq tuzilmaga solib, uni jiddiy ravishda yechishga yordam beradi. Bizda mavjud bo'lgan narsa - qat'iy holatlar to'plami, yagona joriy holat va qattiq kodlangan o'tishlar.

Cheklangan holat mashinasi Turing to'liq emas. Avtomatlar nazariyasi to'liqlikni bir qator mavhum modellar orqali tasvirlaydi, ularning har biri avvalgisidan ancha murakkab. Turing mashinasi eng ifodali mashinalardan biridir.

"Turing tugallangan" deganda Turing mashinasini amalga oshirish uchun yetarlicha ifodali tizim (odatda dasturlash tili) tushuniladi. O'z navbatida, bu barcha Tyuring-to'liq tillar taxminan teng ifodali ekanligini anglatadi. FSM bu klubga kirish uchun etarlicha ifodali emas.

Agar siz AI o'yini kabi murakkabroq narsa uchun davlat mashinasidan foydalanishga harakat qilsangiz, darhol ushbu modelning cheklovlariga duch kelasiz. Yaxshiyamki, bizning salaflarimiz ba'zi to'siqlarni aylanib o'tishni o'rgandilar. Men ushbu bobni bir nechta ana shunday misollar bilan yakunlayman.

Raqobatbardosh davlat mashinasi

Biz qahramonimizga qurol olib yurish qobiliyatini qo'shishga qaror qildik. U hozir qurollangan bo'lsa-da, u hali ham oldin qila oladigan hamma narsani qila oladi: yugurish, sakrash, cho'zilish va hokazo. Ammo endi, bularning barchasini bajarayotib, u quroldan ham o'q uzishi mumkin.

Agar biz ushbu xatti-harakatni FSM tizimiga moslashtirmoqchi bo'lsak, biz davlatlar sonini ikki baravar oshirishimiz kerak bo'ladi. Har bir shtat uchun biz boshqasini yaratishimiz kerak, ammo qurolli qahramon uchun: tik turish, qurol bilan turish, sakrash, qurol bilan sakrash.... Xo'sh, siz g'oyani tushunasiz.

Agar siz yana bir nechta qurol qo'shsangiz, shtatlar soni kombinatsion ravishda ortadi. Va bu shunchaki bir qator davlatlar emas, balki takrorlashlar to'plami: qurolli va qurolsiz davlatlar o'q otish uchun javobgar bo'lgan kod qismidan tashqari deyarli bir xil.

Muammo shundaki, biz davlatning ikki qismini chalkashtirmoqdamiz - bu qiladi nima bo `pti qo'llarida ushlab turadi- bitta mashinada. Barcha mumkin bo'lgan kombinatsiyalarni modellashtirish uchun har biri uchun holat yaratishimiz kerak juftliklar. Yechim aniq: siz ikkita alohida davlat mashinasini yaratishingiz kerak.

Agar biz birlashmoqchi bo'lsak n harakat holatlari va m Qo'limizda ushlab turgan narsalarning holatlari bitta cheklangan holat mashinasiga - bizga kerak n×m davlatlar. Agar bizda ikkita pulemyot bo'lsa, bizga kerak bo'ladi n+m davlatlar.

Biz birinchi davlat mashinamizni harakatlar bilan o'zgarishsiz qoldiramiz. Va bunga qo'shimcha ravishda, biz qahramonning nima ushlab turganini tasvirlash uchun yana bir mashina yaratamiz. Endi Qahramonda ikkita "davlat" ma'lumotnomasi bo'ladi, har bir mashina uchun bittadan.

sinf qahramoni ( // Kodning qolgan qismi... xususiy: HeroineState* state_; HeroineState* uskunasi_; );

Misol uchun, biz ikkinchi holat mashinasi uchun Davlat naqshini to'liq amalga oshirishdan foydalanamiz, ammo amalda bu holda oddiy mantiqiy bayroq etarli bo'ladi.

Qahramon shtatlarga kirishni topshirganda, tarjimani ikkala davlat mashinasiga uzatadi:

void Heroine::handleInput(Kirish kiritish) (state_->handleInput(*bu , kiritish); equipment_->handleInput(*bu , kiritish); )

Murakkabroq tizimlar kirishning bir qismini o'zlashtira oladigan cheklangan holat mashinalarini o'z ichiga olishi mumkin, shunda boshqa mashinalar uni qabul qilmaydi. Bu bizga bir nechta mashinalar bir xil kirishga javob beradigan vaziyatni oldini olishga imkon beradi.

Har bir holat mashinasi kirishga reaksiyaga kirishishi, xatti-harakatlarni ishlab chiqishi va boshqa holat mashinalaridan mustaqil ravishda o'z holatini o'zgartirishi mumkin. Va ikkala davlat deyarli bir-biriga bog'liq bo'lmaganda, u ajoyib ishlaydi.

Amalda siz davlatlar bir-biri bilan o'zaro aloqada bo'lgan vaziyatga duch kelishingiz mumkin. Masalan, u sakrash paytida o'q otolmaydi yoki, masalan, qurollangan holda slaydni hujumini amalga oshira olmaydi. Koddagi avtomatlarning bunday xatti-harakati va muvofiqlashtirilishini ta'minlash uchun siz xuddi shu shafqatsiz kuch tekshiruviga qaytishingiz kerak bo'ladi. boshqa chekli holat mashinasi. Eng oqlangan yechim emas, lekin hech bo'lmaganda ishlaydi.

Ierarxik holat mashinasi

Qahramonning xulq-atvori yanada jonlantirilgandan so'ng, u shunga o'xshash ko'plab holatlarga ega bo'lishi mumkin. Masalan, tik turish, yurish, yugurish va qiyaliklardan pastga siljish sodir bo'lmaydi. Ushbu holatlarning har birida B tugmachasini bosish uning sakrashiga, pastga bosilishi esa cho'kib ketishiga olib keladi.

Davlat mashinasini eng oddiy amalga oshirishda biz ushbu kodni barcha shtatlar uchun takrorladik. Lekin, albatta, kodni faqat bir marta yozishimiz kerak bo'lsa va keyin uni barcha davlatlar uchun qayta ishlata olsak, yaxshi bo'lar edi.

Agar bu davlat mashinasi emas, balki faqat ob'ektga yo'naltirilgan kod bo'lsa, biz meros deb ataladigan holatlar o'rtasida kodni ajratish texnikasidan foydalanishimiz mumkin. Siz sakrash va cho'kib ketishni boshqaradigan asosiy holat uchun sinfni belgilashingiz mumkin. Tik turish, yurish, yugurish va dumalab turish unga meros bo'lib, o'zlarining qo'shimcha xatti-harakatlarini qo'shadilar.

Bu qaror ham yaxshi, ham yomon oqibatlarga olib keladi. Meros - bu kodni qayta ishlatish uchun kuchli vosita, lekin shu bilan birga u ikkita kod bo'lagi o'rtasida juda kuchli uyg'unlikni ta'minlaydi. Bolg'a aqlsiz urish uchun juda og'ir.

Ushbu shaklda hosil bo'lgan tuzilma chaqiriladi ierarxik holat mashinasi(yoki ierarxik avtomat). Va har bir shart o'ziga xos bo'lishi mumkin super davlat(davlatning o'zi deyiladi substate). Voqea sodir bo'lganda va substate uni qayta ishlamasa, u super holatlar zanjiriga o'tadi. Boshqacha qilib aytganda, bu irsiy usulni bekor qilish kabi ko'rinadi.

Haqiqatan ham, agar biz FSMni amalga oshirish uchun asl Davlat namunasidan foydalansak, ierarxiyani amalga oshirish uchun sinf merosidan foydalanishimiz mumkin. Keling, superklass uchun asosiy sinfni aniqlaymiz:

OnGroundState sinfi: ommaviy HeroineState (ommaviy: virtual bo'sh tutqich kiritish (Qahramon va qahramon, Kirish kiritish)( agar (kirish == PRESS_B) ( // O'tish... ) else if (kirish == PRESS_DOWN) ( // Squat... ) ) );

Va endi har bir kichik sinf uni meros qilib oladi:

sinf DuckingState: ommaviy OnGroundState ( ommaviy : virtual bo'sh tutqich kiritish (Qahramon va qahramon, Kirish kiritish)( agar (kirish == RELEASE_DOWN) ( // Tur... ) boshqa ( // Kirish qayta ishlanmadi. Shuning uchun biz uni ierarxiyada yuqoriga o'tamiz. OnGroundState::handleInput(qahramon, kiritish); )))

Albatta, bu ierarxiyani amalga oshirishning yagona yo'li emas. Ammo, agar siz to'rtta davlat to'dasi shablonidan foydalanmasangiz, u ishlamaydi. Buning o'rniga siz hozirgi holatlar va superstatelarning aniq ierarxiyasini modellashtirishingiz mumkin stack asosiy sinfdagi yagona davlat o'rniga davlatlar.

Joriy holat stekning yuqori qismida bo'ladi, uning ostida uning o'ta holati, keyin esa super holat bo'ladi. bu super davlatlar va boshqalar. Va davlatga xos xatti-harakatni amalga oshirish kerak bo'lganda, siz stackning yuqorisidan boshlaysiz va shtat buni hal qilguncha pastga tushasiz. (Agar u uni qayta ishlamasa, siz shunchaki e'tiborsiz qoldirasiz).

Jurnal xotirasi bo'lgan avtomatik mashina

Davlat stackidan foydalanadigan davlat mashinalari uchun yana bir keng tarqalgan kengaytma mavjud. Faqat bu erda stek butunlay boshqa tushunchani ifodalaydi va turli muammolarni hal qilish uchun ishlatiladi.

Muammo shundaki, davlat mashinasida tushuncha yo'q hikoyalar. Siz qanday holatda ekanligingizni bilasizmi? Siz, lekin siz qaysi holatda ekanligingiz haqida hech qanday ma'lumotga ega emassiz edi. Va shunga ko'ra, avvalgi holatga qaytishning oson yo'li yo'q.

Mana oddiy misol: Ilgari biz qo'rqmas qahramonimizga tishlarigacha qurollanishiga ruxsat bergan edik. U qurolini otganida, bizga otishma animatsiyasini o'ynash, o'qni yaratish va unga hamroh bo'lgan vizual effektlar uchun yangi holat kerak. Buning uchun biz yangi FiringState-ni yaratamiz va qahramon o't o'chirish tugmachasini bosish orqali otish mumkin bo'lgan barcha shtatlardan unga o'tishni amalga oshiramiz.

Ushbu xatti-harakat bir nechta holatlar o'rtasida takrorlanganligi sababli, kodni qayta ishlatish uchun ierarxik holat mashinasidan foydalanish mumkin.

Bu erda qiyinchilik shundaki, siz qaysi davlatga borishingiz kerakligini qandaydir tarzda tushunishingiz kerak. keyin tortishish. Qahramon tik turganida, yugurganda, sakrab turganida yoki cho'kkalab turganida butun klipni yoqishi mumkin. Rasmga tushirish ketma-ketligi tugagach, u tortishishdan oldingi holatiga qaytishi kerak.

Agar biz sof FSMga bog'lansak, biz qanday holatda bo'lganimizni darhol unutamiz. Buni kuzatib borish uchun biz deyarli bir xil holatlarni aniqlashimiz kerak - tik turgan otish, yugurish otish, sakrab otish va hokazo. Shunday qilib, bizda qattiq kodlangan o'tishlar mavjud, ular tugagandan so'ng to'g'ri holatga o'tadi.

Bizga haqiqatan ham kerak bo'lgan narsa - tortishishdan oldin bo'lgan holatimizni saqlash va tortishishdan keyin uni yana eslab qolish qobiliyati. Bu erda yana avtomatlar nazariyasi bizga yordam berishi mumkin. Tegishli ma'lumotlar strukturasi Pushdown Automaton deb ataladi.

Cheklangan avtomatda holatni ko'rsatadigan bitta ko'rsatkich mavjud bo'lsa, do'kon xotirasi bo'lgan avtomatda ularning stack. FSMda yangi holatga o'tish oldingi holatni almashtiradi. Jurnal xotirasi bo'lgan mashina ham buni amalga oshirishga imkon beradi, lekin yana ikkita operatsiyani qo'shadi:

    Siz .. qila olasiz; siz ... mumkin joy (Durang) stekdagi yangi holat. Joriy holat har doim stekning yuqori qismida bo'ladi, shuning uchun bu yangi holatga o'tish operatsiyasi. Ammo shu bilan birga, eski holat to'g'ridan-to'g'ri stekdagi hozirgi holatdan pastda qoladi va izsiz yo'qolmaydi.

    Siz .. qila olasiz; siz ... mumkin ekstrakti (pop) stekdan yuqori holat. Davlat yo'qoladi va uning ostida bo'lgan narsa joriy bo'ladi.

Rasmga tushirish uchun bizga kerak bo'lgan narsa shu. Biz yaratamiz yagona narsa tortishish holati. Boshqa holatda bo'lganimizda olov tugmasini bosganimizda, biz joy (Durang) stack tortishish holati. Rasmga tushirish animatsiyasi tugagach, biz ekstrakti (pop) holati va jurnal xotirasi bo'lgan mashina bizni avtomatik ravishda oldingi holatga qaytaradi.

Ular haqiqatan ham qanchalik foydali?

Davlat mashinalarining bunday kengayishi bilan ham, ularning imkoniyatlari hali ham juda cheklangan. Bugungi kunda sun'iy intellektda shunga o'xshash narsalardan foydalanish ustunlik qilmoqda xulq-atvor daraxtlari(xulq-atvor daraxtlari) va rejalashtirish tizimlari(rejalashtirish tizimlari). Va agar siz sun'iy intellekt sohasiga alohida qiziqsangiz, ushbu bobning to'liq qismi ishtahangizni ochishi kerak. Uni qondirish uchun siz boshqa kitoblarga murojaat qilishingiz kerak bo'ladi.

Bu umuman cheklangan holat mashinalari, jurnal xotirasi bo'lgan mashinalar va boshqa shunga o'xshash tizimlar mutlaqo yaroqsiz degani emas. Ba'zi narsalar uchun bu yaxshi modellashtirish vositalari. Davlat mashinalari quyidagi hollarda foydalidir:

  • Sizning xatti-harakati ichki holatiga qarab o'zgarib turadigan ob'ektingiz bor.
  • Bu holat qat'iy ravishda nisbatan kam sonli aniq variantlarga bo'linadi.
  • Korxona doimiy ravishda bir qator kiritish buyruqlari yoki hodisalariga javob beradi.

O'yinlarda davlat mashinalari odatda AIni modellashtirish uchun ishlatiladi, lekin ular foydalanuvchi kiritish, menyu navigatsiyasi, matnni tahlil qilish, tarmoq protokollari va boshqa asinxron xatti-harakatlarni amalga oshirish uchun ham ishlatilishi mumkin.

Davlat ob'ektning holati o'zgarganda uning harakatini dinamik ravishda o'zgartirishga imkon beruvchi xatti-harakatlar modelidir.

Davlatga xos xatti-harakatlar alohida sinflarga o'tadi. Asl sinf ushbu holat ob'ektlaridan biriga havolani saqlaydi va delegatlar unga ishlaydi.

Java-da naqshning xususiyatlari

Murakkablik:

Mashhurlik:

Qo'llanilishi: Shtat namunasi ko'pincha Java-da switch iboralarida qurilgan noqulay holat mashinalarini ob'ektlarga aylantirish uchun ishlatiladi.

Standart Java kutubxonalarida davlat misollari:

  • javax.faces.lifecycle.LifeCycle#execute() (FacesServlet tomonidan boshqariladi: xatti-harakatlar joriy JSF bosqichiga bog'liq)

Naqshdan foydalanish belgilari: Sinf usullari ishni bitta o'rnatilgan ob'ektga topshiradi.

Audio pleer

Asosiy o'yinchi klassi o'yinchi qanday holatda ekanligiga qarab o'z xatti-harakatlarini o'zgartiradi.

davlatlar

states/State.java: Umumiy holat interfeysi

paket site.state.example..state.example.ui.Player; /** * Barcha shtatlar uchun umumiy interfeys. */ ommaviy mavhum sinf Davlat ( Player player; /** * Kontekst oʻzini shtat konstruktoriga oʻtadi, shunda shtat kelajakda * oʻz maʼlumotlari va usullariga kerak boʻlganda kirishi mumkin. */ State(Player player) ( this.player = player ; public abtract String onLock();

states/LockedState.java:"qulflangan" holati

paketi site.state.example..state.example.ui.Player; /** * Konkret holatlar mavhum holat usullarini o'ziga xos tarzda amalga oshiradi. */ umumiy klassi LockedState holatini kengaytiradi ( LockedState(Player player) ( super(player); player.setPlaying(false); ) @Override public String onLock() ( if (player.isPlaying()) ( player.changeState(yangi ReadyState) (o'yinchi)); "O'yinni to'xtating"; ) else ( "Qulflangan..."; Umumiy satrni bekor qilish onNext() ( "Qulflangan..."ni qaytarish; ) @Override public String onPrevious() ( "Qulflangan..."ni qaytarish; ) )

states/ReadyState.java: Tayyor holat

paketi site.state.example..state.example.ui.Player; /** * Ular kontekstni boshqa holatlarga ham o'tkazishlari mumkin. */ umumiy sinf ReadyState holatini kengaytiradi ( umumiy ReadyState(Player o'yinchisi) ( super(o'yinchi); ) @Override public String onLock() ( player.changeState(yangi LockedState(player)); "Locked..." qaytaring; ) @ Ommaviy String onPlay()ni bekor qilish ( String action = player.startPlayback(); player.changeState(new PlayingState(player)); qaytarish amali; ) @Override public String onNext() ( return “Locked...”; ) @Override public String onPrevious() ( "Qulflangan..."ni qaytaring; ) )

states/PlayingState.java:"O'yin" holati

paketi site.state.example..state.example.ui.Player; umumiy sinf PlayingState holatini kengaytiradi ( PlayingState(Player player) ( super(o'yinchi); ) @Override public String onLock() ( player.changeState(yangi LockedState(player)); player.setCurrentTrackAfterStop(); "O'ynashni to'xtatish"ni qaytaring; ) @Override public String onPlay() ( player.changeState(new ReadyState(player)); return "Pauzed..."; ) @Override public String onNext() ( return player.nextTrack(); ) @Override public String onPrevious( ) ( player.previousTrack(); ) ) qaytaring

ui

ui/Player.java: O'yinchi

paket sayt.holat.misol..holat.misol.shtatlar..state.misol.shtatlar.State; import java.util.ArrayList; import java.util.List; ommaviy sinf o'yinchisi (xususiy davlat davlati; xususiy mantiqiy o'ynash = noto'g'ri; shaxsiy ro'yxat pleylist = yangi ArrayList<>(); xususiy int currentTrack = 0; public Player() ( this.state = new ReadyState(bu); 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; ) "Playing" ni qaytarish + playlist.get(currentTrack); ) public String previousTrack() (CurrentTrack--; if (currentTrack< 0) { currentTrack = playlist.size() - 1; } return "Playing " + playlist.get(currentTrack); } public void setCurrentTrackAfterStop() { this.currentTrack = 0; } }

ui/UI.java: O'yinchi GUI

paket site.state.example.ui; import javax.swing.*; import java.awt.*; umumiy sinf foydalanuvchi interfeysi ( xususiy o‘yinchi o‘yinchisi; xususiy statik JTextField textField = yangi JTextField textField = yangi JTextField(); umumiy UI(Player pleer) ( this.player = player; ) public void init() ( JFrame ramkasi = yangi JFrame("Test pleer"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); (textField); // Kontekst foydalanuvchi kiritishiga javob beradi. "Play" play.addActionListener(e -> textField.setText(player.getState().onPlay())); (e -> textField.setText(player.getState().onLock())); = new JButton("Keyingi") next.addActionListener(e -> textField.setText(player.getState().onNext( ))); JButton oldingi = yangi JButton("Oldingi"); prev.addActionListener(e -> textField.setText(player.getState().onPrevious())); frame.setVisible(rost); frame.setSize(300, 100); buttons.add(play); tugmalar.qo‘shish(to‘xtatish); buttons.add(keyingi); buttons.add(oldingi); ))

Demo.java: Mijoz kodi

paket refactoring_guru.state..state.example.ui..state.example.ui.UI; /** * Namoyish sinfi. Bu erda hammasi birlashadi. */ ommaviy klass Demo ( umumiy statik bekor asosiy(String args) ( Oʻyinchi oʻyinchisi = yangi oʻyinchi(); UI ui = yangi UI(pleer); ui.init(); ) )

15.02.2016
21:30

Davlat namunasi bir nechta mustaqil mantiqiy holatlarga ega bo'lgan sinflarni loyihalash uchun mo'ljallangan. Keling, to'g'ridan-to'g'ri misolga o'taylik.

Aytaylik, biz veb-kamerani boshqarish sinfini ishlab chiqmoqdamiz. Kamera uchta holatda bo'lishi mumkin:

  1. Boshlanmagan. Keling, uni NotConnectedState deb ataymiz;
  2. Ishga tushirildi va ishlashga tayyor, lekin hozircha hech qanday kadr olinmayapti. Bu ReadyState bo'lsin;
  3. Faol kadrlarni suratga olish rejimi. ActiveState ni belgilaymiz.

Biz Davlat namunasi bilan ishlayotganimiz sababli, Davlat diagrammasi tasviridan boshlash yaxshidir:

Endi bu diagrammani kodga aylantiramiz. Amalga oshirishni murakkablashtirmaslik uchun biz veb-kameralar bilan ishlash kodini o'tkazib yuboramiz. Agar kerak bo'lsa, siz o'zingiz tegishli kutubxona funksiyasi qo'ng'iroqlarini qo'shishingiz mumkin.

Men darhol to'liq ro'yxatni minimal izohlar bilan taqdim etaman. Keyinchalik biz ushbu amalga oshirishning asosiy tafsilotlarini batafsilroq muhokama qilamiz.

#o'z ichiga oladi #define DECLARE_GET_INSTANCE(ClassName) \ static ClassName* getInstance() (\ static ClassName instance;\ return \ ) class WebCamera ( public: typedef std::string Frame; public: // *********** *************************************** // Istisnolar // ****** ********************************************* sinf Qo'llab-quvvatlanmaydi: ommaviy std: :istisno ( ); // ************************************* ******* ********* // Shtatlar // **************************** ******* ************** sinfi NotConnectedState sinfi Davlat ( public: virtual ~State() ( ) virtual void connect(WebCamera*) ( notSupported(); ) virtual bo'shliqni o'chirish (WebCamera* cam) ( std::cout<< "Деинициализируем камеру..." << std::endl; // ... cam->changeState(NotConnectedState::getInstance()); ) virtual bo'shliqni ishga tushirish(WebCamera*) ( NotSupported(); ) virtual bo'shliqni to'xtatish(WebCamera*) ( NotSupported(); ) virtual Frame getFrame(WebCamera*) ( NotSupported(); ) himoyalangan: State() ( ) ); // **************************************************** ** NotConnectedState sinfi: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(NotConnectedState) bekor ulanish (WebCamera* cam) ( std::cout)<< "Инициализируем камеру..." << std::endl; // ... cam->changeState(ReadyState::getInstance()); ) void disconnect(WebCamera*) ( NotSupported(); ) xususiy: NotConnectedState() ( ) ); // **************************************************** ** ReadyState sinfi: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(ReadyState) ishga tushirilmaydi (WebCamera* kamera) ( std::cout)<< "Запускаем видео-поток..." << std::endl; // ... cam->changeState(ActiveState::getInstance()); ) xususiy: ReadyState() ( ) ); // **************************************************** ** ActiveState sinfi: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(ActiveState) bekor to‘xtash (WebCamera* cam) ( std::cout)<< "Останавливаем видео-поток..." << std::endl; // ... cam-> << "Получаем текущий кадр..." << std::endl; // ... return "Current frame"; } private: ActiveState() { } }; public: explicit WebCamera(int camID) : m_camID(camID), m_state(NotConnectedState::getInstance()) { } ~WebCamera() { try { disconnect(); } catch(const NotSupported& e) { // Обрабатываем исключение } catch(...) { // Обрабатываем исключение } } void connect() { m_state->ulanish (bu); ) void disconnect() ( m_state->disconnect(this); ) void start() ( m_state->start(this); ) void stop() ( m_state->stop(bu); ) Frame getFrame() ( m_state-ni qaytarish ->getFrame(this); void changeState(State* newState) ( m_state = newState; ) private: int m_camID; Davlat* m_state; );

E'tiboringizni DECLARE_GET_INSTANCE makrosiga qarataman. Albatta, C++ da makroslardan foydalanish tavsiya etilmaydi. Biroq, bu so'l shablon funktsiyasining analogi sifatida ishlaydigan holatlarga tegishli. Bunday holda, har doim ikkinchisiga ustunlik bering.

Bizning holatda, so'l amalga oshirish uchun zarur bo'lgan statik funktsiyani aniqlash uchun mo'ljallangan. Shuning uchun uni ishlatish asosli deb hisoblash mumkin. Axir, bu kodning takrorlanishini kamaytirishga imkon beradi va hech qanday jiddiy xavf tug'dirmaydi.

Biz asosiy sinfda Davlat sinflarini e'lon qilamiz - WebCamera. Qisqartirish uchun men barcha sinflarning a'zo funktsiyalarining inline ta'riflaridan foydalandim. Biroq, haqiqiy ilovalarda deklaratsiya va amalga oshirishni h va cpp fayllariga ajratish bo'yicha tavsiyalarga amal qilish yaxshiroqdir.

Davlat sinflari WebCamera ichida e'lon qilinadi, shunda ular ushbu sinfning shaxsiy maydonlariga kirishlari mumkin. Albatta, bu barcha sinflar o'rtasida juda qattiq aloqani yaratadi. Ammo davlatlar shu qadar o'ziga xos bo'lib chiqdiki, ularni boshqa kontekstlarda qayta ishlatish mumkin emas.

Shtat sinflari ierarxiyasining asosi mavhum WebCamera::State sinfidir:

Sinf holati ( ommaviy: virtual ~State() ( ) virtual bo‘shliqqa ulanish(WebCamera*) ( NotSupported(); ) virtual bo‘shliqni uzish(WebCamera* cam) ( std::cout<< "Деинициализируем камеру..." << std::endl; // ... cam->changeState(NotConnectedState::getInstance()); ) virtual bo'shliqni ishga tushirish(WebCamera*) ( NotSupported(); ) virtual bo'shliqni to'xtatish(WebCamera*) ( NotSupported(); ) virtual Frame getFrame(WebCamera*) ( NotSupported(); ) himoyalangan: State() ( ) );

Uning barcha a'zo funktsiyalari WebCamera sinfining funktsiyalariga mos keladi. To'g'ridan-to'g'ri delegatsiya sodir bo'ladi:

WebCamera sinfi ( // ... void connect() ( m_state->connect(this); ) void disconnect() ( m_state->disconnect(this); ) void start() ( m_state->start(this); ) void stop() ( m_state->stop(this); ) Frame getFrame() ( return m_state->getFrame(this); ) // ... Holat* m_state )

Asosiy xususiyat shundaki, Davlat ob'ekti uni chaqiradigan WebCamera misoliga ko'rsatgichni qabul qiladi. Bu o'zboshimchalik bilan ko'p sonli kameralar uchun faqat uchta Davlat ob'ektiga ega bo'lish imkonini beradi. Bu imkoniyatga Singleton naqshidan foydalanish orqali erishiladi. Albatta, misol kontekstida siz bundan sezilarli foyda olmaysiz. Ammo bu texnikani bilish hali ham foydalidir.

O'z-o'zidan, WebCamera klassi deyarli hech narsa qilmaydi. U butunlay o'z davlatlariga bog'liq. Va bu davlatlar, o'z navbatida, operatsiyalarni bajarish shartlarini belgilaydilar va zarur kontekstni ta'minlaydilar.

Aksariyat WebCamera::State a'zosi funktsiyalari o'z veb-kameramizni tashlaydi::NotSupported. Bu butunlay mos standart xatti-harakatlar. Misol uchun, agar kimdir kamera allaqachon ishga tushirilganda uni ishga tushirishga harakat qilsa, ular tabiiy ravishda istisnoga duch kelishadi.

Shu bilan birga, biz WebCamera::State::disconnect() uchun standart dasturni taqdim etamiz. Bu xatti-harakatlar uchta holatdan ikkitasiga mos keladi. Natijada biz kodning takrorlanishining oldini olamiz.

Holatni oʻzgartirish uchun WebCamera::changeState() shaxsiy aʼzo funksiyasidan foydalaning:

O'zgartirishni bekor qilingState(State* newState) ( m_state = newState; )

Endi aniq davlatlarni amalga oshirish. WebCamera::NotConnectedState uchun connect() va disconnect() amallarini bekor qilish kifoya:

NotConnectedState sinfi: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(NotConnectedState) void ulanish (WebCamera* cam) ( std::cout)<< "Инициализируем камеру..." << std::endl; // ... cam->changeState(ReadyState::getInstance()); ) void disconnect(WebCamera*) ( NotSupported(); ) xususiy: NotConnectedState() ( ) );

Har bir shtat uchun siz bitta misol yaratishingiz mumkin. Bu bizga xususiy konstruktorni e'lon qilish orqali kafolatlanadi.

Taqdim etilgan amalga oshirishning yana bir muhim elementi shundaki, biz muvaffaqiyatli bo'lgan taqdirdagina yangi davlatga o'tamiz. Misol uchun, agar kamerani ishga tushirish paytida xatolik yuzaga kelsa, ReadyState-ga kirishga hali erta. Asosiy g'oya - kameraning haqiqiy holati (bizning holatlarimizda) va Davlat ob'ekti o'rtasidagi to'liq muvofiqlik.

Shunday qilib, kamera ishlashga tayyor. Keling, tegishli WebCamera::ReadyState State sinfini yaratamiz:

Sinf ReadyState: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(ReadyState) bekor ishga tushirish (WebCamera* cam) ( std::cout)<< "Запускаем видео-поток..." << std::endl; // ... cam->changeState(ActiveState::getInstance()); ) xususiy: ReadyState() ( ) );

Tayyor holatdan biz faol Frame Capture holatiga kirishimiz mumkin. Shu maqsadda biz amalga oshirgan start() operatsiyasi taqdim etilgan.

Nihoyat biz kameraning oxirgi mantiqiy holatiga yetdik, WebCamera::ActiveState:

ActiveState sinfi: umumiy holat (ommaviy: DECLARE_GET_INSTANCE(ActiveState) bekor to‘xtash (WebCamera* cam) ( std::cout)<< "Останавливаем видео-поток..." << std::endl; // ... cam->changeState(ReadyState::getInstance()); ) Frame getFrame(WebCamera*) ( std::cout<< "Получаем текущий кадр..." << std::endl; // ... return "Current frame"; } private: ActiveState() { } };

Bu holatda siz stop() yordamida kadrlarni suratga olishni to'xtatishingiz mumkin. Natijada biz WebCamera::ReadyState-ga qaytamiz. Bundan tashqari, biz kamera buferida to'plangan ramkalarni qabul qilishimiz mumkin. Oddiylik uchun "ramka" deganda biz oddiy qatorni nazarda tutamiz. Aslida, bu qandaydir bayt massivi bo'ladi.

Endi biz WebCamera sinfimiz bilan ishlashning odatiy misolini yozishimiz mumkin:

Int main() ( WebCamera cam(0); harakat qilib ko‘ring ( // Cam, NotConnectedState cam.connect(); // Cam, ReadyState cam.start(); // Cam ActiveState std::cout da<< cam.getFrame() << std::endl; cam.stop(); // Можно было сразу вызвать disconnect() // cam в Состоянии ReadyState cam.disconnect(); // cam в Состоянии NotConnectedState } catch(const WebCamera::NotSupported& e) { // Обрабатываем исключение } catch(...) { // Обрабатываем исключение } return 0; }

Natijada konsolga chiqadigan narsa:

Kamerani ishga tushiring... Video oqimini boshlang... Joriy kadrni oling... Joriy kadr Video oqimini to‘xtating... Kamerani ishga tushiring...

Endi xatoni qo'zg'atishga harakat qilaylik. Keling, connect() ni ketma-ket ikki marta chaqiramiz:

Int main() ( WebCamera cam(0); sinab ko'ring ( // Cam Cam. NotConnectedState cam.connect(); // Cam ReadyState da // Lekin bu holat uchun connect() operatsiyasi ta'minlanmagan! cam.connect( ); // Istisnoni qo'llab-quvvatlamaydi ) catch(const WebCamera::NotSupported& e) ( std::cout<< "Произошло исключение!!!" << std::endl; // ... } catch(...) { // Обрабатываем исключение } return 0; }

Undan nima chiqadi:

Kamera ishga tushirilmoqda... Istisno yuz berdi!!! Kamerani noaniqlashtiramiz...

Esda tutingki, kamera hali ham ishga tushirilgan. Disconnect() chaqiruvi WebCamera destruktorida sodir bo'ldi. Bular. ob'ektning ichki holati mutlaqo to'g'ri bo'lib qoladi.

xulosalar

Davlat naqshidan foydalanib, siz Davlat diagrammasini kodga noyob tarzda aylantirishingiz mumkin. Bir qarashda, amalga oshirish batafsil bo'lib chiqdi. Biroq, biz asosiy sinf WebCamera bilan ishlash uchun mumkin bo'lgan kontekstlarga aniq bo'linishga keldik. Natijada, har bir alohida shtatni yozishda biz diqqatimizni tor vazifaga qaratishga muvaffaq bo'ldik. Va bu aniq, tushunarli va ishonchli kod yozishning eng yaxshi usuli.

Shakllardan to'g'ri foydalanish uchun Davlat Va Strategiya Java ilovalarining asosiy qismida Java dasturchilari ular orasidagi farqni aniq tushunishlari muhimdir. Garchi ikkala naqsh, Davlat va Strategiya, o'xshash tuzilishga ega bo'lsa-da va ikkalasi ham ochiq/yopiq printsipga asoslangan bo'lib, SOLID tamoyillarida "O" ni ifodalaydi, lekin ular butunlay boshqacha. niyatlar. Naqsh Strategiya Java tilida kapsülleme uchun ishlatiladi mijoz uchun bajarilish moslashuvchanligini ta'minlash uchun tegishli algoritmlar to'plami. Mijoz Strategy ob'ektidan foydalanadigan sinf kontekstini o'zgartirmasdan ish vaqtida istalgan algoritmni tanlashi mumkin. Ba'zi mashhur naqsh namunalari Strategiya shifrlash, siqish yoki saralash kabi algoritmlardan foydalanadigan kod yozmoqda. Boshqa tomondan, Davlat namunasi ob'ektga turli holatlarda turlicha harakat qilish imkonini beradi. Chunki real dunyoda ob'ekt ko'pincha holatlarga ega va u turli shtatlarda o'zini boshqacha tutadi, masalan, savdo avtomati faqat hasCoin holatida bo'lsa, tovarlarni sotadi, unga tanga qo'ymaguningizcha sotilmaydi. Endi siz Strategiya va Davlat naqshlari o'rtasidagi farqni aniq ko'rishingiz mumkin, bular turli xil niyatlar. Davlat namunasi ob'ektga holatni boshqarishga yordam beradi, Strategiya namunasi esa mijozga boshqa xatti-harakatlarni tanlash imkonini beradi. Ko'rish oson bo'lmagan yana bir farq - bu xulq-atvorning o'zgarishiga kim sabab bo'layotgani. Strategiya namunasi bo'lsa, bu Davlat namunasida kontekstga turli strategiyalarni taqdim etadigan mijozdir, o'tish kontekst yoki ob'ektning o'zi tomonidan boshqariladi. Bundan tashqari, agar siz Davlat ob'ektidagi holat o'zgarishlarini o'zingiz boshqarsangiz, kontekstga havola bo'lishi kerak, masalan, savdo mashinasi kontekstning joriy holatini o'zgartirish uchun setState() usulini chaqira olishi kerak. Boshqa tomondan, Strategiya ob'ekti hech qachon kontekstga havolani o'z ichiga olmaydi; Davlat va Strategiya naqshlari o'rtasidagi farq Java naqshlari haqidagi mashhur intervyu savollaridan biridir, Java naqshlari haqidagi ushbu maqolada biz uni batafsil ko'rib chiqamiz. Biz Java-dagi Strategiya va Davlat naqshlari o'rtasidagi o'xshashlik va farqlarni ko'rib chiqamiz, bu sizga ushbu naqshlarni tushunishingizni yaxshilashga yordam beradi.

Davlat va Strategiya naqshlari o'rtasidagi o'xshashliklar

Agar siz Davlat va Strategiya naqshlarining UML diagrammasiga qarasangiz, ikkalasi ham bir-biriga o'xshashligini sezasiz. O'z xatti-harakatlarini o'zgartirish uchun Davlatdan foydalanadigan ob'ekt Kontekst ob'ekti sifatida tanilgan, xuddi shunday o'z xatti-harakatlarini o'zgartirish uchun Strategiyadan foydalanadigan ob'ekt Kontekst ob'ekti deb ataladi. Mijoz kontekst ob'ekti bilan o'zaro aloqada ekanligini unutmang. Davlat namunasi holatida, kontekst joriy ob'ekt sifatida saqlanadigan Davlat ob'ektiga usullarni chaqiradi va Strategiya namunasi holatida, kontekst Strategiya ob'ektini parametr sifatida ishlatadi yoki yaratish jarayonida taqdim etiladi. ob'ekt kontekstidan. Java-da davlat naqshining UML diagrammasi
Davlat namunasi uchun ushbu UML diagrammasi Java-da ob'ektga yo'naltirilgan savdo avtomati dizaynini yaratishning klassik muammosini tasvirlaydi. Savdo avtomatining holati interfeys yordamida taqdim etilishini ko'rishingiz mumkin, keyin esa o'ziga xos holatni ifodalash uchun dastur mavjud. Har bir davlat kontekstda chaqirilgan harakatlar natijasida boshqa holatga o'tish uchun ob'ekt kontekstiga havolalarga ham ega.
Strategiya namunasi uchun ushbu UML diagrammasi turli xil funktsional ilovalarni o'z ichiga oladi. Saralash algoritmlari ko'p bo'lganligi sababli, ushbu dizayn namunasi mijozga ob'ektlarni saralashda algoritm tanlash imkonini beradi. Aslida Java to'plami ramka Java-da obyektlarni saralash uchun foydalaniladigan Collections.sort() usulini amalga oshirish orqali ushbu naqshdan foydalanadi. Yagona farq shundaki, mijozga tartiblash algoritmini tanlashga ruxsat berish o'rniga, u Java-ga Comparator yoki Comparable interfeysi namunasini o'tkazish orqali taqqoslash strategiyasini belgilash imkonini beradi. Keling, Java-dagi ushbu ikkita asosiy dizayn naqshlari o'rtasidagi bir nechta o'xshashliklarni ko'rib chiqaylik:
  1. Har ikkala naqsh, Davlat va Strategiya, ulardan foydalanadigan ob'ekt kontekstiga ta'sir qilmasdan yangi holat va strategiyani qo'shishni osonlashtiradi.

  2. Ikkalasi ham kodingizni ochiq/yopiq printsipga muvofiq saqlaydi, ya'ni dizayn kengaytmalar uchun ochiq, lekin o'zgartirish uchun yopiq bo'ladi. Davlat va Strategiya naqshlari holatida ob'ekt konteksti o'zgartirishlar, yangi shtatlar yoki yangi strategiyalarni joriy qilish uchun yopiq bo'ladi yoki boshqa davlat kontekstini yoki minimal o'zgarishlarni o'zgartirishingiz shart emas.

  3. Ob'ekt konteksti Davlat naqshidagi ob'ektni ishga tushirish holatidan boshlangani kabi, ob'ekt konteksti ham Java-dagi Strategiya naqshida standart strategiyaga ega.

  4. Davlat namunasi turli ob'ekt holatlari ko'rinishidagi turli xatti-harakatlarni ifodalaydi, Strategiya namunasi esa turli xil ob'ekt strategiyalari ko'rinishidagi turli xatti-harakatlarni ifodalaydi.

  5. Har ikkala naqsh, Strategiya va Davlat, xatti-harakatni amalga oshirishning kichik sinflariga bog'liq. Har bir konkret strategiya mavhum strategiyani kengaytiradi.

Java-da Strategiya va Davlat naqshlari o'rtasidagi farqlar

Shunday qilib, endi biz bilamizki, Davlat va Strategiya naqshlari tuzilishi jihatidan o'xshash, ammo ularning maqsadi boshqacha. Keling, ushbu dizayn naqshlari orasidagi ba'zi asosiy farqlarni ko'rib chiqaylik.
  1. Strategiya namunasi o'zaro bog'liq algoritmlar to'plamini qamrab oladi va mijozga ish vaqtida tarkib va ​​delegatsiyaga qaramay, bir-birini almashtiradigan xatti-harakatlardan foydalanishga imkon beradi, boshqa tomondan, Davlat namunasi sinfga turli shtatlarda turli xatti-harakatlarni namoyish etishga yordam beradi.

  2. Davlat va Strategiya naqshlari o'rtasidagi keyingi farq shundaki, Shtat ob'ekt holatini qamrab oladi, Strategiya namunasi esa algoritm yoki strategiyani qamrab oladi. Holat ob'ekt bilan bog'langanligi sababli uni qayta ishlatib bo'lmaydi, lekin strategiya yoki algoritmni kontekstidan ajratish orqali biz uni qayta ishlatishimiz mumkin.

  3. Davlat modelida shaxsiy holat davlatlar o'rtasidagi o'tishni amalga oshirish uchun kontekstga havolani o'z ichiga olishi mumkin, ammo Strategiyada u ishlatiladigan kontekstga havola mavjud emas.

  4. Strategiyani amalga oshirish uni ishlatadigan ob'ektga parametr sifatida o'tkazilishi mumkin, masalan Collection.sort() strategiya bo'lgan Comparatorni oladi. Boshqa tomondan, holat ob'ekt kontekstining bir qismidir va vaqt o'tishi bilan ob'ekt konteksti bir holatdan ikkinchi holatga o'tadi.

  5. Strategiya ham, Davlat ham ochiq/yopiq tamoyilga amal qilsa-da, Strategiya ham Yagona javobgarlik tamoyiliga amal qiladi, chunki har bir strategiya individual algoritmni o'z ichiga oladi, turli strategiyalar bir-biridan mustaqildir. Bir strategiyani o'zgartirish boshqa strategiyani o'zgartirishni talab qilmaydi.

  6. Strategiya va Davlat naqshlari o'rtasidagi yana bir nazariy farq shundaki, yaratuvchi ob'ektning "Qanday" qismini belgilaydi, masalan, saralash ob'ekti ma'lumotlarni saralaydi, boshqa tomondan, Davlat namunasi "nima" va "qachon" ni belgilaydi. ob'ektning qismlari, masalan, ob'ekt ma'lum bir holatda bo'lganda nima qilishi mumkin.

  7. Shtatga o'tish tartibi Davlat namunasida aniq belgilangan. Mijoz o'zi tanlagan Strategiyaning har qanday amalga oshirilishini tanlashda erkindir.

  8. Strategiya naqshining umumiy misollaridan ba'zilari tartiblash algoritmlari, shifrlash algoritmlari yoki siqish algoritmi kabi algoritmlarni inkapsulyatsiya qilishdir. Agar kodingiz turli xil turdagi algoritmlardan foydalanishi kerakligini ko'rsangiz, Strategiya naqshidan foydalanishni o'ylab ko'rishingiz kerak. Boshqa tomondan, Davlat namunasidan foydalanishni tan olish juda oson, agar siz holat va holat oʻtishlarini juda koʻp oʻrnatilgan shartli iboralarsiz boshqarishingiz kerak boʻlsa, Davlat namunasi foydalanish uchun toʻgʻri namunadir.

  9. Davlat va Strategiya naqshlari o'rtasidagi oxirgi, lekin eng muhim farqlardan biri shundaki, Strategiyaga o'zgartirish Mijoz tomonidan amalga oshiriladi, Holbuki holatga o'zgartirish esa kontekst yoki ob'ekt holatining o'zi tomonidan amalga oshirilishi mumkin.

Hammasi haqida Java-da Davlat va Strategiya naqshlari o'rtasidagi farq. Aytganimdek, ikkalasi ham o'z sinflarida va UML diagrammalarida o'xshash ko'rinadi, ikkalasi ham ochiq/yopiq tamoyillarni ta'minlaydi va xatti-harakatlarni qamrab oladi. Ishlash vaqtida kontekstga ta'sir qiladigan algoritm yoki strategiyani, ehtimol parametr yoki kompozit ob'ekt sifatida inkapsulyatsiya qilish uchun Strategiya naqshidan foydalaning va Java-da holat o'tishlarini boshqarish uchun Davlat naqshidan foydalaning. Asl

Oxirgi yangilangan: 31.10.2015

Holat - bu ob'ektning ichki holatiga qarab o'z xatti-harakatlarini o'zgartirishga imkon beruvchi dizayn naqshidir.

Ushbu naqsh qachon ishlatiladi?

    Ob'ektning xatti-harakati uning holatiga bog'liq bo'lishi va ish vaqtida dinamik ravishda o'zgarishi mumkin bo'lganda

    Agar ob'ekt usullarining kodi ko'plab shartli konstruktsiyalardan foydalansa, ularni tanlash ob'ektning hozirgi holatiga bog'liq.

Ushbu dizayn namunasining UML diagrammasi quyidagi tizimni taklif qiladi:

C# da naqshning rasmiy ta'rifi:

Sinf dasturi ( statik void Main() ( Kontekst konteksti = yangi Kontekst(yangi DavlatA()); context.Request(); // StateB ga o‘tish context.Request(); // StateA ga o‘tish ) ) abstrakt sinf holati ( ommaviy abstract void Handle(Context context ) class StateA: State ( public override void Handle(Context context) ( context.State = new StateB(); ) ) class StateB: State ( public override void Handle(Context context) ( kontekst. State = new StateA( ) class Context ( public State State ( get; set; ) public Context(State state) ( this.State = state; ) public void Request() ( this.State.Handle(this ); ) )

Shakl ishtirokchilari

    Davlat: holat interfeysini belgilaydi

    StateA va StateB sinflari shtatlarning aniq amalga oshirilishidir

    Kontekst: xatti-harakati holatga ko'ra dinamik ravishda o'zgarishi kerak bo'lgan ob'ektni ifodalaydi. Muayyan harakatlarni bajarish davlat ob'ektiga topshiriladi

Masalan, suv bir qator holatda bo'lishi mumkin: qattiq, suyuq, bug '. Aytaylik, biz suv sinfini aniqlashimiz kerak, unda suvni isitish va muzlatish usullari mavjud. Davlat naqshini ishlatmasdan, biz quyidagi dasturni yozishimiz mumkin:

Sinf dasturi ( statik void Main(string args) ( Suv suvi = yangi Suv(WaterState.LIQUID); water.Heat(); water.Frost(); water.Frost(); Console.Read(); ) ) enum WaterState ( SOLID, LIQUID, GAS ) klassi Suv ( umumiy WaterState State ( get; set; ) Public Water(WaterState ws) ( State = ws; ) public void Heat() ( if(State==WaterState.SOLID) ( Console.WriteLine ("Muzni suyuqlikka aylantirish" holati = WaterState. LIQUID; (Holat == WaterState.GAS) ( Console.WriteLine("Suv bug'ining haroratini oshirish"); ) ) public void Frost() ( agar (Holat == WaterState.LIQUID) ( Console.WriteLine("Suyuqlikni muzga aylantirish "); Holat = WaterState.SOLID; ) else if (Holat == WaterState.GAS) ( Console.WriteLine("Suv bug'ini suyuqlikka aylantirish"); Holat = WaterState.LIQUID; ) ) )

Suvning uchta holati bor va har bir usulda amallarni bajarish uchun hozirgi holatga qarashimiz kerak. Natijada, uchta davlat allaqachon shartli tuzilmalarning konglomeratsiyasiga olib keladi. Shuningdek, suv sinfida ko'plab usullar mavjud bo'lishi mumkin, bu erda vaziyatga qarab harakat qilish kerak bo'ladi. Shuning uchun, dasturni yanada moslashuvchan qilish uchun, bu holda biz Davlat naqshini qo'llashimiz mumkin:

Sinf dasturi ( statik void Main(string args) (Suv suvi = yangi Suv(yangi LiquidWaterState()); water.Heat(); water.Frost(); water.Frost(); Console.Read(); ) ) sinf Suv ( jamoat IWaterState Davlat ( get; set; ) jamoat suvi (IWaterState ws) ( Davlat = ws; ) jamoat bo'shlig'i Heat() ( State.Heat(this); ) public void Frost() ( State.Frost(bu); ) ) interfeysi IWaterState ( void Heat(Water water); void Frost(Water water); ) class SolidWaterState: IWaterState ( public void Heat(Water water) ( Console.WriteLine("Turn muzni suyuqlikka"); water.State = new LiquidWaterState(); umumiy bo'shliq Frost(Suv suvi) ( Console.WriteLine("Muzni muzlatishda davom eting"); ) ) sinf LiquidWaterState: IWaterState ( umumiy bo'shliq Issiqlik(Suv suvi) ( Console.WriteLine("Suyuqlikni bug'ga aylantirish) ) ; water.State = new GasWaterState( ) public void Frost(Water water) ( Console.WriteLine("Convert suyuqlik into muz"); water.State = new SolidWaterState(); ) ) class GasWaterState ( public void Issiqlik (suv suvi) ( Console.WriteLine("Suv bug'ining haroratini oshirish"); ) ommaviy bo'shliq Frost(Water water) ( Console.WriteLine("Suv bug'ini suyuqlikka aylantirish"); water.State = new LiquidWaterState(); ) )

Shunday qilib, Davlat naqshini amalga oshirish ob'ektning joriy holatiga bog'liq bo'lgan xatti-harakatlarni alohida sinflarga ko'chirish va ob'ekt usullarini if..else yoki switch kabi shartli konstruktsiyalar bilan ortiqcha yuklashdan qochish imkonini beradi. Bundan tashqari, agar kerak bo'lsa, biz tizimga yangi holatlar sinflarini kiritishimiz va boshqa ob'ektlarda mavjud bo'lgan holatlar sinflaridan foydalanishimiz mumkin.

Eng so'nggi sayt materiallari