Deputat nəyə lazımdır?
Bu nümunə obyektə nəzarət edilən girişlə bağlı problemləri həll etməyə kömək edir. Sualınız ola bilər: "Niyə bizə belə idarə olunan giriş lazımdır?" Nəyin nə olduğunu anlamağa kömək edəcək bir neçə vəziyyətə baxaq.Misal 1
Təsəvvür edək ki, bir dəstə köhnə kodu olan böyük bir layihəmiz var, burada verilənlər bazasından hesabatların yüklənməsinə cavabdeh olan bir sinif var. Sinf sinxron işləyir, yəni verilənlər bazası sorğunu emal edərkən bütün sistem boşdur. Orta hesabla hesabat 30 dəqiqə ərzində hazırlanır. Bu xüsusiyyətə görə onun yüklənməsi saat 00:30-da başlayır və rəhbərlik bu hesabatı səhər alır. Təhlil zamanı məlum oldu ki, hesabat yaradılandan dərhal sonra, yəni bir gün ərzində qəbul edilməlidir. Başlama vaxtını dəyişdirmək mümkün deyil, çünki sistem verilənlər bazasından cavab gözləyəcək. Həll yolu, yükləmə və hesabat yaratmağa ayrı bir başlıqda başlamaqla iş prinsipini dəyişdirməkdir. Bu həll sistemin həmişəki kimi işləməsinə imkan verəcək və rəhbərlik yeni hesabatlar alacaq. Bununla belə, bir problem var: mövcud kodu yenidən yazmaq mümkün deyil, çünki onun funksiyaları sistemin digər hissələri tərəfindən istifadə olunur. Bu halda, hesabat yükləmək, başlanğıc vaxtını qeyd etmək və ayrı bir mövzu açmaq üçün sorğu alacaq Müavin nümunəsindən istifadə edərək aralıq proxy sinfi təqdim edə bilərsiniz. Hesabat yaradıldıqda ip öz işini tamamlayacaq və hər kəs xoşbəxt olacaq.Misal 2
İnkişaf qrupu poster veb saytı yaradır. Yeni hadisələr haqqında məlumat əldə etmək üçün onlar üçüncü tərəf xidmətinə müraciət edirlər, onunla qarşılıqlı əlaqə xüsusi qapalı kitabxana vasitəsilə həyata keçirilir. İnkişaf zamanı bir problem yarandı: üçüncü tərəf sistemi məlumatları gündə bir dəfə yeniləyir və istifadəçi səhifəni hər dəfə yenilədikdə ona sorğu baş verir. Bu, çoxlu sayda sorğu yaradır və xidmət cavab verməyi dayandırır. Həll yolu xidmətin cavabını keşləmək və ziyarətçilərə hər yenidən yükləmədə yadda qalan nəticəni təqdim etmək, lazım gəldikdə bu keşi yeniləməkdir. Bu vəziyyətdə, Müavin nümunəsindən istifadə, bitmiş funksionallığı dəyişdirmədən əla bir həlldir.Nümunə necə işləyir
Bu nümunəni həyata keçirmək üçün bir proxy sinfi yaratmalısınız. O, müştəri kodu üçün davranışını simulyasiya edən xidmət sinfi interfeysini həyata keçirir. Beləliklə, müştəri real obyektin əvəzinə onun proxy ilə qarşılıqlı əlaqədə olur. Tipik olaraq, bütün sorğular xidmət sinfinə ötürülür, lakin onun çağırışından əvvəl və ya sonra əlavə tədbirlərlə. Sadə dillə desək, bu proksi obyekt müştəri kodu ilə hədəf obyekt arasında bir təbəqədir. Çox yavaş köhnə diskdən sorğunun keşləşdirilməsi nümunəsinə baxaq. İş prinsipi dəyişdirilə bilməyən bəzi qədim tətbiqlərdə elektrik qatarlarının cədvəli olsun. Yenilənmiş cədvəli olan disk hər gün müəyyən edilmiş vaxtda daxil edilir. Beləliklə, bizdə:- İnterfeys
TimetableTrains
. TimetableElectricTrains
Bu interfeysi həyata keçirən sinif .- Məhz bu sinif vasitəsilə müştəri kodu disk fayl sistemi ilə qarşılıqlı əlaqədə olur.
- Müştəri sinfi
DisplayTimetable
. Onun metoduprintTimetable()
metodlarından istifadə edirTimetableElectricTrains
.
printTimetable()
sinif TimetableElectricTrains
diskə daxil olur, məlumatları boşaldır və müştəriyə təqdim edir. Bu sistem yaxşı işləyir, lakin çox yavaşdır. Buna görə də, keşləmə mexanizmini əlavə etməklə sistem performansını artırmaq qərara alındı. Bu, Proksi nümunəsindən istifadə etməklə edilə bilər: Bu yolla sinif əvvəlki ilə deyil, DisplayTimetable
siniflə qarşılıqlı əlaqədə olduğunu belə hiss etməyəcək . TimetableElectricTrainsProxy
Yeni tətbiq cədvəli gündə bir dəfə yükləyir və təkrar sorğular zamanı artıq yüklənmiş obyekti yaddaşdan qaytarır.
Proksidən hansı vəzifələr üçün istifadə etmək daha yaxşıdır?
Bu nümunənin mütləq faydalı olacağı bir neçə vəziyyət var:- Keşləmə.
- Tənbəl həyata keçirmə tənbəl tətbiq kimi də tanınır. Ehtiyac olduqda onu yükləyə bildiyiniz halda, niyə bir obyekti birdən yükləyin?
- Giriş sorğuları.
- Aralıq məlumatlar və giriş yoxlamaları.
- Paralel emal iplərinin işə salınması.
- Zəng tarixçəsinin qeyd edilməsi və ya sayılması.
Yaxşı və pis tərəfləri
- + Xidmət obyektinə girişi istədiyiniz kimi idarə edə bilərsiniz;
- + Xidmət obyektinin həyat dövrünü idarə etmək üçün əlavə imkanlar;
- + Xidmət obyekti olmadan işləyir;
- + Kod performansını və təhlükəsizliyini artırır.
- - Əlavə müalicələrə görə performansın pisləşməsi riski var;
- - Proqram dərslərinin strukturunu çətinləşdirir.
Təcrübədə əvəzedici nümunə
Gəlin sizinlə diskdən qatar cədvəllərini oxuyan bir sistem tətbiq edək:public interface TimetableTrains {
String[] getTimetable();
String getTrainDepartureTime();
}
Əsas interfeysi həyata keçirən sinif:
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 "";
}
}
Hər dəfə bütün qatarların cədvəlini almağa çalışdığınız zaman proqram diskdən faylı oxuyur. Ancaq bunlar hələ də çiçəklərdir. Fayl yalnız bir qatarın cədvəlini əldə etmək üçün hər dəfə oxunur! Belə kodun yalnız pis nümunələrdə olması yaxşıdır :) Müştəri sinfi:
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]);
}
}
}
Nümunə fayl:
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
test edək:
public static void main(String[] args) {
DisplayTimetable displayTimetable = new DisplayTimetable();
displayTimetable.printTimetable();
}
Nəticə:
Поезд Откуда Куда Время отправления Время прибытия Время в пути
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
İndi nümunəmizi həyata keçirmək üçün addımlardan keçək:
-
Orijinal obyekt əvəzinə yeni proksi istifadə etməyə imkan verən interfeys təyin edin. Bizim nümunəmizdə belədir
TimetableTrains
. -
Proksi sinfi yaradın. O, xidmət obyektinə istinaddan ibarət olmalıdır (sinifdə yaradın və ya konstruktorda keçin);
Budur bizim proxy sinifimiz:
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; } }
Bu mərhələdə biz sadəcə olaraq orijinal obyektə istinadla sinif yaradırıq və bütün zəngləri ona ötürürük.
-
Proxy sinfinin məntiqini həyata keçiririk. Əsasən zəng həmişə orijinal obyektə yönləndirilir.
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; } }
Metod
getTimetable()
cədvəl massivinin yaddaşda saxlanıb saxlanmadığını yoxlayır. Əgər yoxsa, o, nəticəni saxlayaraq diskdən məlumat yükləmək üçün sorğu verir. Əgər sorğu artıq işləyirsə, o, tez yaddaşdan obyekti qaytaracaq.Sadə funksionallığı sayəsində getTrainDepartireTime() metodunu orijinal obyektə yönləndirmək lazım deyildi. Biz sadəcə onun funksionallığını yeni bir üsula təkrarladıq.
Siz bunu edə bilməzsiniz. Əgər kodun dublikatını çıxartmalı və ya oxşar manipulyasiyalar etməlisinizsə, bu, nəyinsə səhv getdiyini və problemə başqa bucaqdan baxmaq lazım olduğunu bildirir. Sadə nümunəmizdə başqa yol yoxdur, amma real layihələrdə çox güman ki, kod daha düzgün yazılacaq.
-
Müştəri kodunda orijinal obyektin yaradılmasını əvəzedici obyektlə əvəz edin:
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]); } } }
İmtahan
Поезд Откуда Куда Время отправления Время прибытия Время в пути 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
Əla, düzgün işləyir.
Müəyyən şərtlərdən asılı olaraq həm orijinal obyekt, həm də əvəzedici obyekt yaradacaq bir fabrik də nəzərdən keçirə bilərsiniz.
Nöqtə əvəzinə faydalı keçid
-
Nümunələr haqqında əla məqalə və bir az "Müavin" haqqında
GO TO FULL VERSION