У меня есть класс, в котором все методы запускаются одинаково:
class Foo {
public void bar() {
if (!fooIsEnabled) return;
//...
}
public void baz() {
if (!fooIsEnabled) return;
//...
}
public void bat() {
if (!fooIsEnabled) return;
//...
}
}
Есть ли хороший способ требовать (и, надеюсь, не писать каждый раз) fooIsEnabled
часть для каждого общедоступного метода в классе?
java
design-patterns
Кристина
источник
источник
Ответы:
Я не знаю насчет элегантности, но вот рабочая реализация с использованием встроенного в 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
проверку, это очень легко сделать безопасно и в одном месте.Spring
, хотя они определенно могут быть хорошими вариантами.Честно говоря, некоторые из недостатков:
FooImpl
класса некрасиво.Foo
, вам нужно внести изменения в 2 места: класс реализации и интерфейс. Ничего страшного, но работы еще немного.РЕДАКТИРОВАТЬ:
Комментарий Фабиана Штрайтеля заставил меня задуматься о двух неприятностях с моим вышеуказанным решением, которые, я признаю, я недоволен собой:
Чтобы разрешить точку №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); } } }
источник
Есть много хороших предложений ... что вы можете сделать, чтобы решить свою проблему, это подумать о 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() {} } }
Надеюсь, вам понравится!
PD: Реализация паттерна состояний (также известная как стратегия в зависимости от контекста ... но принципы те же).
источник
bar()
inFooEnabledBehavior
иbar()
inFooDisabledBehavior
могут использовать один и тот же код, возможно, даже с одной строкой, отличающейся между ними. Вы могли бы очень легко, особенно если бы этот код поддерживался младшими разработчиками (такими как я), в конечном итоге получить гигантский мусор, который невозможно поддерживать и не тестировать. Это может произойти с любым кодом, но кажется, что это так легко сделать очень быстро. +1 правда ведь хорошее предложение.Да, но это немного работы, поэтому все зависит от того, насколько это важно для вас.
Вы можете определить класс как интерфейс, написать реализацию делегата, а затем использовать
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 для получения подробной информации о динамических прокси-классах.
источник
Этот вопрос тесно связан с аспектно-ориентированным программированием . AspectJ - это AOP-расширение Java, и вы можете взглянуть на него, чтобы получить некоторое вдохновение.
Насколько мне известно, в Java нет прямой поддержки АОП. Есть несколько шаблонов GOF, которые относятся к нему, например, шаблонный метод и стратегия, но на самом деле это не спасет вам строки кода.
В Java и большинстве других языков вы можете определить повторяющуюся логику, которая вам нужна в функциях, и принять так называемый дисциплинированный подход к кодированию, при котором вы вызываете их в нужное время.
public void checkBalance() { checkSomePrecondition(); ... checkSomePostcondition(); }
Однако это не подошло бы к вашему случаю, потому что вы хотели бы, чтобы вычеркнутый код мог вернуться из
checkBalance
. В языках, поддерживающих макросы (например, C / C ++), вы можете определитьcheckSomePrecondition
иcheckSomePostcondition
как макросы, и они будут просто заменены препроцессором до того, как компилятор даже будет вызван:#define checkSomePrecondition \ if (!fooIsEnabled) return;
В Java этого нет из коробки. Это может кого-то обидеть, но в прошлом я использовал автоматическое создание кода и механизмы шаблонов для автоматизации повторяющихся задач кодирования. Если вы обрабатываете свои файлы Java перед их компиляцией с помощью подходящего препроцессора, например Jinja2, вы можете сделать что-то похожее на то, что возможно в C.
Возможный подход на чистом Java
Если вы ищете решение на чистом Java, то, возможно, вы найдете не очень лаконичным. Но при этом можно исключить общие части вашей программы и избежать дублирования кода и ошибок. Вы могли бы сделать что-то вроде этого (это своего рода шаблон, вдохновленный Стратегией ). Обратите внимание, что в C # и Java 8, а также на других языках, в которых функции немного проще обрабатывать, этот подход может действительно хорошо выглядеть.
public interface Code { void execute(); } ... public class Foo { private bool fooIsEnabled; private void protect(Code c) { if (!fooIsEnabled) return; c.execute(); } public void bar() { protect(new Code { public void execute() { System.out.println("bar"); } }); } public void baz() { protect(new Code { public void execute() { System.out.println("baz"); } }); } public void bat() { protect(new Code { public void execute() { System.out.println("bat"); } }); } }
Что-то вроде реального сценария
Вы разрабатываете класс для отправки фреймов данных промышленному роботу. Роботу требуется время, чтобы выполнить команду. Как только команда будет завершена, она отправит вам контрольный кадр обратно. Робот может быть поврежден, если он получит новую команду, когда предыдущая все еще выполняется. Ваша программа использует
DataLink
класс для отправки и получения кадров от робота. Вам необходимо защитить доступ кDataLink
экземпляру.Пользовательский интерфейс нить вызовы
RobotController.left
,right
,up
илиdown
когда пользователь нажимает на кнопки, но и вызовыBaseController.tick
через регулярные промежутки времени, для того , чтобы экспедирования повторного включения команды в частномDataLink
случае.interface Code { void ready(DataLink dataLink); } class BaseController { private DataLink mDataLink; private boolean mReady = false; private Queue<Code> mEnqueued = new LinkedList<Code>(); public BaseController(DataLink dl) { mDataLink = dl; } protected void protect(Code c) { if (mReady) { mReady = false; c.ready(mDataLink); } else { mEnqueue.add(c); } } public void tick() { byte[] frame = mDataLink.readWithTimeout(/* Not more than 50 ms */); if (frame != null && /* Check that it's an ACK frame */) { if (mEnqueued.isEmpty()) { mReady = true; } else { Code c = mEnqueued.remove(); c.ready(mDataLink); } } } } class RobotController extends BaseController { public void left(float amount) { protect(new Code() { public void ready(DataLink dataLink) { dataLink.write(/* Create a byte[] that means 'left' by amount */); }}); } public void right(float amount) { protect(new Code() { public void ready(DataLink dataLink) { dataLink.write(/* Create a byte[] that means 'right' by amount */); }}); } public void up(float amount) { protect(new Code() { public void ready(DataLink dataLink) { dataLink.write(/* Create a byte[] that means 'up' by amount */); }}); } public void down(float amount) { protect(new Code() { public void ready(DataLink dataLink) { dataLink.write(/* Create a byte[] that means 'down' by amount */); }}); } }
источник
protect
который легче повторно использовать и документировать. Если вы скажете будущему сопровождающему, что критический код должен быть защищенprotect
, вы уже говорите ему, что делать. Если правила защиты изменятся, новый код все равно будет защищен. Это как раз основная причина определения функций, но OP должен «возвращатьreturn
», чего функции не могут.Я бы подумал о рефакторинге. Этот узор сильно нарушает СУХОЙ узор (не повторяйтесь). Я считаю, что это ломает классовую ответственность. Но это зависит от вашего контроля над кодом. Ваш вопрос очень открытый - куда вы вызываете
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
не спрашивает себя , включена ли отладка. Это просто журналы.Вместо этого вызывающий класс должен это проверить:
if (this.logger.isDebugEnabled()) { this.logger.debug(createResourceExpensiveDump()) }
Если это библиотека, и вы не можете управлять вызовом этого класса, бросьте,
IllegalStateException
которое объясняет, почему, если этот вызов является незаконным и вызывает проблемы.источник
ИМХО, самое элегантное и лучшее решение для этого - иметь более одной реализации 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 или повторно используете тот же экземпляр и т. Д.)
источник
Foo
и забыть добавить нерабочую версию метода в расширенный класс, таким образом обойдя желаемое поведение.isFooEnabled
при созданииFoo
. Это слишком рано. В исходном коде это делается при запуске метода. При этом стоимостьisFooEnabled
может измениться.fooIsEnabled
может быть постоянным. Но ничто не говорит нам о том, что оно будет постоянным. Итак, я рассматриваю общий случай.У меня другой подход: есть
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
переменной , которая либо удерживает ,FooImpl
чтобы использовать илиNullFoo.DEFAULT
. Тогда вызов снова проще:Кстати, это называется «нулевой шаблон».
источник
(isFooEnabled ? foo : NullFoo.DEFAULT).bar();
кажется немного неуклюжим. Имейте третью реализацию, которая делегирует одну из существующих реализаций. Вместо изменения значения поляisFooEnabled
можно изменить цель делегирования. Это уменьшает количество веток в кодеFoo
в телефонный код! Как мы могли знатьisFooEnabled
? Это внутреннее поле в классеFoo
.В аналогичном функциональном подходе к ответу @Colin с лямбда-функциями Java 8 можно обернуть код включения / отключения условной функции в метод защиты (
executeIfEnabled
), который принимает лямбда-действие действия, для которого можно выполнить условно выполняемый код. прошло.Хотя в вашем случае этот подход не сохранит ни одной строчки кода, высушив его, теперь у вас есть возможность централизовать другие проблемы переключения функций, а также проблемы АОП или отладки, такие как ведение журнала, диагностика, профилирование и др.
Одним из преимуществ использования лямбда-выражений здесь является то, что можно использовать замыкания, чтобы избежать необходимости перегрузки
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(); }
источник
Как указано в других ответах, шаблон разработки стратегии является подходящим шаблоном проектирования для упрощения этого кода. Я проиллюстрировал это здесь, используя вызов метода через отражение, но есть любое количество механизмов, которые вы можете использовать для получения того же эффекта.
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... } }
источник
Proxy
.foo.fooIsEnabled ...
? Априори это внутреннее поле объекта, мы не можем и не хотим видеть его снаружи.Если бы только 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 с нулевым значением, но производите
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)
.источник
Foo foo = new Foo()
чтобы позвонить,bar
вы должны написатьfoo.bar.call()
Кажется, что класс ничего не делает, когда Foo не включен, так почему бы не выразить это на более высоком уровне, где вы создаете или получаете экземпляр Foo?
class FooFactory { static public Foo getFoo() { return isFooEnabled ? new Foo() : null; } } ... Foo foo = FooFactory.getFoo(); if(foo!=null) { foo.bar(); .... }
Это работает, только если isFooEnabled является константой. В общем случае вы можете создать свою аннотацию.
источник
fooIsEnabled
когда вызывается метод. Вы делаете это до создания экземпляраFoo
. Это слишком рано. Тем временем значение может измениться.isFooEnabled
это поле экземпляраFoo
объектов.Я не знаком с синтаксисом 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 } }
источник
new bar()
должно значить?Name
с большой буквы.bar()
. Если вам нужно это изменить, вы обречены.Обычно у вас есть флаг, если он установлен, вызов функции следует пропустить. Поэтому я думаю, что мое решение было бы глупым, но вот оно.
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 () { // .. } }
источник
isEnabled()
. Априори включение - это внутреннее приготовлениеFoo
, а не открытое.Есть еще одно решение, использующее делегат (указатель на функцию). У вас может быть уникальный метод, который сначала выполняет проверку, а затем вызывает соответствующий метод в соответствии с вызываемой функцией (параметром). Код 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"); } }
источник