תבנית עיצוב אסטרטגיה בג'אווה 8

1. הקדמה

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

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

לאחר מכן, ננסה את התבנית שוב, רק הפעם עם Java 8 lambdas, מה שמפחית את מילוליות הקוד שלנו.

2. תבנית אסטרטגיה

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

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

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

ממשק ציבורי Discounter {BigDecimal applyDiscount (סכום BigDecimal); } 

אז נניח שאנחנו רוצים להחיל 50% הנחה בחג הפסחא והנחה של 10% בחג המולד. בואו ליישם את הממשק שלנו לכל אחת מהאסטרטגיות הבאות:

מחלקה סטטית ציבורית EasterDiscounter מיישמת Discounter {@Override BigDecimal ציבורי ApplyDiscount (סכום BigDecimal סופי) {סכום החזר. מרובה (BigDecimal.valueOf (0.5)); }} מחלקה סטטית ציבורית ChristmasDiscounter מיישמת Discounter {@Override ציבורי BigDecimal applyDiscount (סכום BigDecimal סופי) {return amount.multiply (BigDecimal.valueOf (0.9)); }} 

לבסוף, בואו ננסה אסטרטגיה במבחן:

Discounter easterDiscounter = EasterDiscounter חדש (); BigDecimal discountedValue = easterDiscounter .applyDiscount (BigDecimal.valueOf (100)); assertThat (discountedValue) .isEqualByComparingTo (BigDecimal.valueOf (50));

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

Discounter easterDiscounter = New Discounter () {@Override BigDecimal publicDiscount (סכום BigDecimal סופי) {amount return.multiply (BigDecimal.valueOf (0.5)); }}; 

3. מינוף Java 8

מאז שחרורו של ג'אווה 8, הכנסת lambdas הפכה סוגים פנימיים אנונימיים למיותרים פחות או יותר. כלומר יצירת אסטרטגיות בתור היא הרבה יותר נקייה וקלה.

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

3.1. הפחתת מילוליות קוד

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

Discounter easterDiscounter = סכום -> סכום. מרובה (BigDecimal.valueOf (0.5)); 

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

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

רשימת מפגשים = newArrayList (סכום -> כמות. מרובה (BigDecimal.valueOf (0.9)), סכום -> סכום. Multiply (BigDecimal.valueOf (0.8)), כמות -> כמות. מרובה (BigDecimal.valueOf (0.5))) ;

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

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

ממשק ציבורי Discounter {BigDecimal applyDiscount (סכום BigDecimal); דיסקונט דיסקונט חג המולד () {סכום החזר -> סכום. מרובה (BigDecimal.valueOf (0.9)); } הנחה סטטית newYearDiscounter () {סכום החזר -> סכום. מרובה (BigDecimal.valueOf (0.8)); } הנחה סטטית easterDiscounter () {סכום החזר -> סכום. מרובה (BigDecimal.valueOf (0.5)); }} 

כפי שאנו רואים, אנו משיגים הרבה בקוד לא מאוד.

3.2. מינוף הרכב פונקציות

בואו נשנה את שלנו הנחה ממשק כך שהוא מרחיב את UnaryOperator ממשק ולאחר מכן הוסף א לְשַׁלֵב() שיטה:

ממשק ציבורי Discounter מרחיב את UnaryOperator {שילוב ברירת המחדל של Discounter (Discounter after) {return value -> after.apply (this.apply (value)); }}

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

ה לְשַׁלֵב() השיטה היא רק הפשטה סביב יישום הנחה לתוצאות של זֶה. הוא משתמש בפונקציונליות המובנית להגיש מועמדות() על מנת להשיג זאת.

עכשיו, בואו ננסה להחיל מספר רב הנחות באופן מצטבר לסכום. אנו נעשה זאת באמצעות פונקציונליות לְהַפחִית() ושלנו לְשַׁלֵב():

Discounter combinedDiscounter = discounters .stream () .reduce (v -> v, Discounter :: combine); combinedDiscounter.apply (...);

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

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

4. מסקנה

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

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