JavaRush /בלוג Java /Random-HE /עיצוב דפוסי ג'אווה
Viacheslav
רָמָה

עיצוב דפוסי ג'אווה

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

תבניות

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

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

  • מִבנִי

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

  • התנהגותי

    דפוסים אלו פותרים את הבעיה של אינטראקציה יעילה בין אובייקטים

כדי לשקול דוגמאות, אני מציע להשתמש במהדר הקוד המקוון repl.it.
עיצוב דפוסי ג'אווה - 2

דפוסי יצירה

נתחיל מתחילת מחזור החיים של חפצים – עם יצירת חפצים. תבניות גנרטיביות עוזרות ליצור אובייקטים בצורה נוחה יותר ומספקות גמישות בתהליך זה. אחד המפורסמים ביותר הוא " בנאי ". דפוס זה מאפשר לך ליצור אובייקטים מורכבים צעד אחר צעד. ב-Java, הדוגמה המפורסמת ביותר היא StringBuilder:
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
גישה ידועה נוספת ליצירת אובייקט היא להעביר את היצירה לשיטה נפרדת. שיטה זו הופכת, כביכול, למפעל חפצים. לכן הדפוס נקרא " שיטת המפעל ". ב-Java, למשל, ניתן לראות את ההשפעה שלו ב-class java.util.Calendar. המחלקה עצמה Calendarמופשטת, וכדי ליצור אותה משתמשים בשיטה getInstance:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
לעתים קרובות הסיבה לכך היא שההיגיון מאחורי יצירת אובייקט יכול להיות מורכב. לדוגמה, במקרה שלמעלה, אנו ניגשים למחלקה הבסיסית Calendarונוצרת מחלקה GregorianCalendar. אם נסתכל על הבנאי, נוכל לראות שיישומים שונים נוצרים בהתאם לתנאים Calendar. אבל לפעמים שיטת מפעל אחת לא מספיקה. לפעמים צריך ליצור חפצים שונים כדי שישתלבו זה בזה. תבנית נוספת תעזור לנו בזה - " מפעל מופשט ". ואז אנחנו צריכים ליצור מפעלים שונים במקום אחד. יחד עם זאת, היתרון הוא שפרטי היישום אינם חשובים לנו, כלומר. זה לא משנה איזה מפעל ספציפי נקבל. העיקר שהוא יוצר את המימושים הנכונים. דוגמה על:
עיצוב דפוסי ג'אווה - 3
כלומר, בהתאם לסביבה (מערכת הפעלה), נקבל מפעל מסוים שייצור אלמנטים תואמים. כחלופה לגישה של יצירה באמצעות מישהו אחר, אנו יכולים להשתמש בדפוס " אב טיפוס ". המהות שלו פשוטה - אובייקטים חדשים נוצרים בדמותם ובדמותם של אובייקטים שכבר קיימים, כלומר. לפי אב הטיפוס שלהם. ב-Java, כולם נתקלו בדפוס הזה - זה השימוש בממשק java.lang.Cloneable:
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
כפי שאתה יכול לראות, המתקשר לא יודע איך ה- clone. כלומר, יצירת אובייקט המבוסס על אב טיפוס היא באחריות האובייקט עצמו. זה שימושי מכיוון שהוא לא קושר את המשתמש ליישום אובייקט התבנית. ובכן, האחרון ברשימה זו הוא דפוס "סינגלטון". מטרתו פשוטה - לספק מופע בודד של האובייקט עבור האפליקציה כולה. דפוס זה מעניין כי הוא מראה לעתים קרובות בעיות ריבוי השרשורים. למבט מעמיק יותר, עיין במאמרים הבאים:
דפוסי עיצוב בג'אווה - 4

דפוסים מבניים

עם יצירת החפצים זה נעשה ברור יותר. עכשיו זה הזמן להסתכל על דפוסים מבניים. המטרה שלהם היא לבנות היררכיות כיתתיות קלות לתמיכה ומערכות היחסים ביניהן. אחד הדפוסים הראשונים והידועים הוא " סגן " (פרוקסי). ל-proxy יש ממשק זהה לאובייקט האמיתי, כך שאין הבדל עבור הלקוח לעבוד דרך ה-proxy או ישירות. הדוגמה הפשוטה ביותר היא java.lang.reflect.Proxy :
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
כפי שאתה יכול לראות, בדוגמה שיש לנו מקורי - זה זה HashMapשמיישם את הממשק Map. בשלב הבא אנו יוצרים פרוקסי שמחליף את המקורי HashMapעבור חלק הלקוח, אשר קורא ל- putand methods get, ומוסיף את ההיגיון שלנו במהלך השיחה. כפי שאנו יכולים לראות, האינטראקציה בדפוס מתרחשת באמצעות ממשקים. אבל לפעמים תחליף לא מספיק. ואז ניתן להשתמש בתבנית " דקורטור ". דקורטור נקרא גם עטיפה או עטיפה. פרוקסי ודקורטור דומים מאוד, אבל אם תסתכל על הדוגמה, תראה את ההבדל:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
שלא כמו פרוקסי, מעצב עוטף את עצמו סביב משהו שמועבר כקלט. פרוקסי יכול גם לקבל את מה שצריך לקבל פרוקסי וגם לנהל את החיים של אובייקט פרוקסי (לדוגמה, ליצור אובייקט פרוקסי). יש עוד דפוס מעניין - " מתאם ". זה דומה למעצב - המעצב לוקח חפץ אחד כקלט ומחזיר עטיפה מעל החפץ הזה. ההבדל הוא שהמטרה היא לא לשנות את הפונקציונליות, אלא להתאים ממשק אחד למשנהו. ל-Java יש דוגמה מאוד ברורה לכך:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
בכניסה יש לנו מערך. לאחר מכן, אנו יוצרים מתאם שמביא את המערך לממשק List. כשעובדים איתו, אנחנו למעשה עובדים עם מערך. לכן, הוספת אלמנטים לא תעבוד, כי... לא ניתן לשנות את המערך המקורי. ובמקרה הזה נקבל UnsupportedOperationException. הגישה המעניינת הבאה לפיתוח מבנה הכיתה היא התבנית המרוכבת . מעניין בכך שקבוצה מסוימת של אלמנטים באמצעות ממשק אחד מסודרת בהיררכיה מסוימת דמוית עץ. על ידי קריאה למתודה על אלמנט אב, אנו מקבלים קריאה לשיטה זו על כל רכיבי הצאצא הדרושים. דוגמה מצוינת לדפוס זה הוא ממשק המשתמש (בין אם זה java.awt או JSF):
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
כפי שאנו יכולים לראות, הוספנו רכיב למיכל. ואז ביקשנו מהמיכל ליישם את הכיוון החדש של הרכיבים. והמיכל, ביודע מאילו רכיבים הוא מורכב, האציל את ביצוע הפקודה הזו לכל רכיבי הצאצא. דפוס מעניין נוסף הוא דפוס " גשר ". זה נקרא כך מכיוון שהוא מתאר קשר או גשר בין שתי היררכיות מחלקות שונות. אחת מההיררכיות הללו נחשבת להפשטה והשנייה למימוש. זה מודגש מכיוון שההפשטה עצמה לא מבצעת פעולות, אלא מאצילה את הביצוע הזה למימוש. דפוס זה משמש לעתים קרובות כאשר יש מחלקות "שליטה" וכמה סוגים של מחלקות "פלטפורמה" (לדוגמה, Windows, Linux וכו'). בגישה זו, אחת מההיררכיות הללו (הפשטה) תקבל התייחסות לאובייקטים של היררכיה אחרת (יישום) ותאציל להם את עיקר העבודה. מכיוון שכל המימושים יפעלו על פי ממשק משותף, ניתן להחליף ביניהם בתוך ההפשטה. ב-Java, דוגמה ברורה לכך היא java.awt:
דפוסי עיצוב בג'אווה - 5
למידע נוסף, עיין במאמר " דפוסים ב-Java AWT ". בין הדפוסים המבניים, ברצוני לציין גם את דפוס " חזית ". המהות שלו היא להסתיר את המורכבות של השימוש בספריות/מסגרות שמאחורי API זה מאחורי ממשק נוח ותמציתי. לדוגמה, אתה יכול להשתמש ב-JSF או ב-EntityManager מ-JPA כדוגמה. יש גם דפוס נוסף שנקרא " משקל זבוב ". המהות שלו היא שאם לאובייקטים שונים יש אותו מצב, אז ניתן להכליל ולאחסן אותו לא בכל אובייקט, אלא במקום אחד. ואז כל אובייקט יוכל להתייחס לחלק משותף, מה שיפחית את עלויות הזיכרון לאחסון. דפוס זה כרוך לעתים קרובות בשמירה מראש במטמון או בשמירה על מאגר של אובייקטים. מעניין, אנחנו גם מכירים את הדפוס הזה מההתחלה:
דפוסי עיצוב בג'אווה - 6
באותה אנלוגיה, ניתן לכלול כאן מאגר של מיתרים. אתה יכול לקרוא את המאמר בנושא זה: " דפוס עיצוב במשקל זבוב ".
דפוסי עיצוב בג'אווה - 7

תבניות התנהגותיות

אז, הבנו כיצד ניתן ליצור אובייקטים וכיצד ניתן לארגן קשרים בין מחלקות. הדבר המעניין ביותר שנותר הוא לספק גמישות בשינוי התנהגותם של חפצים. ודפוסי התנהגות יעזרו לנו בכך. אחד הדפוסים המוזכרים ביותר הוא דפוס ה"אסטרטגיה ". כאן מתחיל לימוד הדפוסים בספר " ראש ראשון. דפוסי עיצוב ". באמצעות תבנית ה"אסטרטגיה", נוכל לאחסן בתוך אובייקט כיצד נבצע את הפעולה, כלומר. האובייקט בפנים מאחסן אסטרטגיה שניתן לשנות במהלך ביצוע קוד. זהו דפוס שאנו משתמשים בו לעתים קרובות בעת שימוש במשוואה:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
לפנינו - TreeSet. יש לו התנהגות של TreeSetשמירה על סדר האלמנטים, כלומר. ממיין אותם (מכיוון שזה SortedSet). להתנהגות זו יש אסטרטגיית ברירת מחדל, שאנו רואים ב-JavaDoc: מיון ב"סדר טבעי" (עבור מחרוזות, זהו סדר לקסיקוגרפי). זה קורה אם אתה משתמש בבנאי ללא פרמטרים. אבל אם אנחנו רוצים לשנות את האסטרטגיה, אנחנו יכולים לעבור Comparator. בדוגמה זו, נוכל ליצור את הסט שלנו כ new TreeSet(comparator), ואז סדר אחסון האלמנטים (אסטרטגיית אחסון) ישתנה לזה שצוין בהשוואה. מעניין, יש כמעט אותו דפוס שנקרא " מדינה ". דפוס ה"מצב" אומר שאם יש לנו התנהגות כלשהי באובייקט הראשי שתלויה במצב של אובייקט זה, אז נוכל לתאר את המצב עצמו כאובייקט ולשנות את אובייקט המצב. ולהאציל שיחות מהאובייקט הראשי למדינה. דפוס נוסף המוכר לנו מלימוד היסודות של שפת ג'אווה הוא דפוס " פקודה ". דפוס עיצוב זה מציע שניתן לייצג פקודות שונות כמחלקות שונות. דפוס זה דומה מאוד לדפוס האסטרטגיה. אבל בדפוס האסטרטגיה, הגדרנו מחדש כיצד פעולה ספציפית תתבצע (לדוגמה, מיון ב TreeSet). בדפוס "פקודה", אנו מגדירים מחדש איזו פעולה תתבצע. פקודת הדפוס נמצאת איתנו כל יום כאשר אנו משתמשים בחוטים:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
כפי שאתה יכול לראות, הפקודה מגדירה פעולה או פקודה שתבוצע בשרשור חדש. כדאי גם לשקול את דפוס " שרשרת האחריות ". גם דפוס זה פשוט מאוד. הדפוס הזה אומר שאם צריך לעבד משהו, אז אתה יכול לאסוף מטפלים בשרשרת. לדוגמה, דפוס זה משמש לעתים קרובות בשרתי אינטרנט. בכניסה, לשרת יש בקשה כלשהי מהמשתמש. בקשה זו עוברת לאחר מכן בשרשרת העיבוד. שרשרת מטפלים זו כוללת מסננים (לדוגמה, אין לקבל בקשות מרשימה שחורה של כתובות IP), מטפלי אימות (אפשר רק למשתמשים מורשים), מטפל בכותרות בקשות, מטפל במטמון וכו'. אבל יש דוגמה פשוטה ומובנת יותר ב-Java java.util.logging:
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
כפי שאתה יכול לראות, מטפלים מתווספים לרשימת מטפלי הלוגרים. כאשר לוגר מקבל הודעה לעיבוד, כל הודעה כזו עוברת דרך שרשרת של מטפלים (מאת logger.getHandlers) עבור אותו לוגר. דפוס נוסף שאנו רואים מדי יום הוא " איטרטור ". המהות שלו היא להפריד בין אוסף של אובייקטים (כלומר מחלקה המייצגת מבנה נתונים. למשל, List) ומעבר של אוסף זה.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
כפי שניתן לראות, האיטרטור אינו חלק מהאוסף, אלא מיוצג על ידי מחלקה נפרדת שחוצה את האוסף. ייתכן שהמשתמש באיטרטור אפילו לא יודע על איזה אוסף הוא חוזר, כלומר. באיזה אוסף הוא מבקר? גם דפוס המבקר שווה לשקול . דפוס המבקר דומה מאוד לדפוס האיטרטור. דפוס זה עוזר לך לעקוף את המבנה של אובייקטים ולבצע פעולות על אובייקטים אלה. הם שונים למדי בקונספט. האיטרטור חוצה את האוסף כך שללקוח המשתמש באיטרטור לא אכפת מה האוסף בפנים, רק האלמנטים ברצף חשובים. המבקר אומר שיש היררכיה או מבנה מסוים של האובייקטים שאנו מבקרים. לדוגמה, אנו יכולים להשתמש בעיבוד ספריות נפרד ובעיבוד קבצים נפרד. ל-Java יש יישום מחוץ לקופסה של דפוס זה בצורה java.nio.file.FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
לפעמים יש צורך באובייקטים מסוימים להגיב לשינויים באובייקטים אחרים, ואז דפוס "הצופה" יעזור לנו . הדרך הנוחה ביותר היא לספק מנגנון מנוי המאפשר לאובייקטים מסוימים לנטר ולהגיב לאירועים המתרחשים באובייקטים אחרים. דפוס זה משמש לעתים קרובות אצל מאזינים ומשקיפים שונים המגיבים לאירועים שונים. כדוגמה פשוטה, אנו יכולים להיזכר ביישום של דפוס זה מהגרסה הראשונה של JDK:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
יש עוד דפוס התנהגותי שימושי - " מתווך ". זה שימושי כי במערכות מורכבות זה עוזר להסיר את הקשר בין אובייקטים שונים ולהאציל את כל האינטראקציות בין אובייקטים לאובייקט כלשהו, ​​שהוא מתווך. אחד היישומים הבולטים ביותר של דפוס זה הוא Spring MVC, המשתמש בדפוס זה. תוכל לקרוא עוד על כך כאן: " אביב: דפוס מתווך ". לעתים קרובות אתה יכול לראות את אותו הדבר בדוגמאות java.util.Timer:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
הדוגמה נראית יותר כמו תבנית פקודה. ומהות דפוס ה"מתווך" חבוי ביישום Timer'א. בתוך הטיימר יש תור משימות TaskQueue, יש שרשור TimerThread. אנו, כלקוחות של מחלקה זו, לא מקיימים איתם אינטראקציה, אלא מקיימים אינטראקציה עם Timerהאובייקט, אשר, בתגובה לקריאה שלנו לשיטות שלו, ניגש לשיטות של אובייקטים אחרים שהוא מתווך שלהם. חיצונית זה עשוי להיראות דומה מאוד ל"חזית". אבל ההבדל הוא שכאשר משתמשים ב-Facade, הרכיבים לא יודעים שהחזית קיימת ומדברים זה עם זה. וכאשר נעשה שימוש ב"מגשר", הרכיבים יודעים ומשתמשים במתווך, אך אינם יוצרים קשר ישיר זה עם זה. כדאי להתייחס לתבנית " שיטת התבנית ", התבנית ברורה משמה. השורה התחתונה היא שהקוד כתוב בצורה כזו שמשתמשי הקוד (מפתחים) מקבלים תבנית אלגוריתם כלשהי, שאת השלבים שלה מותר להגדיר מחדש. זה מאפשר למשתמשי קוד לא לכתוב את כל האלגוריתם, אלא לחשוב רק על איך לבצע נכון שלב כזה או אחר באלגוריתם זה. לדוגמה, ל- Java יש מחלקה אבסטרקטית AbstractListהמגדירה את ההתנהגות של איטרטור על ידי List. עם זאת, האיטרטור עצמו משתמש בשיטות עלים כגון: get, set, remove. התנהגותן של שיטות אלו נקבעת על ידי מפתח הצאצאים AbstractList. לפיכך, האיטרטור ב- AbstractListהוא תבנית עבור האלגוריתם לאיטרציה מעל גיליון. ומפתחים של יישומים ספציפיים AbstractListמשנים את ההתנהגות של איטרציה זו על ידי הגדרת ההתנהגות של שלבים ספציפיים. האחרון מבין הדפוסים שאנו מנתחים הוא דפוס ה-" Snapshot " (מומנטו). המהות שלו היא לשמר מצב מסוים של אובייקט עם יכולת לשחזר מצב זה. הדוגמה המוכרת ביותר מה-JDK היא הסדרת אובייקטים, כלומר. java.io.Serializable. בואו נסתכל על דוגמה:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
דפוסי עיצוב בג'אווה - 8

סיכום

כפי שראינו מהסקירה, יש מגוון עצום של דפוסים. כל אחד מהם פותר את הבעיה שלו. וידע על דפוסים אלה יכול לעזור לך להבין בזמן כיצד לכתוב את המערכת שלך כך שתהיה גמישה, ניתנת לתחזוקה ועמידה בפני שינויים. ולבסוף, כמה קישורים לצלילה מעמיקה יותר: #ויאצ'סלב
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION