java - pattern - ¿Hay una manera elegante de hacer que cada método en una clase comience con un determinado bloque de código?
java factory pattern (14)
Básicamente, tiene un indicador de que si está configurado, se debe omitir la llamada a la función. Así que creo que mi solución sería tonta, pero aquí está.
Foo foo = new Foo();
if (foo.isEnabled())
{
foo.doSomething();
}
Aquí está la implementación de un Proxy simple, en caso de que desee ejecutar algún código antes de ejecutar cualquier función.
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 ()
{
// ..
}
}
Tengo una clase donde cada método comienza de la misma manera:
class Foo {
public void bar() {
if (!fooIsEnabled) return;
//...
}
public void baz() {
if (!fooIsEnabled) return;
//...
}
public void bat() {
if (!fooIsEnabled) return;
//...
}
}
¿Hay una buena manera de requerir (y con suerte no escribir cada vez) la parte
fooIsEnabled
para cada método público en la clase?
Hay otra solución, usar delegado (puntero para funcionar). Puede tener un método único que primero realiza la validación y luego llama al método relevante de acuerdo con la función (parámetro) a llamar. Código 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");
}
}
No estoy familiarizado con la sintaxis de Java. Asunción de que en Java hay polimorfismo, propiedad estática, clase y método abstractos
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
}
}
Parece que la clase no hace nada cuando Foo no está habilitado, entonces ¿por qué no expresar esto en un nivel superior donde creas u obtienes la instancia de Foo?
class FooFactory
{
static public Foo getFoo()
{
return isFooEnabled ? new Foo() : null;
}
}
...
Foo foo = FooFactory.getFoo();
if(foo!=null)
{
foo.bar();
....
}
Sin embargo, esto solo funciona si isFooEnabled es una constante. En un caso general, puede crear su propia anotación.
Como se señala en otras respuestas, el Strategy es un patrón de diseño apropiado a seguir para simplificar este código. Lo he ilustrado aquí usando la invocación de métodos a través de la reflexión, pero hay varios mecanismos que podría usar para obtener el mismo efecto.
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...
}
}
Consideraría refactorizar.
Este patrón está rompiendo fuertemente el patrón SECO (no se repita).
Creo que esto rompe esta responsabilidad de clase.
Pero esto depende de su control de código.
Tu pregunta es muy abierta: ¿a dónde llamas instancia de
Foo
?
Supongo que tienes un código como
foo.bar(); // does nothing if !fooEnabled
foo.baz(); // does also nothing
foo.bat(); // also
tal vez deberías llamarlo así:
if (fooEnabled) {
foo.bat();
foo.baz();
...
}
Y mantenlo limpio. Por ejemplo, el registro:
this.logger.debug(createResourceExpensiveDump())
un
logger
no se pregunta
si la depuración está habilitada.
Solo registra.
En cambio, la clase de llamada debe verificar esto:
if (this.logger.isDebugEnabled()) {
this.logger.debug(createResourceExpensiveDump())
}
Si se trata de una biblioteca y no puede controlar las llamadas de esta clase, arroje una
IllegalStateException
que explica por qué, si esta llamada es ilegal y causa problemas.
En mi humilde opinión, la solución más elegante y de mejor rendimiento para esto es tener más de una implementación de Foo, junto con un método de fábrica para crear uno:
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
}
}
O una variación:
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
}
};
}
Como señala sstan, aún mejor sería usar una interfaz:
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
}
}
Adapte esto al resto de sus circunstancias (¿está creando un nuevo Foo cada vez o reutilizando la misma instancia, etc.)
En un enfoque funcional similar a la respuesta de @ Colin, con
las funciones lambda de Java 8
, es posible ajustar el código de activación / desactivación de la función condicional en un método de protección (
executeIfEnabled
) que acepta la acción lambda, a la que el código que se ejecuta condicionalmente puede ser pasado.
Aunque en su caso, este enfoque no guardará ninguna línea de código, al SECAR esto, ahora tiene la opción de centralizar otras inquietudes de alternancia de funciones, más AOP o inquietudes de depuración como el registro, el diagnóstico, el perfil, etc.
Una ventaja de usar lambdas aquí es que los cierres se pueden usar para evitar la necesidad de sobrecargar el método
executeIfEnabled
.
Por ejemplo:
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);
});
}
Con una prueba:
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();
}
Esta pregunta está estrechamente relacionada con la programación orientada a aspectos . AspectJ es una extensión AOP de Java y puede echarle un vistazo para obtener algo de inspiración.
Hasta donde yo sé, no hay soporte directo para AOP en Java. Hay algunos patrones de GOF que se relacionan con él, como por ejemplo, el Método de plantilla y la Strategy pero en realidad no le ahorrará líneas de código.
En Java y en la mayoría de los otros lenguajes, podría definir la lógica recurrente que necesita en las funciones y adoptar el llamado enfoque de codificación disciplinada en el que los llama en el momento adecuado.
public void checkBalance() {
checkSomePrecondition();
...
checkSomePostcondition();
}
Sin embargo, esto no encajaría en su caso porque desea que el código factorizado pueda regresar de
checkBalance
.
En los lenguajes que admiten macros (como C / C ++), podría definir
checkSomePrecondition
y
checkSomePostcondition
como macros y simplemente serían reemplazados por el preprocesador antes de que se invoque el compilador:
#define checkSomePrecondition /
if (!fooIsEnabled) return;
Java no tiene esto fuera de la caja. Esto puede ofender a alguien, pero en el pasado utilicé motores de generación automática de plantillas y plantillas para automatizar tareas de codificación repetitivas. Si procesa sus archivos Java antes de compilarlos con un preprocesador adecuado, por ejemplo Jinja2, podría hacer algo similar a lo que es posible en C.
Posible enfoque de Java puro
Si está buscando una solución Java pura, lo que puede encontrar probablemente no sea conciso. Sin embargo, aún podría factorizar partes comunes de su programa y evitar la duplicación de código y los errores. Podrías hacer algo como esto (es una especie de patrón inspirado en la Strategy ). Tenga en cuenta que en C # y Java 8, y en otros lenguajes en los que las funciones son un poco más fáciles de manejar, este enfoque en realidad puede verse bien.
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 poco como un escenario del mundo real
Está desarrollando una clase para enviar marcos de datos a un robot industrial.
El robot toma tiempo para completar un comando.
Una vez que se completa el comando, le devuelve un marco de control.
El robot puede dañarse si recibe un nuevo comando mientras el anterior aún se está ejecutando.
Su programa utiliza una clase
DataLink
para enviar y recibir tramas hacia y desde el robot.
DataLink
proteger el acceso a la instancia de
DataLink
.
El hilo de la interfaz de usuario llama a
RobotController.left
,
right
,
up
o
down
cuando el usuario hace clic en los botones, pero también llama a
BaseController.tick
a intervalos regulares, para volver a habilitar el reenvío de comandos a la instancia privada de
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 */);
}});
}
}
Hay muchas buenas sugerencias ... lo que puede hacer para resolver su problema es pensar en el Patrón de estado e implementarlo.
Eche un vistazo a este fragmento de código ... tal vez lo lleve a una idea. En este escenario, parece que desea modificar la implementación completa de los métodos en función del estado interno del objeto. Recuerde que la suma de los métodos en un objeto se conoce como comportamiento.
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() {}
}
}
¡Espero que te guste!
PD: es una implementación del patrón de estado (también conocido como estrategia según el contexto ... pero los principios son los mismos).
No sé acerca de elegante, pero aquí hay una implementación funcional que usa el Java incorporado
java.lang.reflect.Proxy
que
obliga a
que todas las invocaciones de métodos en
Foo
comiencen por verificar el estado
enabled
.
método
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"
}
Interfaz
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();
}
}
Clase
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);
}
}
}
Como otros han señalado, parece excesivo lo que necesita si solo tiene que preocuparse por un puñado de métodos.
Dicho esto, ciertamente hay beneficios:
-
Se logra una cierta separación de preocupaciones, porque las implementaciones de métodos de
Foo
no tienen que preocuparse por la preocupación transversal de verificaciónenabled
. En cambio, el código del método solo necesita preocuparse sobre cuál es el propósito principal del método, nada más. -
No hay forma de que un desarrollador inocente agregue un nuevo método a la clase
Foo
y, por error, "olvide" agregar la verificaciónenabled
. El comportamiento de verificaciónenabled
se hereda automáticamente por cualquier método recién agregado. -
Si necesita agregar otra preocupación transversal, o si necesita mejorar la verificación
enabled
, es muy fácil hacerlo de manera segura y en un solo lugar. -
Es agradable que pueda obtener este comportamiento similar a AOP con la funcionalidad Java incorporada.
No está obligado a tener que integrar algún otro marco como
Spring
, aunque definitivamente también pueden ser buenas opciones.
Para ser justos, algunos de los inconvenientes son:
-
Parte del código de implementación que maneja las invocaciones de proxy es feo.
Algunos también dirían que tener clases internas para evitar la
FooImpl
de instancias de la claseFooImpl
es feo. -
Si desea agregar un nuevo método a
Foo
, debe realizar un cambio en 2 puntos: la clase de implementación y la interfaz. No es un gran problema, pero sigue siendo un poco más de trabajo. - Las invocaciones de poder no son gratuitas. Hay una cierta sobrecarga de rendimiento. Sin embargo, para uso general, no se notará. Ver here para más información.
EDITAR:
El comentario de Fabian Streitel me hizo pensar en 2 molestias con mi solución anterior que, admito, no estoy contento conmigo mismo:
- El controlador de invocación utiliza cadenas mágicas para omitir la "comprobación habilitada" en los métodos "getEnabled" y "setEnabled". Esto puede romperse fácilmente si se refactorizan los nombres de los métodos.
- Si hubo un caso en el que se deben agregar nuevos métodos que no deberían heredar el comportamiento de "verificación habilitada", entonces puede ser bastante fácil para el desarrollador equivocarse y, al menos, significaría agregar más magia instrumentos de cuerda.
Para resolver el punto n. ° 1 y al menos aliviar el problema con el punto n. ° 2, crearía una anotación
BypassCheck
(o algo similar) que podría usar para marcar los métodos en la interfaz de
Foo
para los que no quiero realizar el "cheque habilitado".
De esta manera, no necesito cadenas mágicas en absoluto, y se vuelve mucho más fácil para un desarrollador agregar correctamente un nuevo método en este caso especial.
Usando la solución de anotación, el código se vería así:
método
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"
}
Anotación
BypassCheck
:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BypassCheck {
}
Interfaz
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();
}
}
Clase
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);
}
}
}
Sí, pero es un poco de trabajo, por lo que depende de lo importante que sea para ti.
Puede definir la clase como una interfaz, escribir una implementación de delegado y luego usar
java.lang.reflect.Proxy
para implementar la interfaz con métodos que hacen la parte compartida y luego llaman condicionalmente al delegado.
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);
Su
MyInvocationHandler
puede verse así (se omite el manejo de errores y el andamiaje de clase, suponiendo que
fooIsEnabled
esté definido en algún lugar accesible):
public Object invoke(Object proxy, Method method, Object[] args) {
if (!fooIsEnabled) return null;
return method.invoke(underlying, args);
}
No es increíblemente bonito. Pero a diferencia de varios comentaristas, lo haría, ya que creo que la repetición es un riesgo más importante que este tipo de densidad, y podrá producir la "sensación" de su clase real, con este envoltorio algo inescrutable agregado en muy localmente en solo un par de líneas de código.
Consulte la documentación de Java para obtener detalles sobre las clases de proxy dinámico.
Si solo Java fuera un poco mejor en ser funcional. Piensa que la solución más OOO es crear una clase que envuelva una sola función, por lo que solo se llama cuando foo está habilitado.
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();
}
y luego implementa
bar
,
baz
y
bat
como clases anónimas que extienden
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
}
};
}
Otra idea
Utilice
la solución anulable de glglgl
pero
NullFoo
las clases internas
FooImpl
y
NullFoo
(con constructores privados) de la siguiente clase:
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 esta manera no tiene que preocuparse por recordar usar
(isFooEnabled ? foo : NullFoo.DEFAULT)
.
Tengo otro enfoque: tener 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() {}
}
}
y luego puedes hacer
(isFooEnabled ? foo : NullFoo.DEFAULT).bar();
Tal vez incluso pueda reemplazar
isFooEnabled
con una variable
Foo
que contenga el
FooImpl
que se utilizará o el
NullFoo.DEFAULT
.
Entonces la llamada es más simple nuevamente:
Foo toBeUsed = isFooEnabled ? foo : NullFoo.DEFAULT;
toBeUsed.bar();
toBeUsed.baz();
toBeUsed.bat();
Por cierto, esto se llama el "patrón nulo".