To pytanie jest ściśle związane z programowaniem aspektowym . AspectJ jest rozszerzeniem AOP języka Java i możesz rzucić okiem, aby uzyskać trochę ispiracji.
O ile wiem, nie ma bezpośredniego wsparcia dla AOP w Javie. Jest kilka wzorców GOF, które się z nim wiążą, jak na przykład Template Method i Strategy, ale tak naprawdę nie zaoszczędzą one linii kodu.
W Javie i większości innych języków można zdefiniować powtarzającą się logikę, której potrzebujesz w funkcjach i zastosować tak zwane zdyscyplinowane podejście do kodowania, w którym wywołujesz je we właściwym czasie.
public void checkBalance() {
checkSomePrecondition();
...
checkSomePostcondition();
}
Jednak to nie pasowałoby do twojego przypadku, ponieważ chciałbyś, aby kod wyodrębniony z faktury mógł powrócić z checkBalance. W językach, które obsługują makra (jak C / C ++), można zdefiniować checkSomePreconditioni checkSomePostconditionjako makra, a zostaną one po prostu zastąpione przez preprocesor, zanim jeszcze zostanie wywołany kompilator:
#define checkSomePrecondition \
if (!fooIsEnabled) return;
Java nie ma tego po wyjęciu z pudełka. Może to kogoś urazić, ale w przeszłości korzystałem z automatycznego generowania kodu i silników szablonów, aby zautomatyzować powtarzalne zadania związane z kodowaniem. Jeśli przetwarzasz pliki Java przed skompilowaniem ich za pomocą odpowiedniego preprocesora, na przykład Jinja2, możesz zrobić coś podobnego do tego, co jest możliwe w C.
Możliwe podejście w czystej Javie
Jeśli szukasz czystego rozwiązania w języku Java, to, co możesz znaleźć, prawdopodobnie nie będzie zwięzłe. Ale nadal może uwzględniać wspólne części programu i unikać powielania kodu i błędów. Mógłbyś zrobić coś takiego (jest to jakiś wzorzec inspirowany strategią ). Zwróć uwagę, że w C # i Javie 8 oraz w innych językach, w których funkcje są nieco łatwiejsze w obsłudze, takie podejście może faktycznie wyglądać ładnie.
public interface Code {
void execute();
}
...
public class Foo {
private bool fooIsEnabled;
private void protect(Code c) {
if (!fooIsEnabled) return;
c.execute();
}
public void bar() {
protect(new Code {
public void execute() {
System.out.println("bar");
}
});
}
public void baz() {
protect(new Code {
public void execute() {
System.out.println("baz");
}
});
}
public void bat() {
protect(new Code {
public void execute() {
System.out.println("bat");
}
});
}
}
Coś w rodzaju prawdziwego scenariusza
Tworzysz klasę do wysyłania ramek danych do robota przemysłowego. Wykonanie polecenia wymaga czasu. Po wykonaniu polecenia wysyła z powrotem ramkę sterującą. Robot może ulec uszkodzeniu, jeśli otrzyma nowe polecenie, podczas gdy poprzednie jest nadal wykonywane. Twój program używa DataLinkklasy do wysyłania i odbierania ramek do i od robota. Musisz zabezpieczyć dostęp doDataLink instancji.
Połączenia gwintowe interfejs użytkownika RobotController.left, right, uplub downgdy użytkownik kliknie przycisków, ale również wywołuje BaseController.tickw regularnych odstępach czasu, w celu przekazywania poleceń ponownie włączyć do prywatnej DataLinkinstancji.
interface Code {
void ready(DataLink dataLink);
}
class BaseController {
private DataLink mDataLink;
private boolean mReady = false;
private Queue<Code> mEnqueued = new LinkedList<Code>();
public BaseController(DataLink dl) {
mDataLink = dl;
}
protected void protect(Code c) {
if (mReady) {
mReady = false;
c.ready(mDataLink);
}
else {
mEnqueue.add(c);
}
}
public void tick() {
byte[] frame = mDataLink.readWithTimeout(/* Not more than 50 ms */);
if (frame != null && /* Check that it's an ACK frame */) {
if (mEnqueued.isEmpty()) {
mReady = true;
}
else {
Code c = mEnqueued.remove();
c.ready(mDataLink);
}
}
}
}
class RobotController extends BaseController {
public void left(float amount) {
protect(new Code() { public void ready(DataLink dataLink) {
dataLink.write(/* Create a byte[] that means 'left' by amount */);
}});
}
public void right(float amount) {
protect(new Code() { public void ready(DataLink dataLink) {
dataLink.write(/* Create a byte[] that means 'right' by amount */);
}});
}
public void up(float amount) {
protect(new Code() { public void ready(DataLink dataLink) {
dataLink.write(/* Create a byte[] that means 'up' by amount */);
}});
}
public void down(float amount) {
protect(new Code() { public void ready(DataLink dataLink) {
dataLink.write(/* Create a byte[] that means 'down' by amount */);
}});
}
}