Java สถานะรูปแบบ สถานะ

อนุญาตให้วัตถุเปลี่ยนแปลงพฤติกรรมขึ้นอยู่กับสถานะภายใน จากภายนอกดูเหมือนว่าคลาสของวัตถุมีการเปลี่ยนแปลง

รูปแบบ "สถานะ" เกี่ยวข้องกับการจัดสรรคลาสพื้นฐานหรืออินเทอร์เฟซสำหรับการดำเนินการที่ถูกต้องทั้งหมดและผู้สืบทอดสำหรับแต่ละสถานะที่เป็นไปได้

เมื่อใดจึงจะใช้รูปแบบของรัฐ

    เมื่อพฤติกรรมของวัตถุต้องขึ้นอยู่กับสถานะและสามารถเปลี่ยนแปลงแบบไดนามิกได้ที่รันไทม์

    เมื่อโค้ดของเมธอดของอ็อบเจ็กต์ใช้โครงสร้างแบบมีเงื่อนไขจำนวนมาก การเลือกจะขึ้นอยู่กับสถานะปัจจุบันของอ็อบเจ็กต์

แผนภาพ UML ของรูปแบบ "สถานะ":

การใช้รูปแบบ "สถานะ" ใน C #

ใช้ระบบ; เนมสเปซ DoFactory.GangOfFour.State.Structural ( /// /// คลาสเริ่มต้น MainApp สำหรับโครงสร้าง /// รูปแบบการออกแบบของรัฐ -คลาส MainApp ( /// /// เข้าสู่แอปพลิเคชันคอนโซล - static void Main() ( // ตั้งค่าบริบทในบริบทสถานะ c = บริบทใหม่ (new ConcreteStateA()); // คำขอปัญหาซึ่งสลับสถานะ c.Request(); c.Request(); c.Request() ; c.Request(); // รอผู้ใช้ Console.ReadKey() ) /// /// คลาสนามธรรม "สถานะ" ///สถานะคลาสนามธรรม ( โมฆะนามธรรมสาธารณะจัดการ (บริบทบริบท); ) /// คลาส ConcreteStateA: สถานะ ( โมฆะแทนที่สาธารณะจัดการ (บริบทบริบท) ( context.State = new ConcreteStateB(); ) ) /// /// คลาส "ConcreteState" ///คลาส ConcreteStateB: สถานะ ( โมฆะแทนที่สาธารณะจัดการ (บริบทบริบท) ( context.State = new ConcreteStateA(); ) ) /// /// คลาส "บริบท" ///บริบทของคลาส ( private State _state; // Constructor public Context(State state) ( this.State = state; ) // รับหรือตั้งค่าสถานะ public State State ( get ( return _state; ) set ( _state = value; Console.WriteLine ("สถานะ: " + _state.GetType().Name); คำขอโมฆะสาธารณะ() ( _state.Handle(this); ) )

ตัวอย่างรูปแบบรัฐจากชีวิตจริง

ตัวอย่างใน .NET Framework

  • CommunicationObject ใช้เครื่องสถานะสำหรับการเปลี่ยนระหว่างสถานะไคลเอ็นต์ WCF: สร้าง เปิด เปิด ปิด ปิด และผิดพลาด
  • งานใช้เครื่องสถานะจำกัดสำหรับการเปลี่ยนระหว่างสถานะของงาน: สร้างแล้ว กำลังรอการเปิดใช้งาน กำลังรอเรียกใช้ ทำงาน กำลังดำเนินการจนเสร็จสมบูรณ์ ยกเลิก ผิดพลาด

ในระบอบการปกครองของรัฐ (แบบจำลองรัฐ) พฤติกรรมของชั้นเรียนจะขึ้นอยู่กับสถานะที่เปลี่ยนแปลงไป รูปแบบการออกแบบประเภทนี้หมายถึงแบบจำลองเชิงพฤติกรรม

ในโมเดลสถานะ เราสร้างออบเจ็กต์และสถานะพฤติกรรมต่างๆ พร้อมกับสถานะของออบเจ็กต์ที่ได้รับการแก้ไขโดยการแสดงบริบทของออบเจ็กต์ที่ปรับเปลี่ยน

การแนะนำ

เจตนา: อนุญาตให้วัตถุเปลี่ยนพฤติกรรมเมื่อสถานะภายในเปลี่ยนแปลง จากนั้นวัตถุดูเหมือนจะเปลี่ยนคลาส

แก้ปัญหาเป็นหลัก: พฤติกรรมของวัตถุขึ้นอยู่กับสถานะ (คุณลักษณะ) และคุณสามารถเปลี่ยนแปลงได้ตามสถานะที่เกี่ยวข้องกับการเปลี่ยนแปลงพฤติกรรม

เมื่อใดควรใช้: รหัสประกอบด้วยออบเจ็กต์จำนวนมากที่เกี่ยวข้องกับสถานะของคำสั่งแบบมีเงื่อนไข

วิธีแก้ไข: สถานะของคลาสนามธรรมที่เป็นรูปธรรมดับแล้ว

รหัสคีย์: โหมดอินเทอร์เฟซคำสั่งโดยปกติจะมีเพียงวิธีเดียวเท่านั้นสถานะของอินเทอร์เฟซที่มีหนึ่งหรือหลายวิธี นอกจากนี้ เมธอดโหมดสถานะของคลาสการใช้งานมักจะส่งคืนค่าหรือเปลี่ยนค่าของตัวแปรอินสแตนซ์ นั่นคือสถานะและสถานะของโมเดลออบเจ็กต์มักจะเป็นข้อมูลล่าสุด วิธีการเรียนการใช้งานมีฟังก์ชั่นต่าง ๆ วิธีการอินเตอร์เฟซจะครอบคลุม โหมดเงื่อนไขและโหมดคำสั่งเดียวกันสามารถใช้เพื่อกำจัดเงื่อนไขอื่น ๆ ได้หาก... การเลือกอื่น

ตัวอย่างการใช้งาน: 1 การเล่นบาสเก็ตบอล ผู้เล่นสามารถมีสภาวะปกติ ไม่ใช่สภาวะปกติ และสภาวะผิดปกติได้ 2, Marquise Yi Zeng bells จากนั้น "clock abstract interface", "clock A" และสถานะที่เป็นรูปธรรมอื่น ๆ สภาพแวดล้อมเฉพาะ "" ระฆังจีน (บริบท)

ข้อดี: 1 สรุปกฎการเปลี่ยนแปลง 2 ระบุสถานะที่เป็นไปได้ก่อนที่จะแสดงรายการรัฐจำเป็นต้องกำหนดสถานะของชนิด 3 ทุกอย่างที่มีพฤติกรรมของรัฐนั้นเกี่ยวข้องกับคลาส และคุณสามารถเพิ่มสถานะใหม่ได้อย่างง่ายดาย คุณเพียงแค่ต้องเปลี่ยนสถานะของวัตถุก็สามารถเปลี่ยนพฤติกรรมของวัตถุได้ เวอร์ชัน 4 ซึ่งทำให้สามารถทำการเปลี่ยนแปลงสถานะได้ - สถานะตรรกะของวัตถุในหนึ่งเดียว แทนที่จะเป็นบล็อกคำสั่งแบบมีเงื่อนไขขนาดใหญ่เพียงบล็อกเดียว 5 อนุญาตให้หลายอ็อบเจ็กต์แบ่งปันสภาพแวดล้อมสถานะของอ็อบเจ็กต์ ซึ่งจะช่วยลดจำนวนอ็อบเจ็กต์ในระบบ

ข้อเสีย: 1 รูปแบบสถานะการใช้งานสัมพันธ์กับการเพิ่มจำนวนคลาสและอ็อบเจ็กต์ของระบบ 2 โครงสร้างและการดำเนินการของแบบฟอร์มสถานะมีความซับซ้อนมากขึ้นหากใช้ไม่ถูกต้องอาจทำให้เกิดความสับสนในโครงสร้างโปรแกรมและโค้ดได้ 3 การสนับสนุนโมเดลสถานะ "หลักการเปิดแบบเปิด" ไม่ค่อยดีนัก คุณสามารถเปลี่ยนสถานะของโมเดลสถานะได้โดยการเพิ่มคลาสใหม่ คุณต้องเปลี่ยนสถานะของผู้ที่รับผิดชอบในการเปลี่ยนสถานะซอร์สโค้ด หรือไม่สามารถเปลี่ยนไปใช้ สถานะใหม่และเปลี่ยนสถานะของคลาสให้ดำเนินการเช่นกัน จำเป็นต้องแก้ไขซอร์สโค้ดของคลาสที่เกี่ยวข้อง

กรณีการใช้งาน: 1 พร้อมการเปลี่ยนแปลงสถานะและพฤติกรรมการเปลี่ยนฉาก 2 การยืนยันการเปลี่ยนแปลงแบบมีเงื่อนไขโดยการแทนที่

หมายเหตุ: เมื่อใช้พฤติกรรมสถานะที่จำกัดตามโหมดสถานะ และสถานะจะไม่เกินห้า

การดำเนินการ

เราจะสร้างอินเทอร์เฟซสถานะและเอนทิตี สถานะคลาสการใช้งานอินเทอร์เฟซ สถานะ.บริบทแสดงถึงคลาสที่มีสถานะเฉพาะ

รัฐรูปแบบสาธิต,เราสาธิตการใช้วัตถุ บริบทบริบทของชั้นเรียนและสถานะเพื่อแสดงการเปลี่ยนแปลงพฤติกรรมในสภาวะของการเปลี่ยนแปลง

ขั้นตอนที่ 1

สร้างอินเทอร์เฟซ

State.java

สถานะอินเทอร์เฟซสาธารณะ ( doAction โมฆะสาธารณะ (บริบทบริบท); )

ขั้นตอนที่ 2

สร้างคลาสเอนทิตีที่ใช้อินเทอร์เฟซ

StartState.java

คลาสสาธารณะ StartState ใช้สถานะ ( public void DoAction(บริบทบริบท) ( System.out.println("Player is in beginning state"); context.setState(this); ) public String ToString() (return "starting state"; ) )

StopState.java

คลาสสาธารณะ StopState ใช้สถานะ ( public void doAction(Context context) ( System.out.println("Player is in stop state"); context.setState(this); ) public String toString())( return "Stop State"; ) )

ขั้นตอนที่ 3

การสร้างชั้นเรียน บริบท.

บริบท.java

คลาสสาธารณะ (บริบทของรัฐรัฐเอกชน; บริบทสาธารณะ () (รัฐ = NULL; ) โมฆะสาธารณะ SetState (รัฐรัฐ) ( this.state = รัฐ; ) รัฐสาธารณะ GetState () (สถานะกลับมา; ))

ขั้นตอนที่ 4

ใช้ บริบทเพื่อดูพฤติกรรมเมื่อสถานะการเปลี่ยนแปลงเปลี่ยนแปลง เงื่อนไข.

StatePatternDemo.java

คลาสสาธารณะ StatePatternDemo (สถานะแรงคงที่ของรัฐ main (สตริง) (บริบทบริบท agdz = บริบทใหม่ (); StartState startState = new StartState (); startState.doAction (บริบท); System.out.println (context.getState ()) ToString ( ) .); StopState stopState = new StopState().));

ขั้นตอนที่ 5

ตรวจสอบผลลัพธ์

ผู้เล่นอยู่ในสถานะเริ่มต้น สถานะเริ่ม ผู้เล่นอยู่ในสถานะหยุด หยุดแล้ว

ใช้ระบบ; สถานะเนมสเปซ ( ///

/// สถานะบัญชีถูกบล็อค -คลาสสาธารณะถูกบล็อก: IState ( /// /// เติมเงินในบัญชีของคุณ - /// บัญชีรีฟิลได้ /// จำนวนเงินที่เติมเงินฝากเป็นโมฆะสาธารณะ (บัตรบัตร, เงินทศนิยม) ( // ตรวจสอบอาร์กิวเมนต์อินพุตเพื่อความถูกต้อง if (card == null) ( โยน ArgumentNullException ใหม่ (ชื่อ (ชื่อ (บัตร)); ) ถ้า (เงิน<= 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 การใช้จ่าย(บัตรบัตร ราคาทศนิยม) ( // ปฏิเสธการดำเนินการ Console.WriteLine($"บัญชีของคุณถูกบล็อก เติมเงินในบัญชีของคุณ (card.ToString())"); return false; ) ) )

ชื่อรูปแบบและการจำแนกประเภท

รัฐเป็นรูปแบบพฤติกรรมของวัตถุ

วัตถุประสงค์ของแบบแผนของรัฐ

รูปแบบสถานะอนุญาตให้วัตถุเปลี่ยนพฤติกรรมขึ้นอยู่กับสถานะภายใน ปรากฏว่าวัตถุได้เปลี่ยนคลาสของมัน

รูปแบบสถานะคือการดำเนินการเชิงวัตถุของเครื่องสถานะ

ปัญหาที่ต้องแก้ไข

พฤติกรรมของอ็อบเจ็กต์ขึ้นอยู่กับสถานะและต้องเปลี่ยนแปลงระหว่างการทำงานของโปรแกรม โครงการดังกล่าวสามารถนำไปใช้ได้โดยใช้ตัวดำเนินการตามเงื่อนไขหลายตัว: การดำเนินการบางอย่างจะดำเนินการตามการวิเคราะห์สถานะปัจจุบันของวัตถุ อย่างไรก็ตาม ด้วยสถานะจำนวนมาก ข้อความสั่งแบบมีเงื่อนไขจะกระจัดกระจายไปทั่วโค้ด และโปรแกรมดังกล่าวจะรักษาได้ยาก

รูปแบบของรัฐแก้ไขปัญหานี้ดังนี้:

  • แนะนำคลาสบริบทซึ่งกำหนดส่วนต่อประสานกับโลกภายนอก
  • แนะนำคลาสรัฐนามธรรม
  • แสดงถึง "สถานะ" ต่างๆ ของเครื่องสถานะเป็นคลาสย่อยของ State
  • คลาสบริบทมีตัวชี้ไปยังสถานะปัจจุบัน ซึ่งจะเปลี่ยนแปลงเมื่อสถานะของเครื่องสถานะเปลี่ยนแปลง

รูปแบบสถานะไม่ได้กำหนดว่าเงื่อนไขในการเปลี่ยนไปสู่สถานะใหม่ถูกกำหนดไว้ที่ใด มีสองตัวเลือก: คลาสบริบทหรือคลาสย่อยของรัฐ ข้อดีของตัวเลือกหลังคือสามารถเพิ่มคลาสที่ได้รับใหม่ๆ ได้อย่างง่ายดาย ข้อเสียคือแต่ละคลาสย่อยของรัฐจะต้องรู้เกี่ยวกับเพื่อนบ้านของตนเพื่อทำการเปลี่ยนผ่านไปสู่สถานะใหม่ ซึ่งทำให้เกิดการพึ่งพาระหว่างคลาสย่อย

นอกจากนี้ยังมีแนวทางอื่นที่ใช้ตารางในการออกแบบเครื่องจักรสถานะจำกัด โดยอิงจากการใช้ตารางที่แมปข้อมูลอินพุตกับการเปลี่ยนระหว่างสถานะโดยไม่ซ้ำกัน อย่างไรก็ตาม วิธีการนี้มีข้อเสีย: เป็นการยากที่จะเพิ่มการดำเนินการเมื่อดำเนินการเปลี่ยนภาพ แนวทางรูปแบบสถานะใช้โค้ด (แทนโครงสร้างข้อมูล) เพื่อทำการเปลี่ยนระหว่างสถานะ ดังนั้นจึงเพิ่มการดำเนินการเหล่านี้ได้ง่าย

โครงสร้างรูปแบบของรัฐ

คลาสบริบทกำหนดอินเทอร์เฟซภายนอกสำหรับไคลเอนต์และจัดเก็บการอ้างอิงถึงสถานะปัจจุบันของวัตถุสถานะ อินเทอร์เฟซของสถานะคลาสฐานนามธรรมจะเหมือนกับอินเทอร์เฟซบริบท ยกเว้นพารามิเตอร์เพิ่มเติมหนึ่งรายการ - ตัวชี้ไปยังอินสแตนซ์บริบท คลาสที่ได้มาจากรัฐจะกำหนดพฤติกรรมเฉพาะของรัฐ คลาส Wrapper บริบทมอบหมายคำขอที่ได้รับทั้งหมดให้กับออบเจ็กต์ "สถานะปัจจุบัน" ซึ่งสามารถใช้พารามิเตอร์เพิ่มเติมที่ได้รับเพื่อเข้าถึงอินสแตนซ์บริบท

แผนภาพคลาส UML ของรูปแบบรัฐโครงสร้างของรูปแบบสถานะแสดงไว้ในรูปที่ 1 71.

บริบท setState (StateTwo);

ข้าว. 71. แผนภาพ UML ของรูปแบบรัฐ

ผู้เข้าร่วม

บริบท - บริบท:

  • กำหนดอินเทอร์เฟซที่สนใจให้กับลูกค้า
  • เก็บอินสแตนซ์ของคลาสย่อย ConcreteState ที่กำหนดสถานะปัจจุบัน

สถานะ: กำหนดอินเทอร์เฟซสำหรับการสรุปพฤติกรรมที่เกี่ยวข้องกับสถานะบริบทเฉพาะ

คลาสย่อย StateOne, StateTwo, StateThree - สถานะเฉพาะ: แต่ละคลาสย่อยใช้พฤติกรรมที่เกี่ยวข้องกับสถานะของบริบท

ความสัมพันธ์

คลาสบริบทมอบหมายคำขอที่ขึ้นกับสถานะให้กับอ็อบเจ็กต์ ConcreteState ปัจจุบัน

บริบทสามารถส่งผ่านตัวเองเป็นอาร์กิวเมนต์ไปยังวัตถุ State ที่จะประมวลผลคำขอได้ ซึ่งช่วยให้วัตถุสถานะสามารถเข้าถึงบริบทได้เมื่อจำเป็น

บริบทเป็นอินเทอร์เฟซหลักสำหรับลูกค้า ลูกค้าสามารถกำหนดค่าบริบทด้วยวัตถุสถานะ เมื่อกำหนดค่าบริบทแล้ว ไคลเอ็นต์ไม่จำเป็นต้องสื่อสารโดยตรงกับออบเจ็กต์สถานะอีกต่อไป

คลาสย่อย Context หรือ ConcreteState สามารถตัดสินใจได้ภายใต้เงื่อนไขใดและสถานะการเปลี่ยนแปลงลำดับใด

ตัวอย่างรูปแบบของรัฐ

รูปแบบสถานะอนุญาตให้วัตถุเปลี่ยนพฤติกรรมขึ้นอยู่กับสถานะภายใน ภาพที่คล้ายกันสามารถสังเกตได้ในการทำงานของเครื่องจำหน่ายสินค้าอัตโนมัติ ตู้จำหน่ายสินค้าอัตโนมัติอาจมีสถานะที่แตกต่างกันขึ้นอยู่กับความพร้อมของสินค้า จำนวนเหรียญที่ได้รับ ความเป็นไปได้ในการแลกเปลี่ยนเงิน ฯลฯ หลังจากที่ผู้ซื้อเลือกและชำระค่าสินค้าแล้ว สถานการณ์ (สถานะ) ต่อไปนี้จะเป็นไปได้:

  • มอบสินค้าให้กับผู้ซื้อโดยไม่จำเป็นต้องเปลี่ยนแปลง
  • ให้ผู้ซื้อสินค้าและการเปลี่ยนแปลง;
  • ผู้ซื้อจะไม่ได้รับสินค้าเนื่องจากขาดเงินเพียงพอ
  • ผู้ซื้อจะไม่ได้รับสินค้าเนื่องจากไม่มีสินค้า

การใช้รูปแบบของรัฐ

กำหนดคลาส wrapper บริบทที่มีอยู่หรือสร้างใหม่ที่ไคลเอ็นต์จะใช้เป็นเครื่องสถานะ

สร้างคลาสสถานะพื้นฐานที่จำลองแบบอินเทอร์เฟซของคลาสบริบท แต่ละวิธีใช้พารามิเตอร์เพิ่มเติมหนึ่งตัว: อินสแตนซ์ของคลาสบริบท คลาส State สามารถกำหนดพฤติกรรมเริ่มต้นที่เป็นประโยชน์ได้

สร้างคลาสที่ได้มาจากรัฐสำหรับรัฐที่เป็นไปได้ทั้งหมด

คลาสบริบทเพียงมอบหมายคำขอทั้งหมดที่ได้รับจากไคลเอนต์ไปยังวัตถุ "สถานะปัจจุบัน" และที่อยู่ของวัตถุบริบทจะถูกส่งผ่านเป็นพารามิเตอร์เพิ่มเติม

การใช้ที่อยู่นี้ วิธีการของคลาส State สามารถเปลี่ยน "สถานะปัจจุบัน" ของคลาส Context ได้หากจำเป็น

การดำเนินการตามรูปแบบของรัฐ

ลองพิจารณาตัวอย่างของเครื่องสถานะจำกัดที่มีสถานะที่เป็นไปได้สองสถานะและสองเหตุการณ์

ใช้เนมสเปซมาตรฐาน;

สถานะคลาส * ปัจจุบัน; สาธารณะ:

เป็นโมฆะ setCurrent (สถานะ * s)

เป็นโมฆะเมื่อ (); เป็นโมฆะปิด ();

โมฆะเสมือนบน (เครื่อง * m)

โมฆะเสมือนของ T (เครื่อง * m)

เครื่องเป็นโมฆะ::on()

ปัจจุบัน -> เปิด (นี้);

เครื่องเป็นโมฆะ::ปิด()

ปัจจุบัน -> ปิด (นี่);

ชั้นเรียน ON: รัฐสาธารณะ

เป็นโมฆะปิด (เครื่อง * m);

คลาสปิด: รัฐสาธารณะ

เป็นโมฆะบน(เครื่อง *m)

ศาล setCurrent (เปิดใหม่ ()); ลบสิ่งนี้;

เป็นโมฆะ ON::off (เครื่อง * m)

ศาล setCurrent (ปิดใหม่ ()); ลบสิ่งนี้;

เครื่อง::เครื่อง()

ปัจจุบัน = ปิดใหม่ (); ศาล

เป็นโมฆะ(เครื่อง:: *ptrs)() =

เครื่อง::ปิด เครื่อง::เปิด

เครื่องจักร fsm; หมายเลข int; ในขณะที่ (1)

(fsm. *ptrs)