web-dev-qa-db-ja.com

クラス内のすべてのメソッドを特定のコードブロックで開始するエレガントな方法はありますか?

すべてのメソッドが同じ方法で開始されるクラスがあります。

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

クラス内のすべてのパブリックメソッドにfooIsEnabled部分を要求する(そして、毎回記述しないように)良い方法はありますか?

141
kristina

エレガントについては知りませんが、ここにJavaの組み込みJava.lang.reflect.Proxy that-enforcesを使用して、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 checkのクロスカッティングの問題を心配する必要がないため、懸念の特定の分離が達成されます。代わりに、メソッドのコードは、メソッドの主な目的が何であるかを気にするだけでよく、それ以上何もしません。
  • 無邪気な開発者がFooクラスに新しいメソッドを追加し、誤ってenabledチェックを追加することを「忘れる」方法はありません。 enabled checkの動作は、新しく追加されたメソッドによって自動的に継承されます。
  • 別の横断的関心事を追加する必要がある場合、またはenabledチェックを強化する必要がある場合は、安全かつ一箇所で簡単に行うことができます。
  • 組み込みのJava機能を使用すると、このAOPのような動作を得ることができます。 Springのような他のフレームワークを統合する必要はありませんが、それらも間違いなく良いオプションです。

公平を期すために、いくつかの欠点は次のとおりです。

  • プロキシの呼び出しを処理する実装コードの一部は見苦しいです。また、FooImplクラスのインスタンス化を防ぐために内部クラスを持つことはいことだと言う人もいます。
  • Fooに新しいメソッドを追加する場合は、実装クラスとインターフェースの2つの場所で変更を行う必要があります。大したことではありませんが、まだ少し手間がかかります。
  • プロキシ呼び出しは無料ではありません。一定のパフォーマンスオーバーヘッドがあります。ただし、一般的な使用では、目立ちません。詳細については、 here を参照してください。

編集:

Fabian Streitelのコメントは、上記のソリューションで2つの迷惑なことを考えさせてくれました。

  1. 呼び出しハンドラーは、マジックストリングを使用して、「getEnabled」および「setEnabled」メソッドの「enabled-check」をスキップします。メソッド名がリファクタリングされると、これは簡単に壊れる可能性があります。
  2. 「enabled-check」動作を継承してはならない新しいメソッドを追加する必要がある場合、開発者がこの間違いを犯すのは非常に簡単です。少なくとも魔法を追加することを意味します文字列。

ポイント#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);
        }
    }
}
88
sstan

たくさんの良い提案があります。問題を打つためにできることは、State Patternで考えて実装することです。

このコードスニペットをご覧ください。このシナリオでは、オブジェクトの内部状態に基づいてメソッド実装全体を変更するように見えます。オブジェクト内のメソッドの合計は動作として認識されることを思い出してください。

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:状態パターンの実装です(コンテキストに応じて戦略とも呼ばれますが、原則はまったく同じです)。

50
Victor

はい、しかし、それは少し作業ですので、それはそれがあなたにとってどれほど重要であるかによります。

クラスをインターフェイスとして定義し、デリゲート実装を記述し、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ドキュメント を参照してください。

14

この質問は アスペクト指向プログラミング と密接に関連しています。 AspectJはJavaのAOP拡張であり、いくつかのインスピレーションを得るためにそれを見てみることができます。

私の知る限り、JavaでのAOPの直接的なサポートはありません。例えば Template MethodStrategy など、それに関連するいくつかのGOFパターンがありますが、実際にはコードの行を節約しません。

Javaおよび他のほとんどの言語では、関数で必要な再帰ロジックを定義し、適切なタイミングで呼び出すいわゆる統制のとれたコーディングアプローチを採用できます。

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

ただし、ファクタリングされたコードをcheckBalanceから返せるようにするため、これはケースに当てはまりません。マクロをサポートする言語(C/C++など)では、checkSomePreconditionおよびcheckSomePostconditionをマクロとして定義できます。これらは、コンパイラが呼び出される前にプリプロセッサに置き換えられるだけです。

#define checkSomePrecondition \
    if (!fooIsEnabled) return;

Javaにはこれがすぐに使用できるわけではありません。これは誰かを怒らせるかもしれませんが、私は過去に反復的なコーディングタスクを自動化するために自動コード生成とテンプレートエンジンを使用しました。適切なプリプロセッサ(たとえば、Jinja2)でコンパイルする前にJavaファイルを処理すると、Cで可能なことと同様のことができます。

可能な純粋なJavaアプローチ

純粋なJavaソリューションを探している場合、おそらく簡潔ではないでしょう。ただし、プログラムの一般的な部分を除外して、コードの重複やバグを回避することは可能です。あなたはこのようなことをすることができます(それはある種の Strategy -inspiredパターンです)。 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.leftrightup、または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 */);
        }});
    }
}
14
damix911

リファクタリングを検討します。このパターンは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())

デバッグが有効な場合、logger自分自身に尋ねない。ただログに記録します。

代わりに、呼び出しクラスはこれを確認する必要があります。

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

これがライブラリであり、このクラスの呼び出しを制御できない場合、IllegalStateExceptionをスローします。これは、この呼び出しが違法で問題を引き起こす場合、その理由を説明します。

11
Gondy

これに対する最もエレガントで最高のパフォーマンスのソリューションであるIMHOは、Fooの複数の実装と、それらを作成するためのファクトリーメソッドを持つことです。

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を作成するか、同じインスタンスを再利用するなど)。

6
Pepijn Schmitz

私には別のアプローチがあります:

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またはNullFoo.DEFAULTを保持するFooImpl変数に置き換えることもできます。その後、呼び出しは再び簡単になります。

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

ところで、これは「ヌルパターン」と呼ばれます。

5
glglgl

Java 8のラムダ関数 を使用した@Colinの答えに対する同様の機能的アプローチでは、条件付き機能トグルの有効化/無効化コードをガードメソッド(executeIfEnabled)にラップして、条件付きで実行されるコードを渡すことができるアクションラムダ。

あなたの場合、このアプローチはコードを一切保存しませんが、これを乾燥させることで、他の機能切り替えの懸念に加えて、AOPまたはロギング、診断、プロファイリングなどのデバッグの懸念を集中化するオプションがあります.

ここでラムダを使用する利点の1つは、クロージャーを使用して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();
}
3
StuartLC

他の回答で指摘されているように、 Strategy Design Pattern は、このコードを簡素化するために従うべき適切な設計パターンです。ここでは、リフレクションによるメソッド呼び出しを使用して説明しましたが、同じ効果を得るために使用できるメカニズムはいくつもあります。

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...
  }
}
2
LJ2

Javaのみが機能している場合は少し優れていた場合。ほとんどのOOOソリューションは、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();
}

そして、barbaz、および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ソリューション を使用しますが、以下のクラスの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)の使用を覚えておく必要はありません。

2
Colin

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
        }
    }
1
ehh

Fooが有効になっていない場合、クラスは何もしないようですので、Fooインスタンスを作成または取得する上位レベルでこれを表現しないのはなぜですか?

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

ただし、isFooEnabledが定数の場合にのみ機能します。一般的な場合、独自の注釈を作成できます。

1
Konrad Höffner

基本的に、フラグが設定されている場合、関数呼び出しをスキップする必要があります。だから、私の解決策はばかげていると思うが、ここにある。

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 ()
    {
        // ..
    }
}
1
Khaled.K

デリゲート(関数へのポインター)を使用する別のソリューションがあります。最初に検証を実行し、次に呼び出される関数(パラメーター)に応じて関連するメソッドを呼び出す一意のメソッドを持つことができます。 C#コード:

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");
    }
}
0
ehh