Есть ли элегантный способ заставить каждый метод в классе начать с определенного блока кода?

У меня есть класс, где каждый метод запускается таким же образом:

class Foo {
  public void bar() {
    if (!fooIsEnabled) return;
    //...
  }
  public void baz() {
    if (!fooIsEnabled) return;
    //...
  }
  public void bat() {
    if (!fooIsEnabled) return;
    //...
  }
}

Есть ли хороший способ требовать (и, надеюсь, не писать каждый раз) часть fooIsEnabled для каждого общедоступного метода в классе?

Ответ 1

Я не знаю об элегантности, но вот рабочая реализация с использованием встроенного в Java java.lang.reflect.Proxy, который обеспечивает, что все вызовы методов на Foo начинаются с проверки состояния enabled.

main:

public static void main(String[] args) {
    Foo foo = Foo.newFoo();
    foo.setEnabled(false);
    foo.bar(); // won't print anything.
    foo.setEnabled(true);
    foo.bar(); // prints "Executing method bar"
}
Интерфейс

Foo:

public interface Foo {
    boolean getEnabled();
    void setEnabled(boolean enable);

    void bar();
    void baz();
    void bat();

    // Needs Java 8 to have this convenience method here.
    static Foo newFoo() {
        FooFactory fooFactory = new FooFactory();
        return fooFactory.makeFoo();
    }
}

FooFactory класс:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class FooFactory {

    public Foo makeFoo() {
        return (Foo) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class[]{Foo.class},
                new FooInvocationHandler(new FooImpl()));
    }

    private static class FooImpl implements Foo {
        private boolean enabled = false;

        @Override
        public boolean getEnabled() {
            return this.enabled;
        }

        @Override
        public void setEnabled(boolean enable) {
            this.enabled = enable;
        }

        @Override
        public void bar() {
            System.out.println("Executing method bar");
        }

        @Override
        public void baz() {
            System.out.println("Executing method baz");
        }

        @Override
        public void bat() {
            System.out.println("Executing method bat");
        }

    }

    private static class FooInvocationHandler implements InvocationHandler {

        private FooImpl fooImpl;

        public FooInvocationHandler(FooImpl fooImpl) {
            this.fooImpl = fooImpl;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Foo.class &&
                !method.getName().equals("getEnabled") &&
                !method.getName().equals("setEnabled")) {

                if (!this.fooImpl.getEnabled()) {
                    return null;
                }
            }

            return method.invoke(this.fooImpl, args);
        }
    }
}

Как отмечали другие, это кажется излишним для того, что вам нужно, если у вас есть только несколько способов беспокоиться.

Тем не менее, есть определенные преимущества:

  • Достигнуто определенное разделение проблем, поскольку реализации методов Foo не нужно беспокоиться о проблемах, связанных с проверкой enabled. Вместо этого, код метода должен только беспокоиться о том, что основной целью метода является, не более того.
  • Невозможно, чтобы невинный разработчик добавил новый метод в класс Foo и по ошибке "забыл" добавить проверку enabled. Поведение проверки enabled автоматически наследуется любым новым добавленным способом.
  • Если вам нужно добавить еще одну сквозную проблему или вам нужно улучшить проверку enabled, это очень просто сделать безопасно и в одном месте.
  • Приятно, что вы можете получить это AOP-подобное поведение со встроенной функциональностью Java. Вы не вынуждены интегрировать некоторые другие структуры, такие как Spring, хотя они также могут быть хорошими вариантами.

Чтобы быть справедливым, некоторые из недостатков:

  • Некоторый код реализации, который обрабатывает вызовы proxy, является уродливым. Некоторые также говорят, что наличие внутренних классов для предотвращения создания класса FooImpl является уродливым.
  • Если вы хотите добавить новый метод в Foo, вам нужно внести изменения в 2 точки: класс реализации и интерфейс. Неважно, но это еще немного работает.
  • Запросы прокси не бесплатны. Накладные расходы определенные. Однако для общего использования это не будет заметно. Подробнее см. здесь.

EDIT:

Комментарий Fabian Streitel заставил меня задуматься о 2 неприятностях с моим решением выше, что, я соглашусь, я не доволен собой:

  • Обработчик вызова использует магические строки, чтобы пропустить "включенную проверку" в методах "getEnabled" и "setEnabled". Это можно легко сломать, если имена методов будут реорганизованы.
  • Если бы был случай, когда нужно добавлять новые методы, которые не должны наследовать поведение с включенной проверкой, то разработчику может быть довольно легко сделать это неправильно, и, по крайней мере, это будет означать добавляя больше магических строк.

Чтобы устранить точку # 1 и, по крайней мере, облегчить задачу с помощью точки # 2, я бы создал аннотацию BypassCheck (или что-то подобное), которую я мог бы использовать для обозначения методов в интерфейсе Foo, для которых Я не хочу выполнять "включенную проверку". Таким образом, мне не нужны магические строки вообще, и разработчику будет намного проще правильно добавить новый метод в этом специальном случае.

Используя аннотационное решение, код будет выглядеть так:

main:

public static void main(String[] args) {
    Foo foo = Foo.newFoo();
    foo.setEnabled(false);
    foo.bar(); // won't print anything.
    foo.setEnabled(true);
    foo.bar(); // prints "Executing method bar"
}

BypassCheck аннотация:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BypassCheck {
}
Интерфейс

Foo:

public interface Foo {
    @BypassCheck boolean getEnabled();
    @BypassCheck void setEnabled(boolean enable);

    void bar();
    void baz();
    void bat();

    // Needs Java 8 to have this convenience method here.
    static Foo newFoo() {
        FooFactory fooFactory = new FooFactory();
        return fooFactory.makeFoo();
    }
}

FooFactory класс:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class FooFactory {

    public Foo makeFoo() {
        return (Foo) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class[]{Foo.class},
                new FooInvocationHandler(new FooImpl()));
    }

    private static class FooImpl implements Foo {

        private boolean enabled = false;

        @Override
        public boolean getEnabled() {
            return this.enabled;
        }

        @Override
        public void setEnabled(boolean enable) {
            this.enabled = enable;
        }

        @Override
        public void bar() {
            System.out.println("Executing method bar");
        }

        @Override
        public void baz() {
            System.out.println("Executing method baz");
        }

        @Override
        public void bat() {
            System.out.println("Executing method bat");
        }

    }

    private static class FooInvocationHandler implements InvocationHandler {

        private FooImpl fooImpl;

        public FooInvocationHandler(FooImpl fooImpl) {
            this.fooImpl = fooImpl;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Foo.class
                    && !method.isAnnotationPresent(BypassCheck.class) // no magic strings
                    && !this.fooImpl.getEnabled()) {

                return null;
            }

            return method.invoke(this.fooImpl, args);
        }
    }
}

Ответ 2

Есть много хороших предложений. Что вы можете сделать, чтобы поразить свою проблему, подумайте в шаблоне штата и реализуйте ее.

Взгляните на этот фрагмент кода.. возможно, это поможет вам понять. В этом случае похоже, что вы хотите изменить реализацию всех методов на основе внутреннего состояния объекта. Напомним, что сумма методов в объекте известна как поведение.

public class Foo {

      private FooBehaviour currentBehaviour = new FooEnabledBehaviour (); // or disabled, or use a static factory method for getting the default behaviour

      public void bar() {
        currentBehaviour.bar();
      }
      public void baz() {
        currentBehaviour.baz();
      }
      public void bat() {
        currentBehaviour.bat();
      }

      public void setFooEnabled (boolean fooEnabled) { // when you set fooEnabel, you are changing at runtime what implementation will be called.
        if (fooEnabled) {
          currentBehaviour = new FooEnabledBehaviour ();
        } else {
          currentBehaviour = new FooDisabledBehaviour ();
        }
      }

      private interface FooBehaviour {
        public void bar();
        public void baz();
        public void bat();
      }

      // RENEMBER THAT instance method of inner classes can refer directly to instance members defined in its enclosing class
      private class FooEnabledBehaviour implements FooBehaviour {
        public void bar() {
          // do what you want... when is enabled
        }
        public void baz() {}
        public void bat() {}

      }

      private class FooDisabledBehaviour implements FooBehaviour {
        public void bar() {
          // do what you want... when is desibled
        }
        public void baz() {}
        public void bat() {}

      }
}

Надеюсь, вам понравится!

P.D: Является ли реализация шаблона состояния (также известна как стратегия в зависимости от контекста), но принципы одинаковы).

Ответ 3

Этот вопрос тесно связан с аспектно-ориентированным программированием. AspectJ является расширением AOP Java, и вы можете дать ему понять, чтобы получить некоторую искушение.

Насколько я знаю, прямая поддержка AOP в Java отсутствует. Существуют некоторые шаблоны GOF, которые относятся к нему, например, Template Method и Strategy, но на самом деле это не спасет вас от строк кода.

В Java и большинстве других языков вы можете определить повторяющуюся логику, которая вам нужна в функциях, и принять так называемый дисциплинированный подход к кодированию, в котором вы их называете в нужное время.

public void checkBalance() {
    checkSomePrecondition();
    ...
    checkSomePostcondition();
}

Однако это не подойдет вашему делу, потому что вы хотите, чтобы код с факторингом мог вернуться из checkBalance. В языках, поддерживающих макросы (например, C/С++), вы можете определить checkSomePrecondition и checkSomePostcondition как макросы, и они просто будут заменены препроцессором до того, как компилятор будет даже вызываться:

#define checkSomePrecondition \
    if (!fooIsEnabled) return;

Java не имеет этого из коробки. Это может оскорбить кого-то, но я использовал автоматическое создание кода и механизмы шаблонов для автоматизации повторяющихся задач кодирования в прошлом. Если вы обрабатываете файлы Java перед их компиляцией с помощью подходящего препроцессора, например Jinja2, вы можете сделать что-то похожее на то, что возможно на C.

Возможный чистый подход Java

Если вы ищете чистое решение для Java, то, возможно, вы не найдете кратким. Но он все равно может разделить общие части вашей программы и избежать дублирования кода и ошибок. Вы могли бы сделать что-то вроде этого (это своего рода Strategy - вдохновленный шаблон). Обратите внимание, что в С# и 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 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 */);
        }});
    }
}

Ответ 4

Да, но это немного работает, поэтому зависит от того, насколько важно для вас.

Вы можете определить класс как интерфейс, написать реализацию делегата, а затем использовать java.lang.reflect.Proxy для реализации интерфейса с методами, которые выполняют общую часть, а затем условно вызвать делегата.

interface Foo {
    public void bar();
    public void baz();
    public void bat();
}

class FooImpl implements Foo {
    public void bar() {
      //... <-- your logic represented by this notation above
    }

    public void baz() {
      //... <-- your logic represented by this notation above
    }

    // and so forth
}

Foo underlying = new FooImpl();
InvocationHandler handler = new MyInvocationHandler(underlying);
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
     new Class[] { Foo.class },
     handler);

Ваш MyInvocationHandler может выглядеть примерно так (обработка ошибок и классные леса опущены, предполагая, что fooIsEnabled определен где-то доступным):

public Object invoke(Object proxy, Method method, Object[] args) {
    if (!fooIsEnabled) return null;
    return method.invoke(underlying, args);
}

Это не невероятно красиво. Но, в отличие от разных комментаторов, я бы сделал это, так как я думаю, что повторение - это более важный риск, чем такая плотность, и вы сможете произвести "чувство" своего настоящего класса, с этой несколько непостижимой оболочкой, добавленной на очень локально только в нескольких строках кода.

Подробнее о динамических прокси-классах см. в документации Java.

Ответ 5

Я бы рассмотрел рефакторинг. Этот шаблон сильно разрушает DRY-шаблон (не повторяйте сам). Я считаю, что это нарушает эту классовую ответственность. Но это зависит от вашего контроля над кодом. Ваш вопрос очень открыт - где вы вызываете экземпляр Foo?

Я предполагаю, что у вас есть код типа

foo.bar(); // does nothing if !fooEnabled
foo.baz(); // does also nothing
foo.bat(); // also

возможно, вы должны называть это примерно так:

if (fooEnabled) {
   foo.bat();
   foo.baz();
   ...
}

И держите его в чистоте. Например, запись:

this.logger.debug(createResourceExpensiveDump())

a logger не спрашивает себя, если debug включен. Он просто регистрируется.

Вместо этого вызывающий класс должен проверить это:

if (this.logger.isDebugEnabled()) {
   this.logger.debug(createResourceExpensiveDump())
}

Если это библиотека и вы не можете управлять вызовом этого класса, бросьте IllegalStateException, который объясняет, почему, если это вызов является незаконным и вызывает проблемы.

Ответ 6

IMHO самым элегантным и лучшим решением для этого является наличие более одной реализации Foo вместе с методом factory для создания:

class Foo {
  protected Foo() {
    // Prevent direct instantiation
  }

  public void bar() {
    // Do something
  }

  public static void getFoo() {
    return fooEnabled ? new Foo() : new NopFoo();
  }
}

class NopFoo extends Foo {
  public void bar() {
    // Do nothing
  }
}

Или вариант:

class Foo {
  protected Foo() {
    // Prevent direct instantiation
  }

  public void bar() {
    // Do something
  }

  public static void getFoo() {
    return fooEnabled ? new Foo() : NOP_FOO;
  }

  private static Foo NOP_FOO = new Foo() {
    public void bar() {
      // Do nothing
    }
  };
}

Как указывает Sstan, еще лучше было бы использовать интерфейс:

public interface Foo {
  void bar();

  static Foo getFoo() {
    return fooEnabled ? new FooImpl() : new NopFoo();
  }
}

class FooImpl implements Foo {
  FooImpl() {
    // Prevent direct instantiation
  }

  public void bar() {
    // Do something
  }
}

class NopFoo implements Foo {
  NopFoo() {
    // Prevent direct instantiation
  }

  public void bar() {
    // Do nothing
  }
}

Адаптируйте это к остальным вашим обстоятельствам (вы каждый раз создаете новый Foo или повторно используете один и тот же экземпляр и т.д.)

Ответ 7

У меня есть другой подход:

interface Foo {
  public void bar();
  public void baz();
  public void bat();
}

class FooImpl implements Foo {
  public void bar() {
    //...
  }
  public void baz() {
    //...
  }
  public void bat() {
    //...
  }
}

class NullFoo implements Foo {
  static NullFoo DEFAULT = new NullFoo();
  public void bar() {}
  public void baz() {}
  public void bat() {}
}

}

а затем вы можете сделать

(isFooEnabled ? foo : NullFoo.DEFAULT).bar();

Возможно, вы даже можете заменить isFooEnabled на переменную Foo, в которой либо используется FooImpl, либо NullFoo.DEFAULT. Затем вызов будет проще:

Foo toBeUsed = isFooEnabled ? foo : NullFoo.DEFAULT;
toBeUsed.bar();
toBeUsed.baz();
toBeUsed.bat();

Кстати, это называется "Null pattern".

Ответ 8

В аналогичном функциональном подходе к ответу @Colin, Java 8 лямбда-функции, можно обернуть условную функцию включить/отключить кода в метод защиты (executeIfEnabled), который принимает действие лямбда, к которому может быть передан условный код.

Несмотря на то, что в вашем случае этот подход не сохранит ни одной строки кода, вы можете теперь централизовать другие проблемы с переключением функций, а также AOP или отладки, такие как ведение журнала, диагностика, профилирование и др.

Одно из преимуществ использования лямбда здесь заключается в том, что замыкания могут использоваться, чтобы избежать необходимости перегружать метод executeIfEnabled.

Например:

class Foo {
    private Boolean _fooIsEnabled;

    public Foo(Boolean isEnabled) {
        _fooIsEnabled = isEnabled;
    }

    private void executeIfEnabled(java.util.function.Consumer someAction) {
        // Conditional toggle short circuit
        if (!_fooIsEnabled) return;

        // Invoke action
        someAction.accept(null);
    }

    // Wrap the conditionally executed code in a lambda
    public void bar() {
        executeIfEnabled((x) -> {
            System.out.println("Bar invoked");
        });
    }

    // Demo with closure arguments and locals
    public void baz(int y) {
        executeIfEnabled((x) -> {
            System.out.printf("Baz invoked %d \n", y);
        });
    }

    public void bat() {
        int z = 5;
        executeIfEnabled((x) -> {
            System.out.printf("Bat invoked %d \n", z);
        });
    }

С тестом:

public static void main(String args[]){
    Foo enabledFoo = new Foo(true);
    enabledFoo.bar();
    enabledFoo.baz(33);
    enabledFoo.bat();

    Foo disabledFoo = new Foo(false);
    disabledFoo.bar();
    disabledFoo.baz(66);
    disabledFoo.bat();
}

Ответ 9

Как указано в других ответах, Шаблон разработки стратегии является подходящим шаблоном проектирования для упрощения этого кода, Я проиллюстрировал его здесь, используя вызов метода посредством отражения, но есть несколько механизмов, которые вы могли бы использовать для получения того же эффекта.

class Foo {

  public static void main(String[] args) {
      Foo foo = new Foo();
      foo.fooIsEnabled = false;
      foo.execute("bar");
      foo.fooIsEnabled = true;
      foo.execute("baz");
  }

  boolean fooIsEnabled;

  public void execute(String method) {
    if(!fooIsEnabled) {return;}
    try {
       this.getClass().getDeclaredMethod(method, (Class<?>[])null).invoke(this, (Object[])null);
    }
    catch(Exception e) {
       // best to handle each exception type separately
       e.printStackTrace();
    }
  }

  // Changed methods to private to reinforce usage of execute method
  private void bar() {
    System.out.println("bar called");
    // bar stuff here...
  }
  private void baz() {
    System.out.println("baz called");
    // baz stuff here...
  }
  private void bat() {
    System.out.println("bat called");
    // bat stuff here...
  }
}

Ответ 10

Если бы только java немного лучше работал. Он считает, что большинство решений ООО заключается в создании класса, который обертывает одну функцию, поэтому он вызывается только при включенном foo.

abstract class FunctionWrapper {
    Foo owner;

    public FunctionWrapper(Foo f){
        this.owner = f;
    }

    public final void call(){
        if (!owner.isEnabled()){
            return;
        }
        innerCall();
    }

    protected abstract void innerCall();
}

а затем реализовать bar, baz и bat в качестве анонимных классов, которые расширяют FunctionWrapper.

class Foo {
    public boolean fooIsEnabled;

    public boolean isEnabled(){
        return fooIsEnabled;
    }

    public final FunctionWrapper bar = new FunctionWrapper(this){
        @Override
        protected void innerCall() {
            // do whatever
        }
    };

    public final FunctionWrapper baz = new FunctionWrapper(this){
        @Override
        protected void innerCall() {
            // do whatever
        }
    };

    // you can pass in parms like so 
    public final FunctionWrapper bat = new FunctionWrapper(this){
        // some parms:
        int x,y;
        // a way to set them
        public void setParms(int x,int y){
            this.x=x;
            this.y=y;
        }

        @Override
        protected void innerCall() {
            // do whatever using x and y
        }
    };
}

Другая идея

Используйте glglgl nullable solution, но создайте внутренние классы FooImpl и NullFoo (с частными конструкторами) следующего класса:

class FooGateKeeper {

    public boolean enabled;

    private Foo myFooImpl;
    private Foo myNullFoo;

    public FooGateKeeper(){
        myFooImpl= new FooImpl();
        myNullFoo= new NullFoo();
    }

    public Foo getFoo(){
        if (enabled){
            return myFooImpl;
        }
        return myNullFoo;
    }  
}

таким образом вам не нужно беспокоиться о том, чтобы запомнить использование (isFooEnabled ? foo : NullFoo.DEFAULT).

Ответ 11

Кажется, что класс ничего не делает, когда Foo не включен, поэтому почему бы не выразить это на более высоком уровне, где вы создаете или получаете экземпляр Foo?

class FooFactory
{
 static public Foo getFoo()
 {
   return isFooEnabled ? new Foo() : null;
 }
}
 ...
 Foo foo = FooFactory.getFoo();
 if(foo!=null)
 {
   foo.bar();
   ....
 }     

Это работает, только если isFooEnabled является константой. В общем случае вы можете создать свою собственную аннотацию.

Ответ 12

Я не знаком с синтаксисом Java. Предполагая, что в Java существует полиморфизм, статическое свойство, абстрактный класс и метод:

    public static void main(String[] args) {
    Foo.fooIsEnabled = true; // static property, not particular to a specific instance  

    Foo foo = new bar();
    foo.mainMethod();

    foo = new baz();
    foo.mainMethod();

    foo = new bat();
    foo.mainMethod();
}

    public abstract class Foo{
      static boolean fooIsEnabled;

      public void mainMethod()
      {
          if(!fooIsEnabled)
              return;

          baMethod();
      }     
      protected abstract void baMethod();
    }
    public class bar extends Foo {
        protected override baMethod()
        {
            // bar implementation
        }
    }
    public class bat extends Foo {
        protected override baMethod()
        {
            // bat implementation
        }
    }
    public class baz extends Foo {
        protected override baMethod()
        {
            // baz implementation
        }
    }

Ответ 13

В принципе у вас есть флаг, который, если он установлен, вызов функции должен быть пропущен. Поэтому я думаю, что мое решение было бы глупо, но вот оно.

Foo foo = new Foo();

if (foo.isEnabled())
{
    foo.doSomething();
}

Вот реализация простого прокси, если вы хотите выполнить какой-либо код перед выполнением какой-либо функции.

class Proxy<T>
{
    private T obj;
    private Method<T> proxy;

    Proxy(Method<T> proxy)
    {
        this.ojb = new T();
        this.proxy = proxy;
    }

    Proxy(T obj, Method<T> proxy)
    {
        this.obj = obj;
        this.proxy = proxy;
    }

    public T object ()
    {
        this.proxy(this.obj);
        return this.obj;
    }
}

class Test
{
    public static void func (Foo foo)
    {
        // ..
    }

    public static void main (String [] args)
    {
        Proxy<Foo> p = new Proxy(Test.func);

        // how to use
        p.object().doSomething();
    }
}

class Foo
{
    public void doSomething ()
    {
        // ..
    }
}

Ответ 14

Существует другое решение, использующее делегат (указатель на функцию). У вас может быть уникальный метод, который сначала выполняет проверку, а затем вызывает соответствующий метод в соответствии с вызываемой функцией (параметром). Код С#:

internal delegate void InvokeBaxxxDelegate();

class Test
{
    private bool fooIsEnabled;

    public Test(bool fooIsEnabled)
    {
        this.fooIsEnabled = fooIsEnabled;
    }

    public void Bar()
    {
        InvokeBaxxx(InvokeBar);
    }

    public void Baz()
    {
        InvokeBaxxx(InvokeBaz);
    }

    public void Bat()
    {
        InvokeBaxxx(InvokeBat);
    }

    private void InvokeBaxxx(InvokeBaxxxDelegate invoker)
    {
        if (!fooIsEnabled) return;
        invoker();
    }

    private void InvokeBar()
    {
        // do Invoke bar stuff
        Console.WriteLine("I am Bar");
    }

    private void InvokeBaz()
    {
        // do Invoke bar stuff
        Console.WriteLine("I am Baz");
    }

    private void InvokeBat()
    {
        // do Invoke bar stuff
        Console.WriteLine("I am Bat");
    }
}