שרשרת תבנית עיצוב אחריות בג'אווה

1. הקדמה

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

אנו יכולים למצוא דפוסי עיצוב נוספים במאמר הקודם שלנו.

2. שרשרת אחריות

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

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

דפוס שרשרת האחריות שימושי ל:

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

אז, בואו נראה דוגמה פשוטה לדפוס.

3. דוגמא

אנו נשתמש בשרשרת האחריות כדי ליצור שרשרת לטיפול בבקשות אימות.

אז ספק אימות הקלט יהיה פקודה, וכל מעבד אימות יהיה נפרד מעבד לְהִתְנַגֵד.

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

מחלקה מופשטת ציבורית AuthenticationProcessor {Public AuthenticationProcessor nextProcessor; // בונים סטנדרטיים בוליאני מופשט ציבורי isAuthorized (AuthenticationProvider authProvider); }

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

מחלקה ציבורית OAuthProcessor מרחיב את AuthenticationProcessor {public OAuthProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override בוליאני ציבורי isAuthorized (AuthenticationProvider authProvider) {if (authProvider instance of OAuthTokenProvider) {return true; } אחרת אם (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } להחזיר שקר; }}
מחלקה ציבורית UsernamePasswordProcessor מרחיב את AuthenticationProcessor {public UsernamePasswordProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override בוליאני ציבורי isAuthorized (AuthenticationProvider authProvider) {if (authProvider instance of UsernamePasswordProvider) {return true; } אחרת אם (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } להחזיר שקר; }}

כאן יצרנו שני מעבדים קונקרטיים לבקשות ההרשאה הנכנסות שלנו: UsernamePasswordProcessor ו OAuthProcessor.

עבור כל אחד, אנו מכבידים על ה- הוא מורשה שיטה.

עכשיו בואו ניצור כמה מבחנים:

מחלקה ציבורית ChainOfResponsibilityTest {AuthenticationProcessor static private getChainOfAuthProcessor () {AuthenticationProcessor oAuthProcessor = OAuthProcessor חדש (null); להחזיר UsernamePasswordProcessor חדש (oAuthProcessor); } @Test הציבור בטל שניתןOAuthProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertTrue (authProcessorChain.isAuthorized (חדש OAuthTokenProvider ())); } @Test הציבור בטל givenSamlProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertFalse (authProcessorChain.isAuthorized (חדש SamlTokenProvider ())); }}

הדוגמה לעיל יוצרת שרשרת של מעבדי אימות: UsernamePasswordProcessor -> OAuthProcessor. במבחן הראשון ההרשאה מצליחה, ובשנייה היא נכשלת.

ראשון, UsernamePasswordProcessor בודק אם ספק האימות הוא מופע של UsernamePasswordProvider.

לא להיות הקלט הצפוי, UsernamePasswordProcessor נציגים ל OAuthProcessor.

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

4. עקרונות יישום

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

  • לכל מעבד בשרשרת יישוםו לעיבוד פקודה
    • בדוגמה שלנו לעיל, כל המעבדים מיישמים את הוא מורשה
  • לכל מעבד בשרשרת צריך להיות התייחסות למעבד הבא
    • מֵעַל, UsernamePasswordProcessor נציגים ל OAuthProcessor
  • כל מעבד אחראי להאצלתו למעבד הבא ולכן היזהרו מפקודות שנפלו
    • שוב בדוגמה שלנו, אם הפקודה היא מופע של SamlProvider אז ייתכן שהבקשה לא תטופל והיא לא תהיה מורשית
  • מעבדים לא צריכים ליצור מחזור רקורסיבי
    • בדוגמה שלנו, אין לנו מחזור בשרשרת שלנו: UsernamePasswordProcessor -> OAuthProcessor. אבל, אם קבענו במפורש UsernamePasswordProcessor כמעבד הבא של OAuthProcessor, ואז נגמר עם מחזור בשרשרת שלנו: UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. לקיחת המעבד הבא בבנאי יכולה לעזור בכך
  • רק מעבד אחד בשרשרת מטפל בפקודה נתונה
    • בדוגמה שלנו, אם פקודה נכנסת מכילה מופע של OAuthTokenProvider, רק אז OAuthProcessor יטפל בפקודה

5. שימוש בעולם האמיתי

בעולם ג'אווה, אנו נהנים מדי יום משרשרת האחריות. דוגמה קלאסית כזו היא Servlet Filters in ג'אווה המאפשרים למסננים מרובים לעבד בקשת HTTP. אם כי במקרה זה, כל פילטר קורא לשרשרת במקום למסנן הבא.

בואו נסתכל על קטע הקוד למטה כדי להבין טוב יותר את הדפוס הזה במסנני Servlet:

מחלקה ציבורית CustomFilter מיישמת את המסנן {public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) זורק IOException, ServletException {// מעבד את הבקשה // מעביר את הבקשה (כלומר הפקודה) לאורך שרשרת שרשרת המסננים. doFilter (בקשה, תגובה ); }}

כפי שנראה בקטע הקוד לעיל, עלינו להפעיל FilterChainשל doFilter שיטה על מנת להעביר את הבקשה למעבד הבא בשרשרת.

6. חסרונות

ועכשיו, אחרי שראינו עד כמה שרשרת האחריות מעניינת, בואו נזכור כמה חסרונות:

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

7. מסקנה

במאמר זה שוחחנו על שרשרת אחריות ועל חוזקותיה וחולשותיה בעזרת שרשרת לאשר בקשות אימות נכנסות.

וכמו תמיד, ניתן למצוא את קוד המקור ב- GitHub.