תבנית עיצוב המדינה בג'אווה

1. סקירה כללית

במדריך זה נציג את אחד מתבניות העיצוב של GoF ההתנהגותי - דפוס המדינה.

בהתחלה, ניתן סקירה על מטרתה ונסביר את הבעיה שהיא מנסה לפתור. לאחר מכן, נבחן את דיאגרמת ה- UML של המדינה ויישום הדוגמה המעשית.

2. תבנית עיצוב מדינה

הרעיון המרכזי של תבנית המדינה הוא לאפשר לאובייקט לשנות את התנהגותו מבלי לשנות את המעמד שלו. כמו כן, על ידי יישומו, הקוד צריך להישאר נקי יותר ללא הרבה הצהרות אם / אחר.

תאר לעצמך שיש לנו חבילה שנשלחת לסניף דואר, ניתן להזמין את החבילה עצמה, ואז למסירה לסניף דואר ולקבל אותה לבסוף על ידי לקוח. כעת, בהתאם למצב בפועל, אנו רוצים להדפיס את מצב המסירה שלו.

הגישה הפשוטה ביותר תהיה להוסיף כמה דגלים בוליאניים ולהחיל הצהרות פשוטות אם / אחרת בכל אחת מהשיטות שלנו בכיתה. זה לא יסבך את זה הרבה בתרחיש פשוט. עם זאת, זה עלול לסבך ולזהם את הקוד שלנו כאשר נקבל יותר מדינות לעיבוד, מה שיביא עוד יותר הצהרות אם / אחר.

חוץ מזה, כל ההיגיון של כל אחת מהמדינות יתפשט בכל השיטות. כעת, כאן ניתן לשקול שימוש בתבנית המדינה. הודות לתבנית העיצוב של המדינה, אנו יכולים לתמצת את ההיגיון בשיעורים ייעודיים, להחיל את עקרון האחריות היחידה ואת העיקרון הפתוח / סגור, לקבל קוד נקי יותר ומתוחזק יותר.

3. דיאגרמת UML

בתרשים ה- UML אנו רואים זאת הֶקשֵׁר בכיתה יש משויך מדינה אשר ישתנה במהלך ביצוע התוכנית.

ההקשר שלנו הולך האצילו את ההתנהגות ליישום המדינה. במילים אחרות, כל הבקשות הנכנסות יטופלו על ידי יישום קונקרטי של המדינה.

אנו רואים שההיגיון מופרד והוספת מצבים חדשים היא פשוטה - זה מסתכם בהוספת עוד מדינה יישום במידת הצורך.

4. יישום

בואו נעצב את היישום שלנו. כאמור, ניתן להזמין, למסור ולקבל את החבילה, ולכן יהיו לנו שלוש מדינות ומחלקת ההקשר.

ראשית, בואו נגדיר את ההקשר שלנו, זה הולך להיות a חֲבִילָה מעמד:

חבילה בכיתה ציבורית {State PackageState פרטי = חדש OrderedState (); // getter, setter public void previousState () {state.prev (this); } חלל ציבורי nextState () {state.next (this); } חלל ציבורי printStatus () {state.printStatus (); }}

כפי שאנו רואים, הוא מכיל התייחסות לניהול המדינה, הודעה previousState (), nextState () ו- printStatus () שיטות בהן אנו מאצילים את התפקיד לאובייקט המדינה. המדינות יקושרו זה לזה ו כל מדינה תקבע מדינה אחרת על בסיס זֶה התייחסות הועבר לשתי השיטות.

הלקוח יקיים אינטראקציה עם חֲבִילָה בכיתה, ובכל זאת הוא לא יצטרך להתמודד עם הגדרת המדינות, כל מה שהלקוח צריך לעשות זה ללכת למצב הבא או הקודם.

לאחר מכן, יהיה לנו את PackageState שיש לו שלוש שיטות עם החתימות הבאות:

ממשק ציבורי PackageState {void next (חבילה pkg); בטל הקודם (חבילה pkg); בטל printStatus (); }

ממשק זה יושם על ידי כל מחלקת מדינה קונקרטית.

המצב הקונקרטי הראשון יהיה OrderedState:

מחלקה ציבורית OrderedState מיישם את PackageState {@Override public void next (Package pkg) {pkg.setState (DeliveredState חדש ()); } @ ביטול חלל ציבורי הקודם (חבילה pkg) {System.out.println ("החבילה במצב הבסיס שלה."); } @ ביטול הריק הציבורי printStatus () {System.out.println ("החבילה הוזמנה, טרם נמסרה למשרד."); }}

כאן, אנו מצביעים על המצב הבא שיתרחש לאחר הזמנת החבילה. המצב המסודר הוא מצב הבסיס שלנו ואנחנו מסמנים אותו במפורש. אנו יכולים לראות בשתי השיטות כיצד מתמודד עם המעבר בין המדינות.

בואו נסתכל על ה- DeliveredState מעמד:

המחלקה הציבורית DeliveredState מיישמת את PackageState {@Override public void next (Package pkg) {pkg.setState (New ReceState ()); } @Override public void prev (חבילה pkg) {pkg.setState (חדש OrderedState ()); } @Override public void printStatus () {System.out.println ("החבילה נשלחה לסניף הדואר, טרם התקבלה."); }}

שוב אנו רואים את הקשר בין המדינות. החבילה משנה את מצבה מההזמנה למסירה, ההודעה ב printStatus () משתנה גם כן.

הסטטוס האחרון הוא מדינה קיבלה:

המחלקה הציבורית ReceState מיישמת את PackageState {@Override public void next (Package pkg) {System.out.println ("חבילה זו כבר התקבלה על ידי לקוח."); } @Override public void prev (חבילה pkg) {pkg.setState (DeliveredState חדש ()); }}

זה המקום בו אנו מגיעים למצב האחרון, אנו יכולים רק להחזיר למצב הקודם.

אנחנו כבר רואים שיש תמורה מסוימת מכיוון שמדינה אחת יודעת על המדינה האחרת. אנחנו הופכים אותם לזוגיים הדוקים.

5. בדיקות

בואו נראה איך היישום מתנהג. ראשית, בוא נוודא אם מעברי ההתקנה פועלים כצפוי:

@Test הציבור בטל givenNewPackage_whenPackageReceived_thenStateReceived () {חבילה pkg = חבילה חדשה (); assertThat (pkg.getState (), instanceOf (OrderedState.class)); pkg.nextState (); assertThat (pkg.getState (), instanceOf (DeliveredState.class)); pkg.nextState (); assertThat (pkg.getState (), instanceOf (ReceiveState.class)); }

ואז, בדוק במהירות אם החבילה שלנו יכולה לחזור אחורה עם מצבה:

@Test הציבור בטל givenDeliveredPackage_whenPrevState_thenStateOrdered () {חבילה pkg = חבילה חדשה (); pkg.setState (DeliveredState חדש ()); pkg.previousState (); assertThat (pkg.getState (), instanceOf (OrderedState.class)); }

לאחר מכן, בואו נאמת את שינוי המצב ונראה כיצד היישום של printStatus () השיטה משנה את יישומה בזמן הריצה:

class public StateDemo {public static void main (String [] args) {Package pkg = new Package (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); pkg.nextState (); pkg.printStatus (); }}

זה ייתן לנו את התפוקה הבאה:

חבילה הוזמנה, טרם נמסרה למשרד. החבילה נמסרה לסניף הדואר, טרם התקבלה. החבילה התקבלה על ידי הלקוח. חבילה זו כבר התקבלה על ידי לקוח. החבילה התקבלה על ידי הלקוח.

מכיוון ששינינו את מצב ההקשר שלנו, ההתנהגות השתנתה אך המעמד נשאר זהה. כמו גם ה- API בו אנו משתמשים.

כמו כן, המעבר בין המדינות התרחש, המעמד שלנו שינה את מצבו וכתוצאה מכך את התנהגותו.

6. חסרונות

החיסרון בתבנית המדינה הוא התמורה בעת ביצוע מעבר בין המדינות. זה הופך את המדינה לקודדת קשה, וזה נוהג רע באופן כללי.

אבל, בהתאם לצרכים ולדרישות שלנו, זה יכול להיות או לא יכול להיות בעיה.

7. דפוס מדינה לעומת אסטרטגיה

שני דפוסי העיצוב דומים מאוד, אך דיאגרמת ה- UML שלהם זהה, כאשר הרעיון מאחוריהם שונה במקצת.

קודם ה דפוס אסטרטגיה מגדיר משפחה של אלגוריתמים הניתנים להחלפה. ככלל, הם משיגים את אותה מטרה, אך עם יישום שונה, למשל, אלגוריתמי מיון או עיבוד.

בתבנית המצב, ההתנהגות עשויה להשתנות לחלוטין, בהתבסס על המצב בפועל.

הַבָּא, באסטרטגיה, הלקוח צריך להיות מודע לאסטרטגיות האפשריות לשימוש ושינוי בהן במפורש. ואילו בתבנית המצב, כל מדינה מקושרת למדינה אחרת ויוצרת את הזרימה כמו במכונת המצב הסופי.

8. מסקנה

דפוס עיצוב המדינה הוא נהדר כשאנחנו רוצים הימנע מהצהרות פרימיטיביות אם / אחר. במקום זאת, אנחנו לחלץ את ההיגיון לשיעורים נפרדים ולתת שלנו אובייקט ההקשר האציל את ההתנהגות לשיטות המיושמות במעמד המדינה. חוץ מזה, אנו יכולים למנף את המעברים בין המדינות, כאשר מדינה אחת יכולה לשנות את מצב ההקשר.

באופן כללי, דפוס עיצוב זה נהדר ליישומים פשוטים יחסית, אך לגישה מתקדמת יותר, אנו יכולים להסתכל במדריך של מכונת המדינה של Spring.

כרגיל, הקוד השלם זמין בפרויקט GitHub.