CodeGym /בלוג Java /Random-HE /דפוס עיצוב מתאם
John Squirrels
רָמָה
San Francisco

דפוס עיצוב מתאם

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

   void connectWithUsbCable();
}
זהו ממשק ה-USB שלנו עם שיטה אחת בלבד לחיבור באמצעות USB.
public class MemoryCard {

   public void insert() {
       System.out.println("Memory card successfully inserted!");
   }

   public void copyData() {
       System.out.println("The data has been copied to the computer!");
   }
}
זו הכיתה שלנו שמייצגת את כרטיס הזיכרון. יש לו כבר את 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הכיתה ומה בדיוק הופך אותה למתאם? הכל פשוט. המחלקה המותאמת (MemoryCard) הופכת לאחד מהשדות של המתאם. זה הגיוני. כאשר אנו שמים כרטיס זיכרון בתוך קורא כרטיסים במציאות, הוא גם הופך לחלק ממנו. בניגוד לכרטיס הזיכרון, המתאם חולק ממשק עם המחשב. יש לו כבל USB, כלומר ניתן לחבר אותו למכשירים אחרים באמצעות USB. זו הסיבה שמחלקת CardReader שלנו מיישמת את ממשק ה-USB. אבל מה בדיוק קורה בתוך השיטה הזו? בדיוק מה שאנחנו צריכים שיקרה! המתאם מאציל את העבודה לכרטיס הזיכרון שלנו. אכן, המתאם לא עושה כלום בעצמו. לקורא כרטיסים אין שום פונקציונליות עצמאית. תפקידו הוא רק לחבר את המחשב וכרטיס הזיכרון על מנת לאפשר לכרטיס לעשות את עבודתו - העתקת קבצים! המתאם שלנו מאפשר זאת על ידי מתן ממשק משלו ( connectWithUsbCable()השיטה) שיענה על ה"צרכים" של כרטיס הזיכרון. בואו ניצור תוכנית לקוח שתדמה אדם שרוצה להעתיק נתונים מכרטיס זיכרון:
public class Main {

   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();
   }
}
אז מה קיבלנו? פלט מסוף:

Memory card successfully inserted! 
The data has been copied to the computer!
מְעוּלֶה. השגנו את המטרה שלנו! הנה קישור לסרטון עם מידע על תבנית המתאם:

שיעורי תקציר קורא וכותב

כעת נחזור לפעילות האהובה עלינו: ללמוד על כמה שיעורים חדשים לעבודה עם קלט ופלט :) מעניין על כמה כבר למדנו. היום נדבר על 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ו  Readerאינם תואמים! כפי שניתן לראות, לכל יישום של read()השיטה יש פרמטרים וערכי החזר שונים. וכאן אנחנו צריכים InputStreamReader! זה ישמש כמתאם בין השיעורים שלנו. כמו בדוגמה של קורא הכרטיסים, שחשבנו עליו למעלה, שמנו מופע של התאמה של המחלקה "בתוך" מחלקת המתאם, כלומר אנו מעבירים אחד לבנאי שלו. בדוגמה הקודמת, שמנו אובייקט MemoryCardבתוך CardReader. עכשיו אנחנו מעבירים InputStream אובייקט לבנאי InputStreamReader! אנו משתמשים System.inבמשתנה המוכר שלנו בתור InputStream:
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