здрасти Днес ще се докоснем до една важна нова тема: дизайнерски модели . Какви са тези модели? Мисля, че трябва да знаете израза „ не преоткривайте колелото “. В програмирането, Howто и в много други области, има голям брой често срещани ситуации. С развитието на софтуерната разработка за всеки от тях са създадени готови работещи решения. Тези решения се наричат шаблони за проектиране. По конвенция моделът е няHowво решение, формулирано така: "ако трябва да направите X във вашата програма, тогава това е най-добрият начин да го направите". Има много модели. На тях е посветена страхотната книга "Head First Design Patterns", с която определено трябва да се запознаете.
Казано накратко, моделът се състои от общ проблем и съответно решение, което може да се счита за вид стандарт. В днешния урок ще се запознаем с един от тези модели: Адаптер. Името му казва всичко и вие сте срещали адаптери много пъти в реалния живот. Някои от най-често срещаните адаптери са четците на карти, които имат много компютри и лаптопи.
Да предположим, че имаме няHowва карта с памет. Та Howъв е проблема? То не знае How да взаимодейства с компютъра. Те не споделят общ интерфейс. Компютърът има USB порт, но не можем да поставим картата с памет в него. Картата не може да бъде включена в компютъра, така че не можем да запазим нашите снимки, видеоклипове и други данни. Четецът на карти е адаптер, който решава този проблем. Все пак има USB кабел! За разлика от самата карта, четецът на карти може да бъде включен в компютъра. Те споделят общ интерфейс с компютъра: USB. Нека да видим How изглежда това на практика:
Набор от методи е точно това, което е интерфейс. Както можете да видите, този клас има a
обаче са несъвместими! Както можете да видите, всяка реализация на метода има различни параметри и връщани стойности. И това е мястото, от което имаме нужда ! Той ще действа като адаптер между нашите класове.
И въпреки че нашият


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
class do и Howво точно го прави адаптер? Всичко е просто. Класът, който се адаптира (MemoryCard), става едно от полетата на адаптера. Това има смисъл. Когато поставим карта с памет в четец на карти в реалния живот, тя също става част от него. За разлика от картата с памет, адаптерът споделя интерфейс с компютъра. Има USB кабел, тоест може да се свързва с други устройства чрез USB. Ето защо нашият клас CardReader имплементира USB интерфейса. Но Howво точно се случва вътре в този метод? Точно това, което трябва да се случи! Адаптерът делегира работата на нашата карта с памет. Всъщност адаптерът не прави нищо сам. Четецът на карти няма независима функционалност. Неговата задача е само да свърже компютъра и картата с памет, за да може картата да си върши работата — копиране на файлове!connectWithUsbCable()
метод), за да отговори на „нуждите“ на картата с памет. Нека създадем клиентска програма, която ще симулира човек, който иска да копира данни от карта с памет:
public class Main {
public static void main(String[] args) {
USB cardReader = new CardReader(new MemoryCard());
cardReader.connectWithUsbCable();
}
}
И така, Howво получихме? Конзолен изход:
Memory card successfully inserted!
The data has been copied to the computer!
Отлично. Постигнахме целта си! Ето връзка към видеоклип с информация за модела на адаптера:
Абстрактни класове по читател и писател
Сега ще се върнем към любимата ни дейност: запознаване с няколко нови класа за работа с вход и изход :) Чудя се колко вече сме научor. Днес ще говорим за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);
}
Но Howво InputStreamReader
прави? Като всеки адаптер, той преобразува един интерфейс в друг. В този случай интерфейсът InputStream
към Reader
интерфейса. Първоначално имаме InputStream
класа. Работи добре, но можете да го използвате само за четене на отделни byteове. Освен това имаме Reader
абстрактен клас. Има някои много полезни функции — знае How да чете знаци! Със сигурност имаме нужда от тази способност. Но тук се сблъскваме с класическия проблем, който обикновено се решава от адаптери - несъвместими интерфейси. Какво означава това? Нека да разгледаме documentацията на Oracle. Ето методите на InputStream
класа. 
read()
метод (всъщност няколко варианта), но може да чете само byteове: or отделни byteове, or няколко byteа с помощта на буфер. Но тази опция не ни подхожда - искаме да четем знаци. Имаме нужда от функционалността, която вече е внедрена в Reader
абстрактния клас . Можем да видим това и в documentацията. Интерфейсите и 
InputStream
Reader
read()
InputStreamReader
Както в примера с четеца на карти, който разгледахме по-горе, поставяме екземпляр на адаптирания клас "вътре" в класа на адаптера, т.е. предаваме такъв на неговия конструктор. В предишния пример поставихме MemoryCard
обект вътре CardReader
. Сега предаваме InputStream
обект на InputStreamReader
конструктора! Използваме нашата позната System.in
променлива като InputStream
:
public static void main(String[] args) {
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
И наистина, разглеждайки documentацията за InputStreamReader
, можем да видим, че адаптацията е успешна :) Сега имаме методи за четене на знаци на наше разположение. 
System.in
обект (потокът, свързан с клавиатурата) първоначално не позволяваше това, създателите на езика решиха този проблем чрез прилагане на модела на адаптера. Абстрактният Reader
клас, подобно на повечето I/O класове, има брат близнак — Writer
. Има същото голямо предимство като Reader
— осигурява удобен интерфейс за работа с герои. При изходните потоци проблемът и неговото решение изглеждат по същия начин като при входните потоци. Има OutputStream
клас, който може да записва само byteове, имаWriter
абстрактен клас, който знае How да работи със знаци и има два несъвместими интерфейса. Този проблем отново се решава от модела на адаптера. Използваме OutputStreamWriter
класа, за да адаптираме лесно двата интерфейса на класовете Writer
и OutputStream
един към друг. След предаване на OutputStream
поток от byteове към конструктора, можем да използваме OutputStreamWriter
за запис на символи, а не на byteове!
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();
}
}
Написахме символа с code 32144 (綐) в нашия файл, елиминирайки необходимостта от работа с byteове :) Това е всичко за днес. Ще се видим в следващите уроци! :)
GO TO FULL VERSION