דפוס עיצוב מתורגמן בג'אווה

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

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

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

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

2. דפוס עיצוב מתורגמן

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

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

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

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

אחרת, זה עלול להיות קשה לתחזוקה.

3. דיאגרמת UML

בתרשים שלמעלה מוצגות שתי ישויות עיקריות: הֶקשֵׁר וה ביטוי.

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

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

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

אז מה ההבדל בין TerminalExpression ו NonTerminalExpression?

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

כדאי לציין זאת NonTerminalExpression הוא מרוכבים.

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

4. יישום

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

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

ה ביטוי לממשק תהיה שיטת הפרשנות:

פרשנות רשימה (Context ctx);

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

מחלקה בחר מיישמת ביטוי {טור טור פרטי; פרטי מאת מ; // constructor @Override רשימת הפרשנות הציבורית (Context ctx) {ctx.setColumn (עמודה); לחזור מ.פרש (ctx); }}

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

שים לב שבביטול לפרש() בשיטה זה קובע את מצב ההקשר ומעביר את הפרשנות לביטוי אחר יחד עם ההקשר.

בדרך זו אנו רואים שזה NonTerminalExpression.

ביטוי נוסף הוא ה מ מעמד:

מחלקה ממכשירים ביטוי {טבלת מיתרים פרטית; פרטי איפה איפה; // בונים @ פרסום רשימת ציבורי ציבורי (הקשר ctx) {ctx.setTable (טבלה); אם (איפה == null) {להחזיר ctx.search (); } להחזיר לאן. לפרש (ctx); }}

כעת, ב- SQL הסעיף שבו הוא אופציונלי, ולכן מחלקה זו היא ביטוי מסוף או ביטוי שאינו סופני.

אם המשתמש מחליט לא להשתמש בסעיף איפה, מ הביטוי זה יסתיים עם ctx.search () להתקשר ולהחזיר את התוצאה. אחרת זה יתפרש עוד יותר.

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

class איפה מיישם ביטוי {פילטר פרדיקט פרטי; // constructor @Override רשימת הפרשנות הציבורית (Context ctx) {ctx.setFilter (filter); החזר ctx.search (); }}

לדוגמא, ה הֶקשֵׁר מחלקה מחזיקה את הנתונים המחקים את טבלת מסד הנתונים.

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

הקשר קלאסי {מפה סטטית פרטית טבלאות = HashMap חדש (); סטטי {List list = new ArrayList (); list.add (שורה חדשה ("ג'ון", "איילה")); list.add (שורה חדשה ("יאן", "קובלסקי")); list.add (שורה חדשה ("דומיניק", "אבדון")); tables.put ("אנשים", רשימה); } שולחן מחרוזת פרטי; טור מחרוזת פרטי; פרדיקט פרטי WhereFilter; // ... חיפוש רשימה () {List result = tables.entrySet () .stream () .filter (entry -> entry.getKey (). EqualsIgnoreCase (table)) .flatMap (entry -> Stream.of (entry .getValue ())) .flatMap (אוסף :: stream) .map (שורה :: toString) .flatMap (columnMapper) .filter (whereFilter) .collect (Collectors.toList ()); ברור(); תוצאת החזרה; }}

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

כך כל פרשנות לא תשפיע על השנייה.

5. בדיקות

למטרות בדיקה, בואו נסתכל על ה- מתורגמן הדגמה מעמד:

class class InterpreterDemo {public static void main (String [] args) {Expression query = new Select ("name", new From ("people")); הקשר ctx = הקשר חדש (); תוצאת רשימה = query.interpret (ctx); System.out.println (תוצאה); שאילתת ביטוי 2 = חדש בחר ("*", חדש מאת ("אנשים")); רשימה result2 = query2.interpret (ctx); System.out.println (result2); שאילתת ביטוי 3 = חדשה בחר ("שם", חדש מאת ("אנשים", איפה חדש (שם -> שם.לולקייס (). StartsWith ("ד")))); רשימה result3 = query3.interpret (ctx); System.out.println (result3); }}

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

על ידי הפעלת התוכנית, הפלט צריך להיות כדלקמן:

[ג'ון, יאן, דומיניק] [ג'ון דו, ג'אן קובלסקי, דומיניק דום] [דומיניק]

6. חסרונות

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

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

7. מסקנה

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

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

לבסוף, אתה יכול למצוא שימוש בתבנית זו ב- JDK, במיוחד ב java.util. דפוס, java.text.Format אוֹ java.text.Normalizer.

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


$config[zx-auto] not found$config[zx-overlay] not found