JavaRush /בלוג Java /Random-HE /מתאם תבנית עיצוב

מתאם תבנית עיצוב

פורסם בקבוצה
שלום! היום ניגע בנושא חדש וחשוב - דפוסים, או במילים אחרות - דפוסי עיצוב . מה זה דפוסים? אני חושב שאתה מכיר את הביטוי "אל תמציא את הגלגל מחדש". בתכנות, כמו בתחומים רבים אחרים, יש מספר רב של מצבים אופייניים. עבור כל אחד מהם, בתהליך פיתוח התכנות, נוצרו פתרונות עבודה מוכנים. אלה דפוסי עיצוב. באופן יחסי, דפוס הוא דוגמה מסוימת שמציעה פתרון למצב כמו: "אם התוכנית שלך צריכה לעשות משהו, איך הכי טוב לעשות את זה." יש הרבה דפוסים, מוקדש להם ספר מצוין "לימוד דפוסי עיצוב", שבהחלט כדאי לקרוא. דפוס עיצוב "מתאם" - 2אם לומר את זה בקצרה ככל האפשר, דפוס מורכב מבעיה נפוצה והפתרון שלה, שכבר יכול להיחשב כסוג של תקן. בהרצאה של היום נתוודע לאחד מהדפוסים הללו שנקרא "מתאם". השם שלו אומר, ונתקלתם במתאמים בחיים האמיתיים יותר מפעם אחת. אחד המתאמים הנפוצים ביותר הוא קוראי כרטיסים, המצוידים במחשבים ומחשבים ניידים רבים. דפוס עיצוב מתאם - 3תארו לעצמכם שיש לנו סוג של כרטיס זיכרון. מה הבעיה? העובדה היא שהיא לא יודעת איך ליצור אינטראקציה עם מחשב. אין להם ממשק משותף. למחשב יש מחבר USB, אך לא ניתן להכניס אליו כרטיס זיכרון. לא ניתן להכניס את הכרטיס למחשב, עקב כך לא נוכל לשמור את התמונות, הסרטונים ונתונים נוספים שלנו. קורא הכרטיסים הוא מתאם שפותר בעיה זו. אחרי הכל, יש לו כבל USB! בניגוד לכרטיס עצמו, ניתן להכניס את קורא הכרטיסים למחשב. יש להם ממשק משותף עם המחשב - USB. בוא נראה איך זה ייראה עם דוגמה:
public interface USB {

   void connectWithUsbCable();
}
זהו ממשק ה-USB שלנו, כאשר השיטה היחידה היא להכניס את כבל ה-USB:
public class MemoryCard {

   public void insert() {
       System.out.println("Карта памяти успешно вставлена!");
   }

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
זו הכיתה שלנו שמיישמת את מפת הזיכרון. יש לו כבר 2 שיטות שאנחנו צריכים, אבל הנה הבעיה: הוא לא מיישם את ממשק ה-USB. לא ניתן להכניס את הכרטיס לחריץ ה-USB.
public class CardReader implements USB {

   private MemoryCard memoryCard;

   public CardReader(MemoryCard memoryCard) {
       this.memoryCard = memoryCard;
   }

   @Override
   public void connectWithUsbCable() {
       this.memoryCard.insert();
       this.memoryCard.copyData();
   }
}
והנה המתאם שלנו! מה עושה הכיתהCardReader ולמה זה מתאם? זה פשוט. המחלקה המותאמת (מפת זיכרון) הופכת לאחד השדות של המתאם. זה הגיוני, כי בחיים האמיתיים אנחנו גם מכניסים כרטיס בתוך קורא הכרטיסים, והוא גם הופך לחלק ממנו. בניגוד לכרטיס זיכרון, למתאם יש ממשק משותף עם המחשב. יש לו כבל USB, כלומר הוא יכול להתחבר להתקנים אחרים באמצעות USB. לכן, בתוכנית, הכיתה שלנו CardReaderמיישמת את ממשק ה-USB. אבל מה קורה בתוך השיטה הזו? וקורה בדיוק מה שאנחנו צריכים! המתאם מאציל את העבודה לכרטיס הזיכרון שלנו. אחרי הכל, המתאם עצמו לא עושה כלום; לקורא הכרטיסים אין שום פונקציונליות עצמאית. תפקידו הוא רק לקשר בין המחשב לכרטיס הזיכרון כדי שהכרטיס יוכל לעשות את עבודתו ולהעתיק קבצים! המתאם שלנו מאפשר לו לעשות זאת על ידי מתן ממשק משלו (שיטה connectWithUsbCable()) ל"צרכים" של כרטיס הזיכרון. בואו ניצור איזושהי תוכנת לקוח שתדמה אדם שרוצה להעתיק נתונים מכרטיס זיכרון:
public class Main {

   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();

   }
}
מה קיבלנו כתוצאה מכך? פלט מסוף:
Карта памяти успешно вставлена!
Данные скопированы на компьютер!
נהדר, המשימה שלנו הושלמה בהצלחה! הנה כמה קישורים נוספים עם מידע על תבנית המתאם:

שיעורים מופשטים קורא וכותב

כעת נחזור לבילוי המועדף עלינו: נלמד כמה שיעורים חדשים לעבודה עם קלט ופלט :) כמה מהם כבר למדנו, אני תוהה? היום נדבר על שיעורים Readerו Writer. למה איתם? כי זה יהיה קשור לסעיף הקודם שלנו - מתאמים. בואו נסתכל עליהם ביתר פירוט. נתחיל ב Reader-'א. Readerהוא מחלקה מופשטת, ולכן לא נוכל ליצור את האובייקטים שלה במפורש. אבל למעשה, אתה כבר מכיר אותו! אחרי הכל, המעמדות שאתה מכיר היטב BufferedReaderהם InputStreamReaderהיורשים שלה :)
public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
אז, מחלקה InputStreamReaderהיא מתאם קלאסי . כפי שאתה בוודאי זוכר, אנו יכולים להעביר אובייקט לבנאי שלו InputStream. לרוב אנו משתמשים במשתנה לשם כך System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
מה זה עושה InputStreamReader? כמו כל מתאם, הוא ממיר ממשק אחד למשנהו. במקרה זה, הממשק InputStream'א לממשק Reader'א. בהתחלה היה לנו שיעור InputStream. זה עובד טוב, אבל זה יכול לקרוא רק בתים בודדים. בנוסף, יש לנו שיעור מופשט Reader. יש לו פונקציונליות מעולה שאנחנו באמת צריכים - הוא יכול לקרוא תווים! אנחנו, כמובן, באמת צריכים את ההזדמנות הזו. אבל כאן אנו עומדים בפני בעיה קלאסית שמתאמים פותרים בדרך כלל - אי תאימות של ממשק. איך זה בא לידי ביטוי? בואו נסתכל ישר לתוך התיעוד של אורקל. להלן שיטות הכיתה InputStream. Паттерн проектирования «Адаптер» - 4קבוצה של שיטות היא ממשק. כפי שאתה יכול לראות, read()למחלקה הזו יש שיטה (אפילו במספר גרסאות), אבל היא יכולה לקרוא רק בתים: או בתים בודדים, או כמה בתים באמצעות מאגר. האפשרות הזו לא מתאימה לנו - אנחנו רוצים לקרוא תווים. הפונקציונליות שאנו צריכים כבר מיושמת במחלקה המופשטתReader . ניתן לראות זאת גם בתיעוד. Паттерн проектирования «Адаптер» - 5עם זאת, ממשקי InputStream'a' ו- Reader'a' אינם תואמים! כפי שאתה יכול לראות, בכל יישומי השיטה, read()גם הפרמטרים שהועברו וגם ערכי ההחזרה שונים. וכאן אנחנו צריכים את זה InputStreamReader! הוא ישמש כמתאם בין השיעורים שלנו. כמו בדוגמה עם קורא הכרטיסים, שעליו הסתכלנו למעלה, אנו מעבירים את האובייקט של המחלקה "מותאמת" "פנימית", כלומר לבנאי מחלקת המתאם. בדוגמה הקודמת, העברנו אובייקט MemoryCardבתוך CardReader. כעת אנו מעבירים את האובייקט InputStreamלבנאי InputStreamReader! כאיכות InputStreamאנו משתמשים במשתנה המוכר ממילא System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
ואכן: בהסתכלות על התיעוד InputStreamReaderנראה שה"הסתגלות" הצליחה :) כעת עומדות לרשותנו שיטות המאפשרות לנו לקרוא דמויות. Паттерн проектирования «Адаптер» - 6ולמרות שבתחילה האובייקט שלנו System.in(חוט קשור למקלדת) לא אפשר זאת, על ידי יצירת תבנית המתאם , יוצרי השפה פתרו את הבעיה הזו. למחלקה המופשטת Reader, כמו לרוב מחלקות ה-I/O, יש אח תאום - Writer. יש לו את אותו יתרון גדול Reader- הוא מספק ממשק נוח לעבודה עם סמלים. עם זרמי פלט, הבעיה והפתרון שלה נראים זהים כמו במקרה של זרמי קלט. יש מחלקה OutputStreamשיכולה לכתוב רק בתים; יש מחלקה מופשטת Writerשיכולה לעבוד עם סמלים, ויש שני ממשקים לא תואמים. בעיה זו נפתרת שוב בהצלחה על ידי דפוס המתאם. באמצעות מחלקה, OutputStreamWriterאנו יכולים בקלות "להתאים" שני ממשקי מחלקה Writerזה OutputStreamלזה. ולאחר שקיבלנו זרם בתים OutputStreamבקונסטרוקטור, בעזרת העזרה OutputStreamWriterאנו יכולים, לעומת זאת, לכתוב תווים, לא בתים!
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException {

       OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"));
       streamWriter.write(32144);
       streamWriter.close();
   }
}
כתבנו תו עם קוד 32144 - 綐 לתוך הקובץ שלנו, ובכך ביטלנו את הצורך לעבוד עם בתים :) זה הכל להיום, נתראה בהרצאות הבאות! :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION