Article View: pl.comp.objects
Article #17249Re: Composition Filters [uwaga dlugie]
From: Adam Przybylek
Date: Thu, 16 Aug 2007 16:28
Date: Thu, 16 Aug 2007 16:28
418 lines
12432 bytes
12432 bytes
Kamil Janiszewski napisa?(a):
> Zobaczymy jakie nowosci przyniesie przyszla wypowiedz Pana Adama
> Przybylka.
No wie;c skoro obieca?em to pisze;. W poprzednim pos'cie mówi?em, z.e
znalaz?em cze;s'ciowo odpowiedz' na pytanie dlaczego CFs sie; nie
upowszechni?y. Otóz. celem CFs by?o dostarczenie mechanizmu
umoz.liwiaja;cego lepsza; separacje; zagadnien' (SoC - Separation of
Concerns) niz. w paradygmacie obiektowym. Moje zdanie jest takie, z.e
wykorzystuja;c CFs dla niektórych przypadków moz.na osia;gna;c' lepsza; SoC,
ale nie ma ona wie;kszego wp?ywu na poprawe; evolvability i reusability.
Poniz.ej przyk?ad na problemy jakie powstaja; przy implementacji w Javie
oraz próba (bezowocna) rozwia;zania ich przy pomocy dodania do Javy
filtrów input (hipotetyczna sk?adnia).
Za?óz.my, z.e tworza;c nowy modu? chcemy w jak najwie;kszym stopniu
wykorzystac' istnieja;ce modu?y. Ponadto na kaz.dym etapie chcemy zrobic'
dobra; implementacje; pod aktualne wymagania, a nie pod ewentualne
przysz?e, o których nic nie wiemy. Zapominamy o istnieniu gotowych
pakietów Javy. Za?óz.my takz.e, z.e posiadamy w repozytorium naste;puja;ca;
implementacje; kolejki:
public class Queue {
protected Object[] buf;
protected int numItems;
protected int nextToRemove;
public Queue(int capacity) {
buf = new Object[capacity];
}
public boolean isFull() {
return (numItems==buf.length);
}
public boolean isEmpty() {
return (numItems==0);
}
public int size() {
return numItems;
}
public boolean put(Object x) {
if ( isFull() ) return false;
buf[(nextToRemove+numItems) % buf.length] = x;
numItems++;
return true;
}
public Object get() {
Object tmp=null;
if ( !isEmpty() ) {
tmp = buf[nextToRemove];
numItems--;
nextToRemove = (nextToRemove+1) % buf.length;
}
return tmp;
}
}
Etap 1. Nalez.y zaimplementowac' klase; Buffer pod klasyczny problem
producenta-konsumenta.
a) rozwia;zanie Java
public class Buffer extends Queue {
public Buffer(int capacity) {
super(capacity);
}
public synchronized boolean put(Object x) {
while ( isFull() ) try {
wait();
} catch (InterruptedException e) {}
super.put(x);
notifyAll();
return true;
}
public synchronized Object get() {
while ( isEmpty() ) try {
wait();
} catch (InterruptedException e) {}
Object tmp = super.get();
notifyAll();
return tmp;
}
}
Nalez.y zauwaz.yc', z.e zagadnienie synchronizacji (zagadnienie
przecinaja;ce) jest splecione z zagadnieniem odk?adania i pobierania
danych (zagadnienie podstawowe).
b) rozwia;zanie CFs
public class BufferCF {
private Queue delegate;
filter Wait {
delegate.isFull() => put(Object),
delegate.isEmpty() => get()
};
filter Dispatch { true => delegate.* };
}
Dzia?a to w ten sposób, z.e kaz.dy komunikat przychodza;cy z zewna;trz do
obiektu przechodzi najpierw przez filtr Wait, a naste;pnie przez filtr
Dispatch. Pierwszy wpis w filtrze Wait mówi, aby us'pic' wa;tek wykonuja;cy
put(Object) na pe?nym buforze, a drugi, aby us'pic' wa;tek wykonuja;cy get()
na pustym buforze.
Na?oz.enie na metode; filtru Wait sprawia, z.e staje sie; ona synchroniczna.
Ponadto sprawdzenie warunku (równiez. synchroniczne) i ewentualne
rozpocze;cie metody jest atomowe.
Z kolei zastosowany filtr Dispatch mówi, aby kaz.dy komunikat (*)
bezwarunkowo (true) przekazac' do obs?uz.enia obiektowi delegate. Gdyby
filtr Dispatch zawiera? wie;cej wpisów rozpatrywane by by?y od lewej do
prawej tak d?ugo az. skojarzony z któryms' warunek be;dzie spe?niony. Jes'li
z.aden warunek nie zostanie spe?niony to rzucany jest run-time error.
Etap 2. Przychodzi nowe wymaganie - dla kaz.dego elementu wk?adanego do
bufora nalez.y pamie;tac' date; kiedy zosta? on w?oz.ony. Ponadto umoz.liwic'
pobranie elementu razem z ta; data;.
a) rozwia;zanie Java
public class HistoryBuffer extends Buffer {
protected Queue delegateDates;
public HistoryBuffer(int capacity) {
super(capacity);
delegateDates = new Queue(capacity);
}
public synchronized boolean put(Object x) {
while ( isFull() ) try {
wait();
} catch (InterruptedException e) {}
super.put(x);
delegateDates.put( new Date() );
notifyAll();
return true;
}
public synchronized Object get() {
while ( isEmpty() ) try {
wait();
} catch (InterruptedException e) {}
Object tmp = super.get();
delegateDates.get();
notifyAll();
return tmp;
}
public synchronized Pair getWithDate() {
while ( isEmpty() ) try {
wait();
} catch (InterruptedException e) {}
Pair tmp = new Pair( super.get(), delegateDates.get() );
notifyAll();
return tmp;
}
}
b) rozwiazanie CFs
public class HistoryBufferCF {
private Queue delegateObjects;
private Queue delegateDates;
public HistoryBufferCF(int capacity) {
delegateObjects = new Queue(capacity);
delegateDates = new Queue(capacity);
}
public void put(Object x) {
delegateObjects.put(x);
delegateDates.put(new Date());
}
public Object get() {
delegateDates.get();
return delegateObjects.get();
}
public Pair getWithDate() {
return new Pair(delegateObjects.get(),delegateDates.get());
}
filter Wait {
delegateObjects.isFull() => put(Object),
delegateObjects.isEmpty() => { get(), getWithDate() }
};
filter Dispatch { true => this.*, true => delegateObjects.* };
}
Etap 3. Na potrzeby monitoringu nalez.y logowac' informacje o stanie
bufora po kaz.dej transakcji.
a) rozwia;zanie Java
public class LogHistoryBuffer extends HistoryBuffer {
public LogHistoryBuffer(int capacity) {
super(capacity);
}
protected void log() {
System.out.println("stan bufora: "+size());
}
public synchronized boolean put(Object x) {
super.put(x);
log();
return true;
}
public synchronized Object get() {
Object tmp = super.get();
log();
return tmp;
}
public synchronized Pair getWithDate() {
Pair tmp = super.getWithDate();
log();
return tmp;
}
}
b) rozwia;zanie CFs
public class LogHistoryBufferCF {
private HistoryBufferCF delegate;
private void log() {
System.out.println("stan bufora: "+delegate.size());
}
public void put(Object x) {
delegate.put(x);
log();
}
public Object get() {
Object tmp = delegate.get();
log();
return tmp;
}
public Pair getWithDate() {
Pair tmp = delegate.getWithDate();
log();
return tmp;
}
filter Wait {
delegate.isFull() => put(Object),
delegate.isEmpty() => { get(), getWithDate() }
};
filter Dispatch { true => this.*, true => delegate.* };
}
W obu implementacjach zagadnienie logowania (zagadnienie przecinaja;ce)
przeplatane jest z zagadnieniem podstawowym.
Etap 4. Stworzyc' moz.liwos'c' pobrania n kolejnych elementów w postaci
tablicy. Pobieranie rozpocza;c' jes'li istnieje w buforze przynajmniej
jeden element. Logowanie ma byc' pojedyncze dla kaz.dej transakcji
(pobranie tablicy n elementów be;dzie logowane tylko raz).
a) rozwia;zanie Java
public class EnhancedLogHistoryBuffer extends HistoryBuffer {
protected boolean lock;
public EnhancedLogHistoryBuffer(int capacity) {
super(capacity);
}
protected void lock(boolean b) {
lock = b;
}
protected boolean isLock() {
return lock;
}
protected void log() {
System.out.println("stan bufora: "+size());
}
public synchronized boolean put(Object x) {
super.put(x);
log();
return true;
}
public synchronized Object get() {
while ( isEmpty()||isLock() ) try {
wait();
} catch (InterruptedException e) {}
Object tmp = super.get();
log();
return tmp;
}
public synchronized Pair getWithDate() {
while ( isEmpty()||isLock() ) try {
wait();
} catch (InterruptedException e) {}
Pair tmp = super.getWithDate();
log();
return tmp;
}
public synchronized Object[] get(int n) {
while ( isEmpty()||isLock() ) try {
wait();
} catch (InterruptedException e) {}
lock(true);
Object[] tmp = new Object[n];
for(int i=0; i<n; i++) {
tmp[i] = super.get();
dates.get();
}
lock(false);
log();
return tmp;
}
public synchronized Pair[] getWithDate(int n) {
while ( isEmpty()||isLock() ) try {
wait();
} catch (InterruptedException e) {}
lock(true);
Pair[] tmp = new Pair[n];
for(int i=0; i<n; i++) tmp[i] = super.getWithDate();
lock(false);
log();
return tmp;
}
}
Implementuja;c operacje get(int) i getWithDate(int) nie moz.na wykorzystac'
implementacji get() i getWithDate() z klasy LogHistoryBuffer poniewaz. sa;
one splecione z zagadnieniem logowania. Poniewaz. w Javie nie ma
moz.liwos'ci, aby niepolimorficznie wywo?ac' metode; zdefiniowana; w
okres'lonej nadklasie, nalez.y ponownie za punkt wyjs'cia przyja;c' klase;
HistoryBuffer. Takie rozwia;zanie wymaga ponownego zaimplementowania
metody log.
b) rozwia;zanie CFs
public class EnhancedLogHistoryBufferCF {
private HistoryBufferCF delegate;
private boolean lock;
public EnhancedLogHistoryBufferCF(int capacity) {
delegate = new HistoryBufferCF(capacity);
}
public void lock(boolean b) {
lock = b;
}
public boolean isLock() {
return lock;
}
private void log() {
System.out.println("stan bufora: "+delegate.size());
}
public void put(Object x) {
delegate.put(x);
log();
}
public Object get() {
Object tmp = delegate.get();
log();
return tmp;
}
public Pair getWithDate() {
Pair tmp = delegate.getWithDate();
log();
return tmp;
}
public Object[] get(int n) {
lock(true);
Object[] tmp = new Object[n];
for(int i=0; i<n; i++) tmp[i] = delegate.get();
lock(false);
log();
return tmp;
}
public Pair[] getWithDate(int n) {
lock(true);
Pair[] tmp = new Pair[n];
for(int i=0; i<n; i++) tmp[i] = delegate.getWithDate();
lock(false);
log();
return tmp;
}
filter Wait {
delegate.isFull() => put(Object),
delegate.isEmpty() || isLock() => { get(), getWithDate(), get(int),
getWithDate(int) }
};
filter Dispatch { true => this.*, true => delegate.* };
}
Podobnie jak w przypadku rozwia;zania w Javie, trzeba ponownie
zaimplementowac' zagadnienie logowania. Ponadto zagadnienie
synchronizacji poprzez metode; lock zosta?o splecione z zagadnieniem
podstawowym.
***
Wszystkie przedstawione problemy zwia;zane z przeplataniem zagadnien'
moz.na rozwia;zac' za pomoca; AspectJ, a cze;s'c' z nich w klasycznym je;zyku OO
umoz.liwiaja;cym niepolimorficzne wo?anie metody.
Pozdrawiam,
Adam
Technical Details
Message-ID:
<fa1tpl$vru$2@news.onet.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.onet.pl!not-for-mailReferences:
<f738su$kn$1@news.onet.pl> <c68j93p3t4k797gp8cimgdifl25q1on43j@4ax.com> <1186094704.244462.260290@e9g2000prf.googlegroups.com> <c8q4b3d5kr5245mvpe7frtu5siik750r0n@4ax.com> <1186147569.379046.72670@r34g2000hsd.googlegroups.com>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.onet.pl!not-for-mail From: Adam Przybylek <adampap1@vp.pl> Newsgroups: pl.comp.objects Subject: Re: Composition Filters [uwaga dlugie] Date: Thu, 16 Aug 2007 18:28:42 +0200 Organization: Onet.pl Lines: 421 Message-ID: <fa1tpl$vru$2@news.onet.pl> References: <f738su$kn$1@news.onet.pl> <c68j93p3t4k797gp8cimgdifl25q1on43j@4ax.com> <1186094704.244462.260290@e9g2000prf.googlegroups.com> <c8q4b3d5kr5245mvpe7frtu5siik750r0n@4ax.com> <1186147569.379046.72670@r34g2000hsd.googlegroups.com> NNTP-Posting-Host: 81.210.34.66 Mime-Version: 1.0 X-Trace: news.onet.pl 1187281526 32638 81.210.34.66 (16 Aug 2007 16:25:26 GMT) X-Complaints-To: usenet@news.onet.pl NNTP-Posting-Date: Thu, 16 Aug 2007 16:25:26 +0000 (UTC) X-Sender: 7oaVUrxaL5umdVGseRDCgA== In-Reply-To: <1186147569.379046.72670@r34g2000hsd.googlegroups.com> User-Agent: Thunderbird 1.5.0.8 (Windows/20061025) Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit