Сізге депутат не үшін керек?
Бұл үлгі нысанға басқарылатын қатынаспен байланысты мәселелерді шешуге көмектеседі. Сізде сұрақ туындауы мүмкін: «Бізге мұндай басқарылатын қол жеткізу не үшін қажет?» Ненің не екенін анықтауға көмектесетін бірнеше жағдайды қарастырайық.1-мысал
Бізде дерекқордан есептерді жүктеп алуға жауапты класс бар ескі codeтар жиынтығы бар үлкен жоба бар деп елестетіп көрейік. Класс синхронды түрде жұмыс істейді, яғни дерекқор сұрауды өңдеген кезде бүкіл жүйе жұмыс істемейді. Орта есеппен есеп 30 minutesта жасалады. Осы мүмкіндіктің арқасында оны жүктеп салу 00:30-да басталады және басшылық бұл есепті таңертең алады. Сараптама барысында есеп құрастырылғаннан кейін бірден, яғни бір күн ішінде алу қажет екені анықталды. Бастау уақытын өзгерту мүмкін емес, өйткені жүйе дерекқордан жауап күтеді. Шешім бөлек ағында жүктеп салуды және есеп шығаруды бастау арқылы жұмыс принципін өзгерту болып табылады. Бұл шешім жүйенің әдеттегідей жұмыс істеуіне мүмкіндік береді және басшылық жаңа есептерді алады. Дегенмен, мәселе бар: ағымдағы codeты қайта жазу мүмкін емес, өйткені оның функцияларын жүйенің басқа бөліктері пайдаланады. Бұл жағдайда есепті жүктеп салуға, басталу уақытын тіркеуге және бөлек ағынды іске қосуға сұранысты алатын орынбасар үлгісін пайдаланып аралық прокси класын енгізуге болады. Есеп жасалғанда, ағын өз жұмысын аяқтайды және барлығы бақытты болады.2-мысал
Әзірлеу тобы постер веб-сайтын жасайды. Жаңа оқиғалар туралы деректерді алу үшін олар үшінші тарап қызметіне жүгінеді, онымен өзара әрекеттесу арнайы жабық кітапхана арқылы жүзеге асырылады. Әзірлеу кезінде мәселе туындады: үшінші тарап жүйесі деректерді күніне бір рет жаңартады және оған сұрау пайдаланушы бетті жаңартқан сайын пайда болады. Бұл сұраулардың көп санын жасайды және қызмет жауап беруді тоқтатады. Шешім қызмет жауабын кэштеу және келушілерді әрбір қайта жүктеу кезінде сақталған нәтижемен қамтамасыз ету, қажет болған жағдайда бұл кэшті жаңарту болып табылады. Бұл жағдайда орынбасар үлгісін пайдалану дайын функционалдылықты өзгертпестен тамаша шешім болып табылады.Үлгі қалай жұмыс істейді
Бұл үлгіні енгізу үшін прокси класын жасау керек. Ол клиент codeы үшін оның әрекетін модельдейтін қызмет класының интерфейсін жүзеге асырады. Осылайша, нақты an objectінің орнына клиент өзінің проксиімен әрекеттеседі. Әдетте, барлық сұраулар қызмет көрсету класына беріледі, бірақ оның шақыруына дейін немесе одан кейін қосымша әрекеттермен. Қарапайым тілмен айтқанда, бұл прокси нысаны клиент codeы мен мақсатты нысан арасындағы қабат болып табылады. Өте баяу ескі дискіден сұрауды кэштеу мысалын қарастырайық. Бұл жұмыс принципін өзгертуге болмайтын ежелгі қолданбадағы электр пойыздарының кестесі болсын. Жаңартылған кестесі бар диск күн сайын белгіленген уақытта енгізіледі. Сонымен, бізде:- Интерфейс
TimetableTrains
. TimetableElectricTrains
Бұл интерфейсті жүзеге асыратын класс .- Дәл осы класс арқылы клиент codeы дискінің файлдық жүйесімен әрекеттеседі.
- Клиент класы
DisplayTimetable
. Оның әдістемесіprintTimetable()
әдістерін пайдаланадыTimetableElectricTrains
.
printTimetable()
сынып TimetableElectricTrains
дискіге кіріп, деректерді түсіреді және оны клиентке береді. Бұл жүйе жақсы жұмыс істейді, бірақ өте баяу. Сондықтан кэштеу механизмін қосу арқылы жүйе өнімділігін арттыру туралы шешім қабылданды. Мұны прокси үлгісін пайдалану арқылы жасауға болады: Осылайша сынып алдыңғы DisplayTimetable
сыныппен емес, сыныппен өзара әрекеттесетінін байқамайды . TimetableElectricTrainsProxy
Жаңа енгізу кестені күніне бір рет жүктейді және қайталанатын сұраулар бойынша бұрыннан жүктелген нысанды жадтан қайтарады.
Проксиді қандай тапсырмалар үшін қолданған дұрыс?
Міне, бұл үлгі міндетті түрде пайдалы болатын бірнеше жағдайлар:- Кэштеу.
- Жалқау іске асыру жалқау іске асыру ретінде де белгілі. Неліктен an objectіні қажетінше жүктей алатын болсаңыз, оны бірден жүктеңіз?
- Тіркеу сұраулары.
- Аралық деректер мен қол жеткізуді тексеру.
- Параллельді өңдеу ағындарын іске қосу.
- Қоңырау тарихын жазу немесе санау.
Артылықшылықтар мен кемшіліктер
- + Қызмет көрсету an objectісіне қол жеткізуді өз қалауыңыз бойынша басқара аласыз;
- + Қызмет көрсету an objectісінің өмірлік циклін басқарудың қосымша мүмкіндіктері;
- + Қызмет көрсету an objectісісіз жұмыс істейді;
- + Код өнімділігі мен қауіпсіздігін жақсартады.
- - қосымша емдеуге байланысты өнімділіктің нашарлау қаупі бар;
- - Бағдарлама сабақтарының құрылымын қиындатады.
Тәжірибедегі алмастыру үлгісі
Пойыз кестелерін дискіден оқитын жүйені енгізейік:public interface TimetableTrains {
String[] getTimetable();
String getTrainDepartureTime();
}
Негізгі интерфейсті жүзеге асыратын класс:
public class TimetableElectricTrains implements TimetableTrains {
@Override
public String[] getTimetable() {
ArrayList<String> list = new ArrayList<>();
try {
Scanner scanner = new Scanner(new FileReader(new File("/tmp/electric_trains.csv")));
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
list.add(line);
}
} catch (IOException e) {
System.err.println("Error: " + e);
}
return list.toArray(new String[list.size()]);
}
@Override
public String getTrainDepartureTime(String trainId) {
String[] timetable = getTimetable();
for(int i = 0; i<timetable.length; i++) {
if(timetable[i].startsWith(trainId+";")) return timetable[i];
}
return "";
}
}
Сіз барлық пойыздардың кестесін алуға тырысқан сайын, бағдарлама файлды дискіден оқиды. Бірақ бұл әлі де гүлдер. Файл сонымен қатар бір пойыздың кестесін алу қажет болған сайын оқылады! Мұндай code тек нашар мысалдарда болғаны жақсы :) Клиент класы:
public class DisplayTimetable {
private TimetableTrains timetableTrains = new TimetableElectricTrains();
public void printTimetable() {
String[] timetable = timetableTrains.getTimetable();
String[] tmpArr;
System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути");
for(int i = 0; i < timetable.length; i++) {
tmpArr = timetable[i].split(";");
System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]);
}
}
}
Мысал файл:
9B-6854;Лондон;Прага;13:43;21:15;07:32
BA-1404;Париж;Грац;14:25;21:25;07:00
9B-8710;Прага;Вена;04:48;08:49;04:01;
9B-8122;Прага;Грац;04:48;08:49;04:01
сынап көрейік:
public static void main(String[] args) {
DisplayTimetable displayTimetable = new DisplayTimetable();
displayTimetable.printTimetable();
}
Қорытынды:
Поезд Откуда Куда Время отправления Время прибытия Время в пути
9B-6854 Лондон Прага 13:43 21:15 07:32
BA-1404 Париж Грац 14:25 21:25 07:00
9B-8710 Прага Вена 04:48 08:49 04:01
9B-8122 Прага Грац 04:48 08:49 04:01
Енді үлгімізді жүзеге асырудың қадамдарын қарастырайық:
-
Бастапқы нысанның орнына жаңа проксиді пайдалануға мүмкіндік беретін интерфейсті анықтаңыз. Біздің мысалда бұл
TimetableTrains
. -
Прокси класын жасаңыз. Онда қызмет көрсету an objectісіне сілтеме болуы керек (сыныпта жасау немесе конструкторда өту);
Міне, біздің прокси классымыз:
public class TimetableElectricTrainsProxy implements TimetableTrains { // Ссылка на оригинальный an object private TimetableTrains timetableTrains = new TimetableElectricTrains(); private String[] timetableCache = null @Override public String[] getTimetable() { return timetableTrains.getTimetable(); } @Override public String getTrainDepartureTime(String trainId) { return timetableTrains.getTrainDepartureTime(trainId); } public void clearCache() { timetableTrains = null; } }
Бұл кезеңде біз жай ғана бастапқы нысанға сілтеме жасайтын класс жасаймыз және оған барлық қоңырауларды жібереміз.
-
Біз прокси класының логикасын жүзеге асырамыз. Негізінде қоңырау әрқашан бастапқы нысанға қайта бағытталады.
public class TimetableElectricTrainsProxy implements TimetableTrains { // Ссылка на оригинальный an object private TimetableTrains timetableTrains = new TimetableElectricTrains(); private String[] timetableCache = null @Override public String[] getTimetable() { if(timetableCache == null) { timetableCache = timetableTrains.getTimetable(); } return timetableCache; } @Override public String getTrainDepartureTime(String trainId) { if(timetableCache == null) { timetableCache = timetableTrains.getTimetable(); } for(int i = 0; i < timetableCache.length; i++) { if(timetableCache[i].startsWith(trainId+";")) return timetableCache[i]; } return ""; } public void clearCache() { timetableTrains = null; } }
Әдіс
getTimetable()
кесте массивінің жадта кэштелгенін тексереді. Олай болмаса, ол нәтижені сақтай отырып, дискіден деректерді жүктеуге сұраныс береді. Егер сұрау әлдеқашан іске қосылған болса, ол жадтан нысанды жылдам қайтарады.Қарапайым функционалдығының арқасында getTrainDepartireTime() әдісін бастапқы нысанға қайта бағыттау қажет болмады. Біз оның функционалдығын жаңа әдіске көшірдік.
Сіз мұны істей алмайсыз. Егер сізге codeты көшіру немесе ұқсас манипуляцияларды орындау қажет болса, бұл бірдеңе дұрыс болмады және мәселені басқа бұрыштан қарау керек дегенді білдіреді. Біздің қарапайым мысалда басқа жол жоқ, бірақ нақты жобаларда code дұрысырақ жазылуы мүмкін.
-
Клиент codeындағы бастапқы нысанның жасалуын ауыстыру нысанымен ауыстырыңыз:
public class DisplayTimetable { // Измененная link private TimetableTrains timetableTrains = new TimetableElectricTrainsProxy(); public void printTimetable() { String[] timetable = timetableTrains.getTimetable(); String[] tmpArr; System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути"); for(int i = 0; i<timetable.length; i++) { tmpArr = timetable[i].split(";"); System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]); } } }
Емтихан
Поезд Откуда Куда Время отправления Время прибытия Время в пути 9B-6854 Лондон Прага 13:43 21:15 07:32 BA-1404 Париж Грац 14:25 21:25 07:00 9B-8710 Прага Вена 04:48 08:49 04:01 9B-8122 Прага Грац 04:48 08:49 04:01
Керемет, ол дұрыс жұмыс істейді.
Сондай-ақ белгілі бір шарттарға байланысты бастапқы нысанды да, ауыстыру нысанын да жасайтын зауытты қарастыруға болады.
Нүктенің орнына пайдалы сілтеме
-
Үлгілер туралы тамаша мақала және «Депутат» туралы аздап
GO TO FULL VERSION