היי! היום ניגע בנושא חדש וחשוב: תבניות עיצוב . מהן הדפוסים האלה? אני חושב שאתה חייב להכיר את הביטוי " אל תמציא את הגלגל מחדש ". בתכנות, כמו בתחומים רבים אחרים, יש מספר רב של מצבים נפוצים. ככל שפיתוח התוכנה התפתח, נוצרו עבור כל אחד מהם פתרונות מוכנים שעובדים. פתרונות אלו נקראים דפוסי עיצוב. לפי המוסכמה, דפוס הוא פתרון כלשהו שנוסח כך: "אם אתה צריך לעשות X בתוכנית שלך, זו הדרך הטובה ביותר לעשות זאת". יש המון דפוסים. להן מוקדש הספר המצוין "Head First Design Patterns", שבהחלט כדאי שתכירו.
בניסוח תמציתי, דפוס מורכב מבעיה נפוצה ומפתרון תואם שיכול להיחשב כמעין תקן. בשיעור של היום נפגוש את אחת מהדפוסים הבאים: מתאם. השם שלו אומר הכל, ונתקלתם במתאמים פעמים רבות בחיים האמיתיים. כמה מהמתאמים הנפוצים ביותר הם קוראי הכרטיסים שיש למחשבים ומחשבים ניידים רבים.
נניח שיש לנו כרטיס זיכרון כלשהו. אז מה הבעיה? זה לא יודע איך לתקשר עם המחשב. הם אינם חולקים ממשק משותף. למחשב יש יציאת 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
הכיתה. 
read()
שיטה (כמה גרסאות, למעשה), אבל היא יכולה לקרוא רק בתים: או בתים בודדים, או כמה בתים באמצעות מאגר. אבל האפשרות הזו לא מתאימה לנו - אנחנו רוצים לקרוא דמויות. אנחנו צריכים את הפונקציונליות שכבר מיושמת במחלקה Reader
המופשטת . אנו יכולים לראות זאת גם בתיעוד. 
InputStream
ו Reader
אינם תואמים! כפי שניתן לראות, לכל יישום של read()
השיטה יש פרמטרים וערכי החזר שונים. וכאן אנחנו צריכים InputStreamReader
! זה ישמש כמתאם בין השיעורים שלנו. כמו בדוגמה של קורא הכרטיסים, שחשבנו עליו למעלה, שמנו מופע של התאמה של המחלקה "בתוך" מחלקת המתאם, כלומר אנו מעבירים אחד לבנאי שלו. בדוגמה הקודמת, שמנו אובייקט MemoryCard
בתוך CardReader
. עכשיו אנחנו מעבירים InputStream
אובייקט לבנאי InputStreamReader
! אנו משתמשים System.in
במשתנה המוכר שלנו בתור InputStream
:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
ואכן, בהסתכלות על התיעוד של InputStreamReader
, אנו יכולים לראות שהעיבוד הצליח :) כעת עומדות לרשותנו שיטות לקריאת דמויות. 
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 (綐) לקובץ שלנו, ומבטל את הצורך לעבוד עם בתים :) זהו להיום. נתראה בשיעורים הבאים! :)
GO TO FULL VERSION