Це питання тісно пов'язане з аспектно-орієнтованим програмуванням . AspectJ - це розширення AOP Java на AOP, і ви можете поглянути на це, щоб отримати деяку іспірацію.
Наскільки я знаю, немає прямої підтримки AOP на Java. Існують деякі шаблони GOF, які пов'язані з нею, наприклад, метод шаблону та стратегія шаблону, але він насправді не збереже рядки коду.
У Java та більшості інших мов ви можете визначити необхідну для функцій повторювану логіку та прийняти так званий дисциплінований підхід кодування, за допомогою якого ви викликаєте їх у потрібний час.
public void checkBalance() {
checkSomePrecondition();
...
checkSomePostcondition();
}
Однак це не відповідатиме вашому випадку, тому що ви хочете, щоб код, який вийшов із фактури, міг повернутися з нього checkBalance
. На мовах, що підтримують макроси (наприклад, C / C ++), ви можете визначити checkSomePrecondition
і checkSomePostcondition
як макроси, і вони будуть просто замінені препроцесором до того, як компілятор навіть буде викликаний:
#define checkSomePrecondition \
if (!fooIsEnabled) return;
У Java цього немає. Це може образити когось, але я використовував автоматичне генерування коду та шаблонні двигуни для автоматизації повторюваних завдань кодування в минулому. Якщо ви обробляєте ваші Java-файли, перш ніж компілювати їх з відповідним препроцесором, наприклад, Jinja2, ви можете зробити щось подібне до того, що можливо в C.
Можливий чистий підхід до Java
Якщо ви шукаєте чисте рішення Java, те, що ви, можливо, знайдете, не буде стислим. Але це все ще може визначити загальні частини вашої програми та уникнути дублювання коду та помилок. Ви можете зробити щось подібне (це якась стратегія- натхненна модель). Зауважте, що в C # і Java 8, а також в інших мовах, якими функціями трохи легше керувати, такий підхід може насправді виглядати приємно.
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");
}
});
}
}
Своєрідний сценарій у реальному світі
Ви розробляєте клас для надсилання кадрів даних промисловому роботу. Роботу потрібен час, щоб виконати команду. Після того, як команда буде виконана, вона поверне вам керуючий кадр назад. Робот може отримати пошкодження, якщо він отримає нову команду, поки виконується попередня. Ваша програма використовує DataLink
клас для надсилання та отримання кадрів на робот і з нього. Вам потрібно захистити доступ до DataLink
екземпляра.
Інтерфейс нитка виклики RobotController.left
, right
, up
або down
коли користувач натискає на кнопки, а й виклики BaseController.tick
через регулярні проміжки часу, для того , щоб експедиції повторного включення команди в приватному DataLink
випадку.
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 */);
}});
}
}