سلام! امروز به یک موضوع جدید مهم خواهیم پرداخت: الگوهای طراحی . این الگوها چیست؟ من فکر می کنم شما باید عبارت " چرخ را دوباره اختراع نکنید " را بدانید. در برنامه نویسی، مانند بسیاری از زمینه های دیگر، تعداد زیادی موقعیت رایج وجود دارد. همانطور که توسعه نرم افزار تکامل یافته است، راه حل های آماده ای که برای هر یک از آنها کار می کند ایجاد شده است. این راه حل ها الگوهای طراحی نامیده می شوند. طبق قرارداد، یک الگو راه حلی است که به این صورت فرموله شده است: "اگر باید X را در برنامه خود انجام دهید، این بهترین راه برای انجام آن است". الگوهای زیادی وجود دارد. کتاب عالی الگوهای طراحی اول سر که حتما باید با آن آشنا شوید تقدیم به آنها.
به طور خلاصه، یک الگو شامل یک مسئله مشترک و یک راه حل مربوطه است که می تواند نوعی استاندارد در نظر گرفته شود. در درس امروز با یکی از این الگوها آشنا می شویم: آداپتور. نام آن گویای همه چیز است و شما بارها در زندگی واقعی با آداپتورها روبرو شده اید. برخی از رایج ترین آداپتورها کارتخوان هایی هستند که بسیاری از رایانه ها و لپ تاپ ها دارند.
فرض کنید ما نوعی کارت حافظه داریم. پس مشکل چیست؟ نمی داند چگونه با کامپیوتر ارتباط برقرار کند. آنها رابط مشترکی ندارند. رایانه دارای پورت 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
امروز در مورد و کلاس ها صحبت خواهیم کرد 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
کلاس abstract پیاده سازی شده است . ما همچنین می توانیم این را در مستندات مشاهده کنیم. 
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
جریان بایت به سازنده، می توانیم از an 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