Ես գրում եմ այս հոդվածը, քանի որ ես չեմ գտել լուծում, որը նման է իմին, ուստի իմ լուծումը կարող է օգտակար լինել մեկ ուրիշի համար:
Բովանդակության աղյուսակ
Իրականացում
Իրականացնել դասերը
Օգտագործեք վիճակի օրինակը արձագանքման կեռիկում
Ամբողջական կոդը, այնպես որ կարող եք պատճենել-տեղադրել:
Ընդլայնված վիճակի մեքենա (Սխալի վիճակ, Պատճենել-Pastable HTML)
Դիագրամ
Կոդ
Ի՞նչ խնդիրներ է դա լուծում:
Ինչու է այս հոդվածը իմաստալից:
Իրականացում
Մենք իրականացնում ենք պետական դիզայնի օրինակը ճիշտ այնպես, ինչպես խորհուրդ է տալիս վերամշակման գուրուն՝ https://refactoring.guru/design-patterns/state
Իրականացնել Դասերը
class RoomState { #roomClient = null; #roomId = null; constructor(roomClient, roomId) { if (roomClient) { this.#roomClient = roomClient; } if (roomId) { this.roomId = roomId; } } set roomClient(roomClient) { if (roomClient) { this.#roomClient = roomClient; } } get roomClient() { return this.#roomClient; } set roomId(roomId) { if (roomId) { this.#roomId = roomId; } } get roomId() { return this.#roomId; } join(roomId) { throw new Error('Abstract method join(roomId).'); } leave() { throw new Error('Abstract method leave().'); } getStatusMessage() { throw new Error('Abstract method getStatusMessage().'); } } // ------------------------------------------------------------------------- class PingRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PongRoomState(this.roomClient, roomId)); } leave() { const message = `Left Ping room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Ping room ${this.roomId}`; } } // ------------------------------------------------------------------------- class PongRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { const message = `Left Pong room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Pong room ${this.roomId}`; } } // ------------------------------------------------------------------------- class LeftRoomState extends RoomState { #previousRoom = null; constructor(roomClient, previousRoom) { super(roomClient); this.#previousRoom = previousRoom; } join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { throw new Error(`Can't leave, no room assigned`); } getStatusMessage() { return `Not in any room (previously in ${this.#previousRoom})`; } }
Սա է առայժմ մեր պետական մեքենան
Օգտագործեք State Pattern-ը React Hook-ում
Հաջորդ խնդիրը. ինչպե՞ս ենք մենք օգտագործում դասերը react-ի հետ համատեղ:
Մյուս հոդվածները օգտագործում են useEffect
և տող՝ ընթացիկ վիճակի անունը պահելու համար. մենք ցանկանում ենք մաքուր պահել մեր իրականացումը:
roomClient
ը կարող է փոփոխել վիճակը, եթե այն հղում ունի setState
ֆունկցիային:
Խնդիրներ:
- Մենք չենք կարող փոխանցել
setState
ը, եթե մենք նախաստորագրենք վիճակը դասի հետ: - Մենք չենք ցանկանում վերադարձնել null-ը մանգաղից:
- Մենք չենք ցանկանում վերադարձնել կեղծ մեթոդներ, որոնք ոչինչ չեն վերադարձնում մանգաղից:
Լուծում, տրամադրեք roomClient
, հենց որ վիճակը սկզբնավորվի, հենց useState
ի տակ:
function useRoomClient() { const [state, setState] = useState(new PingRoomState()); // State contains the class // Initialize once // We can do this thanks to the `set` and `get` methods on // `roomClient` property if (!state.roomClient) { state.roomClient = { setState }; } return state; }
Ամբողջական կոդը, որպեսզի կարողանաք պատճենել-տեղադրել
class RoomState { #roomClient = null; #roomId = null; constructor(roomClient, roomId) { if (roomClient) { this.#roomClient = roomClient; } if (roomId) { this.roomId = roomId; } } set roomClient(roomClient) { if (roomClient) { this.#roomClient = roomClient; } } get roomClient() { return this.#roomClient; } set roomId(roomId) { if (roomId) { this.#roomId = roomId; } } get roomId() { return this.#roomId; } join(roomId) { throw new Error('Abstract method join(roomId).'); } leave() { throw new Error('Abstract method leave().'); } getStatusMessage() { throw new Error('Abstract method getStatusMessage().'); } } // ------------------------------------------------------------------------- class PingRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PongRoomState(this.roomClient, roomId)); } leave() { const message = `Left Ping room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Ping room ${this.roomId}`; } } // ------------------------------------------------------------------------- class PongRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { const message = `Left Pong room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Pong room ${this.roomId}`; } } // ------------------------------------------------------------------------- class LeftRoomState extends RoomState { #previousRoom = null; constructor(roomClient, previousRoom) { super(roomClient); this.#previousRoom = previousRoom; } join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { throw new Error(`Can't leave, no room assigned`); } getStatusMessage() { return `Not in any room (previously in ${this.#previousRoom})`; } } function useRoomClient() { const [state, setState] = useState(new PingRoomState()); // State contains the class // Initialize once // We can do this thanks to the `set` and `get` methods on // `roomClient` property if (!state.roomClient) { state.roomClient = { setState }; } return state; }
Ընդլայնված վիճակի մեքենա (սխալի վիճակ, պատճենահանվող HTML)
Մենք երկարացնում ենք վիճակի մեքենան, քանի որ ցանկանում ենք անցնել Error
վիճակի, եթե փորձենք դուրս գալ սենյակից, և դա հանգեցնում է սխալ գործողության: Այն թույլ է տալիս մեզ ցուցադրել կարգավիճակի հաղորդագրությունները՝ զանգահարելով getStatusMessage
:
Դիագրամ
Կոդ
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"></div> <script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react.development.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.development.js"></script> <script> class RoomState { #roomClient = null; #roomId = null; constructor(roomClient, roomId) { if (roomClient) { this.#roomClient = roomClient; } if (roomId) { this.roomId = roomId; } } set roomClient(roomClient) { if (roomClient) { this.#roomClient = roomClient; } } get roomClient() { return this.#roomClient; } set roomId(roomId) { if (roomId) { this.#roomId = roomId; } } get roomId() { return this.#roomId; } join(roomId) { throw new Error('Abstract method join(roomId).'); } leave() { throw new Error('Abstract method leave().'); } getStatusMessage() { throw new Error('Abstract method getStatusMessage().'); } } // ------------------------------------------------------------------------- class PingRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PongRoomState(this.roomClient, roomId)); } leave() { const message = `Left Ping room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Ping room ${this.roomId}`; } } // ------------------------------------------------------------------------- class PongRoomState extends RoomState { join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { const message = `Left Pong room ${this.roomId}`; this.roomClient.setState(new LeftRoomState(this.roomClient, message)); } getStatusMessage() { return `In the Pong room ${this.roomId}`; } } // ------------------------------------------------------------------------- class LeftRoomState extends RoomState { #previousRoom = null; constructor(roomClient, previousRoom) { super(roomClient); this.#previousRoom = previousRoom; } join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { // Extend to shift to error state this.roomClient.setState( new ErrorRoomState( this.roomClient, new Error(`Can't leave, no room assigned`), ), ); } getStatusMessage() { return `Not in any room (previously in ${this.#previousRoom})`; } } // Extend our state machine to hold one more state. class ErrorRoomState extends RoomState { #error = null; constructor(roomClient, error) { super(roomClient); this.#error = error; } join(roomId) { this.roomClient.setState(new PingRoomState(this.roomClient, roomId)); } leave() { // Do nothing... We can't move anywhere. We handled error. } getStatusMessage() { return `An error occurred. ${this.#error.message}`; } } const { useState } = React; function useRoomClient() { const [state, setState] = useState(new PingRoomState()); // State contains the class // Initialize once // We can do this thanks to the `set` and `get` methods on // `roomClient` property if (!state.roomClient) { state.roomClient = { setState }; } return state; } // ---------------------------------------------------------------------- // Usage example // ---------------------------------------------------------------------- const e = React.createElement; function useWithError(obj) {} function App() { const roomClient = useRoomClient(); return e( 'div', null, e('h1', null, 'Change room state'), e('p', null, `Status message: ${roomClient.getStatusMessage()}`), e( 'div', null, e('button', { onClick: () => roomClient.join('a') }, 'Join'), e('button', { onClick: () => roomClient.leave() }, 'Leave'), ), ); } const { createRoot } = ReactDOM; const root = document.getElementById('root'); createRoot(root).render(React.createElement(App)); </script> </body> </html>
Ի՞նչ խնդիրներ է այն լուծում:
- Մենք կարող ենք մասշտաբավորել պետական մեքենան՝ առանց գոյություն ունեցող կոդը փոփոխելու:
- Ավելի քիչ սխալներ:
- Ավելի հասկանալի կոդը, երբ մենք հասկանանք, թե ինչպես է այն աշխատում (մեզ մնում է նոր դաս ավելացնել նոր վիճակի համար) :
- Խուսափեք բարդ if-else բլոկներից, բարդ վիճակի մուտացիաներից և մեկ անջատիչ հայտարարությունից:
- Հաճելի է, եթե ցանկանում եք ստեղծել իրական ժամանակի սենյակներ՝ օգտագործելով WebSockets (Մենք կարող ենք վերահսկել օգտատերերի սենյակի կապի վիճակը և այլ տեսակի վիճակները):
Ինչու այս հոդվածը իմաստ ունի
Երբ Google-ում որոնեցի state design pattern
, սրանք իմ առաջին արդյունքներն էին
Հղումներ դեպի 3 արդյունքներ.
- https://refactoring.guru/design-patterns/state
- https://en.wikipedia.org/wiki/State_pattern
- https://www.geeksforgeeks.org/state-design-pattern/
- https://medium.com/@udaykale/evolving-the-state-design-pattern-e6682a866fdd
react state design pattern
որոնումը տալիս է այնպիսի իրականացումներ, որոնք նման չեն https://refactoring.guru/design-patterns/state կայքում իրականացվածին:
Որոնման արդյունքների հղումներ.
- https://medium.com/@yah.emam/state-design-pattern-using-react-hooks-c535e1daa6f1
- https://blog.logrocket.com/modern-guide-react-state-patterns/
- https://github.com/themithy/react-design-patterns/blob/master/doc/state-pattern.md
- https://refine.dev/blog/react-design-patterns/
- https://react.dev/learn/choosing-the-state-structure