source

수업의 모든 메서드를 특정 코드 블록으로 시작하는 우아한 방법이 있을까요?

gigabyte 2022. 10. 1. 21:19
반응형

수업의 모든 메서드를 특정 코드 블록으로 시작하는 우아한 방법이 있을까요?

모든 방법이 같은 방식으로 시작되는 수업이 있습니다.

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

요?fooIsEnabled수업의 모든 공개적인 방법을 위해 파트를 짜깁기?

것은 「Java」, 「Java」를 한 작업 입니다.java.lang.reflect.Proxy모든 메서드 호출을 강제합니다.Foo 확인하다enableddiscloss.discloss.conf.

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체크, 한 곳에서 안전하게 작업하는 것은 매우 쉽습니다.
  • 내장된 Java 기능으로 AOP와 같은 동작을 할 수 있다는 것은 좋은 일입니다. 것은 아닙니다.Spring★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★♪

공정하게 말하면 다음과 같은 단점이 있습니다.

  • 프록시 호출을 처리하는 구현 코드 중 일부가 보기 흉합니다.또 은 '이 '내부 계급'이 ' 계급'이 '내부 계급'이 '내부 계급'이 있다'고도 있다.FooImpl업은못못 못못못다다
  • Foo실장 클래스와 인터페이스라는2개의 장소에서 변경을 가할 필요가 있습니다.별일은 아니지만 아직 일이 좀 더 남았네요.
  • 프록시 호출은 무료가 아닙니다.일정한 퍼포먼스 오버헤드가 있습니다.일반용으로는 눈에 띄지 않습니다.상세한 것에 대하여는, 여기를 참조해 주세요.

편집:

Fabian Streitel의 코멘트는 위의 솔루션에서 두 가지 문제를 생각하게 했습니다.인정하건대, 나는 나 자신에 대해 만족하지 못합니다.

  1. 호출 핸들러는 마법 문자열을 사용하여 "getEnabled" 및 "setEnabled" 메서드의 "enabled-check"를 건너뜁니다.메서드 이름을 리팩터링하면 쉽게 파손될 수 있습니다.
  2. "enabled-check" 동작을 상속해서는 안 되는 새로운 메서드를 추가할 필요가 있는 경우 개발자가 이를 오해하기 쉬우며 적어도 매직 문자열을 추가하는 것을 의미합니다.

#1을 .BypassCheck것)의할 때 할 수 있습니다.Foo check "enabled check" 를 않은 입니다.이렇게 하면 마법의 끈이 전혀 필요 없고, 개발자가 이 특별한 경우에 새로운 방법을 올바르게 추가할 수 있게 됩니다.

주석 솔루션을 사용하면 코드는 다음과 같습니다.

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

좋은 제안들이 많이 있어요.문제를 해결하기 위해 할 수 있는 일은 상태 패턴으로 생각하고 실행하는 것입니다.

이 코드 조각 좀 보세요아마 아이디어를 얻을 수 있을 거예요.이 시나리오에서는 객체의 내부 상태에 따라 메서드 구현 전체를 수정하려는 것처럼 보입니다.객체의 메서드의 합계가 동작으로 인식된다는 점에 유의하십시오.

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: 상태 패턴의 구현(문맥에 따라 전략이라고도 함)원칙은 똑같다).

네, 하지만 일이 좀 있어서 당신에게 얼마나 중요한지에 달렸어요.

후 를 할 수 .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 문서를 참조하십시오.

이 질문은 애스펙트 지향 프로그래밍과 밀접하게 관련되어 있습니다.Aspect J는 Java의 AOP 확장으로, 어떤 ISP를 얻을 수 있는지 확인할 수 있습니다.

Java에서 AOP를 직접 지원하지 않는 것으로 알고 있습니다.Template Method나 Strategy 등 와 관련된 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 */);
        }});
    }
}

이치노이 패턴은 DRY 패턴(반복하지 마세요)을 크게 깨트리고 있습니다.나는 이것이 이 반의 책임을 깬다고 믿는다.하지만 이것은 코드를 제어하는 방법에 따라 달라집니다.요. 로 전화하는 예요? 디로로 전화 ?? ????Foo인턴instanceinstanceinstance?

내 생각에 당신은 다음과 같은 코드를 가지고 있을 것이다.

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이 전화가 불법이고 문제를 일으킨다면 그 이유가 설명되죠

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를 작성하거나 동일한 인스턴스를 재사용하는 경우 등).

또 다른 접근법이 있습니다.

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 a Foo""를하는 "FooImplNullFoo.DEFAULT그러면 콜이 다시 쉬워집니다.

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

그나저나 이건 '늘 패턴'이라고 불려요.

@Colin의 답변에 대한 유사한 기능적 접근법에서는 Java 8의 람다 함수를 사용하여 조건부 기능 토글/디세이블 코드를 가드 메서드로 랩할 수 있습니다.executeIfEnabled조건부로 실행할 코드를 전달할 수 있는 액션 람다를 받아들입니다.

이 방법으로는 코드 행이 저장되지 않지만, 이 방법을 드라이업(DRYUP)함으로써 다른 기능 전환 문제 및 로깅, 진단, 프로파일링 등의 디버깅 문제를 집중 관리할 수 있습니다.

여기서 람다를 사용하는 것의 한 가지 이점은 폐쇄가 과부하를 피하기 위해 사용될 수 있다는 것입니다.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...
  }
}

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

후, 「」, 「」를 실장합니다.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
        }
    };
}

다른 아이디어

glgl의 무효 솔루션을 사용하여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 인스턴스를 만들거나 가져오는 높은 수준에서 표현해 보는 것은 어떨까요?

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

이것은 isFooEnabled가 상수인 경우에만 작동합니다.일반적인 경우 주석을 직접 작성할 수 있습니다.

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

기본적으로 플래그가 설정되어 있으면 함수 호출을 건너뜁니다.그래서 제 해결책은 바보같을 것 같지만, 여기 있습니다.

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

또 다른 해결책은 위임자를 사용하는 것입니다(기능 포인터)를 사용하는 것입니다.처음에 검증을 실시하고 다음으로 호출하는 함수(파라미터)에 따라 관련 메서드를 호출하는 고유한 메서드를 사용할 수 있습니다.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");
    }
}

언급URL : https://stackoverflow.com/questions/31121513/is-there-an-elegant-way-to-make-every-method-in-a-class-start-with-a-certain-blo

반응형