web-dev-qa-db-fra.com

Existe-t-il un moyen élégant de faire en sorte que chaque méthode d'une classe commence par un certain bloc de code?

J'ai une classe où chaque méthode commence de la même manière:

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

Existe-t-il un bon moyen d’exiger (et d’espérer ne pas écrire à chaque fois) la partie fooIsEnabled pour chaque méthode publique de la classe?

141
kristina

Je ne connais pas le style élégant, mais voici une implémentation qui utilise le langage intégré de Java Java.lang.reflect.Proxy cela applique que tous les appels de méthode sur Foo commencent par vérifier le enabled Etat.

main méthode:

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 interface:

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 classe:

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);
        }
    }
}

Comme d'autres l'ont déjà fait remarquer, il semble exagéré de ne plus avoir besoin de ce dont vous avez besoin si vous n'avez à vous préoccuper que de quelques méthodes.

Cela dit, il y a certainement des avantages:

  • Une certaine séparation des préoccupations est réalisée, car les implémentations de méthodes de Foo n'ont pas à s'inquiéter de l'inquiétude transversale de enabled. Au lieu de cela, le code de la méthode doit seulement s'inquiéter de l'objectif principal de la méthode, rien de plus.
  • Il n’ya aucun moyen pour un développeur innocent d’ajouter une nouvelle méthode à la classe Foo et, par erreur, "oublier" d’ajouter le contrôle enabled. Le comportement de vérification de enabled est automatiquement hérité par toute méthode récemment ajoutée.
  • Si vous devez ajouter une autre préoccupation transversale ou si vous devez améliorer la vérification enabled, il est très facile de le faire en toute sécurité et au même endroit.
  • C'est un peu gentil que vous puissiez obtenir ce comportement de type AOP avec la fonctionnalité Java intégrée). Vous n'êtes pas obligé d'intégrer un autre framework comme Spring, bien que ils peuvent certainement être de bonnes options aussi.

Pour être juste, certains des inconvénients sont:

  • Une partie du code d'implémentation qui gère les invocations de proxy est laide. Certains diraient également qu'avoir des classes internes pour empêcher l'instanciation de la classe FooImpl est moche.
  • Si vous souhaitez ajouter une nouvelle méthode à Foo, vous devez modifier 2 zones: la classe d'implémentation et l'interface. Ce n'est pas grave, mais il reste encore un peu de travail.
  • Les invocations par procuration ne sont pas gratuites. Il y a une certaine surcharge de performance. Pour une utilisation générale cependant, cela ne sera pas perceptible. Voir ici pour plus d'informations.

EDIT:

Le commentaire de Fabian Streitel m'a fait penser à deux inconvénients avec ma solution ci-dessus, je l'avoue, je ne suis pas content de moi:

  1. Le gestionnaire d'appels utilise des chaînes magiques pour ignorer les commandes "enabled-check" sur les méthodes "getEnabled" et "setEnabled". Cela peut facilement casser si les noms de méthodes sont refactorisés.
  2. S'il y avait un cas où il fallait ajouter de nouvelles méthodes qui ne devraient pas hériter du comportement "enabled-check", il peut être assez facile pour le développeur de se tromper, et à tout le moins, cela reviendrait à ajouter plus de magie. des cordes.

Pour résoudre le point n ° 1, et au moins atténuer le problème avec le point n ° 2, je créerais une annotation BypassCheck (ou quelque chose de similaire) que je pourrais utiliser pour marquer les méthodes dans le Foo interface pour laquelle je ne souhaite pas effectuer le "contrôle activé". De cette façon, je n'ai pas du tout besoin de chaînes magiques et il est beaucoup plus facile pour un développeur d'ajouter correctement une nouvelle méthode dans ce cas particulier.

En utilisant la solution d'annotation, le code ressemblerait à ceci:

main méthode:

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 annotation:

import Java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BypassCheck {
}

Foo interface:

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 classe:

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

Il y a beaucoup de bonnes suggestions. Ce que vous pouvez faire pour résoudre votre problème est de penser au modèle d'état et de le mettre en œuvre.

Jetez un coup d'oeil à cet extrait de code .. peut-être que cela vous donnera une idée. Dans ce scénario, il semble que vous souhaitiez modifier toute l'implémentation des méthodes en fonction de l'état interne de l'objet. Rappelez-vous que la somme des méthodes dans un objet est connue sous le nom de comportement.

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() {}

      }
}

J'espère que vous aimez!

P.D: Est une mise en œuvre du modèle d'état (également connu sous le nom de stratégie dépendant du contexte .. mais les principes sont les mêmes).

50
Victor

Oui, mais c'est un peu de travail, donc cela dépend de votre importance.

Vous pouvez définir la classe en tant qu'interface, écrire une implémentation de délégué, puis utiliser Java.lang.reflect.Proxy pour implémenter l'interface avec des méthodes qui effectuent la partie partagée puis appellent le délégué de manière conditionnelle.

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);

Votre MyInvocationHandler peut ressembler à quelque chose comme ceci (traitement des erreurs et échafaudage de classe omis, en supposant que fooIsEnabled soit défini quelque part accessible):

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

Ce n'est pas incroyablement joli. Mais contrairement à divers commentateurs, je le ferais, car je pense que la répétition est un risque plus important que ce genre de densité, et vous serez capable de produire le "feeling" de votre classe réelle, avec cette enveloppe un peu impénétrable ajoutée à très localement en quelques lignes de code.

Voir le documentation Java pour plus de détails sur les classes de proxy dynamiques.

14
David P. Caldwell

Cette question est étroitement liée à programmation orientée aspect . AspectJ est une extension AOP de Java et vous pouvez y jeter un coup d'oeil pour en tirer un peu quelque chose.

Autant que je sache, il n’existe pas de support direct pour AOP en Java. Certains modèles GOF y sont associés, comme par exemple Méthode du modèle et Stratégie , mais cela ne vous sauvera pas vraiment les lignes de code.

Dans Java et dans la plupart des autres langages, vous pouvez définir la logique récurrente dont vous avez besoin dans les fonctions et adopter une approche dite de codage discipliné dans laquelle vous les appelez au bon moment.

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

Toutefois, cela ne conviendrait pas à votre cas, car vous souhaiteriez que le code décompacté puisse être retourné à partir de checkBalance. Dans les langages qui supportent les macros (comme C/C++), vous pouvez définir checkSomePrecondition et checkSomePostcondition en tant que macros. Elles seraient simplement remplacées par le préprocesseur avant même que le compilateur ne soit appelé:

#define checkSomePrecondition \
    if (!fooIsEnabled) return;

Java n’a pas cette solution prête à l'emploi. Cela peut choquer quelqu'un, mais j’ai utilisé une génération de code automatique et des moteurs de modèles pour automatiser des tâches de codage répétitives dans le passé. Si vous traitez vos fichiers Java avant de les compiler avec un préprocesseur approprié, par exemple Jinja2, vous pourriez faire quelque chose de similaire à ce qui est possible en C.

Possible approche pure Java

Si vous recherchez une solution pure Java, ce que vous pourrez trouver ne sera probablement pas concis. Toutefois, cela pourrait tout de même minimiser les parties communes de votre programme et éviter la duplication de code et les bugs. Vous pouvez faire quelque chose comme ceci (c'est une sorte de Stratégie - motif inspiré). Notez qu'en C # et Java 8, et dans d'autres langages dans lesquels les fonctions sont un peu plus facile à gérer, cette approche peut effectivement sembler agréable.

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

Un peu comme un scénario du monde réel

Vous développez une classe pour envoyer des trames de données à un robot industriel. Le robot prend du temps pour terminer une commande. Une fois la commande terminée, il vous renvoie une image de contrôle. Le robot peut être endommagé s'il reçoit une nouvelle commande alors que la précédente est encore en cours d'exécution. Votre programme utilise une classe DataLink pour envoyer et recevoir des images vers et depuis le robot. Vous devez protéger l'accès à l'instance DataLink.

Le fil de l'interface utilisateur appelle RobotController.left, right, up ou down lorsque l’utilisateur clique sur les boutons mais appelle également BaseController.tick à intervalles réguliers, afin de réactiver le transfert de commande vers l'instance privée 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

Je considérerais le refactoring. Ce modèle casse énormément DRY (ne vous répétez pas). Je crois que cela brise cette responsabilité de classe. Mais cela dépend de votre contrôle du code. Votre question est très ouverte - où êtes-vous appelant Foo instance?

Je suppose que tu as un code comme

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

peut-être devriez-vous l'appeler quelque chose comme ceci:

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

Et garde-le propre. Par exemple, en vous connectant:

this.logger.debug(createResourceExpensiveDump())

un loggerne se demande pas, si le débogage est activé. Il suffit de se connecter.

Au lieu de cela, la classe appelante doit vérifier ceci:

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

S'il s'agit d'une bibliothèque et que vous ne pouvez pas contrôler l'appel de cette classe, lancez un IllegalStateException qui explique pourquoi, si cet appel est illégal et cause des problèmes.

11
Gondy

À mon humble avis, la solution la plus élégante et la plus performante à cet égard consiste à avoir plusieurs implémentations de Foo, ainsi qu'une méthode d'usine permettant d'en créer une:

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

Ou une variante:

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
    }
  };
}

Comme le fait remarquer sstan, il serait préférable d'utiliser une interface:

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

Adaptez cela au reste de votre situation (créez-vous un nouveau Foo à chaque fois ou réutilisez-vous la même instance, etc.)

6
Pepijn Schmitz

J'ai une autre approche: avoir un

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() {}
}

}

et alors vous pouvez faire

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

Peut-être pourriez-vous même remplacer le isFooEnabled par une variable Foo qui contient le FooImpl à utiliser ou le NullFoo.DEFAULT. Ensuite, l'appel est à nouveau plus simple:

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

BTW, cela s'appelle le "modèle nul".

5
glglgl

Dans une approche fonctionnelle similaire à la réponse de @ Colin, avec fonctions lambda de Java 8 , il est possible d’envelopper le code d’activation/désactivation conditionnelle dans une méthode Guard (executeIfEnabled) qui accepte l'action lambda, à laquelle le code à exécuter conditionnellement peut être passée.

Bien que, dans votre cas, cette approche ne sauvegarde aucune ligne de code, vous pouvez désormais centraliser d'autres problèmes de bascule de fonctionnalités, ainsi que des problèmes de débogage, tels que la journalisation, les diagnostics, le profilage, etc.

L'un des avantages de l'utilisation de lambdas ici est que les fermetures peuvent être utilisées pour éviter le besoin de surcharger la méthode executeIfEnabled.

Par exemple:

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);
        });
    }

Avec un test:

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

Comme indiqué dans d'autres réponses, le modèle de conception de stratégie est un modèle de conception approprié à suivre pour simplifier ce code. Je l'ai illustré ici en utilisant l'invocation de méthode par réflexion, mais vous pouvez utiliser un certain nombre de mécanismes pour obtenir le même effet.

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

Si seulement Java était un peu meilleur pour être fonctionnel. Il pense que la solution la plus OOO est de créer une classe qui englobe une fonction unique de sorte qu'elle ne soit appelée que lorsque foo est activé.

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();
}

puis implémenter bar, baz et bat en tant que classes anonymes qui étendent 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
        }
    };
}

Une autre idée

Utilisez la solution nullable de glglgl mais créez FooImpl et NullFoo classes internes (avec constructeurs privés) de la classe ci-dessous:

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;
    }  
}

de cette façon, vous n'avez pas à vous soucier de vous rappeler d'utiliser (isFooEnabled ? foo : NullFoo.DEFAULT).

2
Colin

Java. Supposons qu’en Java, il existe polymorphisme, propriété statique, classe abstraite et méthode:

    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

Il semble que la classe ne fasse rien quand Foo n'est pas activé, alors pourquoi ne pas l'exprimer à un niveau supérieur où vous créez ou obtenez l'instance de Foo?

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

Cela ne fonctionne que si isFooEnabled est une constante. Dans un cas général, vous pouvez créer votre propre annotation.

1
Konrad Höffner

En gros, vous indiquez que, s’il est défini, l’appel de fonction doit être ignoré. Je pense donc que ma solution serait idiote, mais la voici.

Foo foo = new Foo();

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

Voici l'implémentation d'un proxy simple, au cas où vous voudriez exécuter du code avant d'exécuter une fonction.

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

Il existe une autre solution, en utilisant delegate (pointeur vers fonction). Vous pouvez avoir une méthode unique qui commence par effectuer la validation puis appelle la méthode correspondante en fonction de la fonction (paramètre) à appeler. Code 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