当前位置: 首页 > 面试题库 >

有没有一种优雅的方法可以使类中的每个方法都以一定的代码块开头?

白翔
2023-03-14
问题内容

我有一个类,每个方法都以相同的方式开始:

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

是否有一种很好的方法要求(希望每次都不要编写)fooIsEnabled该类中每个公共方法的部分?


问题答案:

我对优雅并不了解,但这是一个使用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检查效果,那么在一个地方安全,轻松地进行检查非常容易。
  • 可以通过内置的Java功能获得类似AOP的行为,这是一种很好的选择。您不必被迫集成诸如之类的其他框架Spring,尽管它们也绝对是不错的选择。

公平地说,一些缺点是:

  • 一些处理代理调用的实现代码很丑陋。有人还会说拥有内部类来防止类的实例化FooImpl是丑陋的。
  • 如果要向中添加新方法Foo,则必须在两个位置进行更改:实现类和接口。没什么大不了的,但是还有很多工作要做。
  • 代理调用不是免费的。有一定的性能开销。对于一般用途,它不会引起注意。
    编辑:

Fabian Streitel的评论使我想出了上述解决方案中的2个烦恼,我承认,我对自己不满意:

  1. 调用处理程序使用魔术字符串跳过“ getEnabled”和“ setEnabled”方法上的“ enabled-check”。如果重构了方法名称,这很容易中断。
  2. 如果存在需要添加不应继承“ enabled-check”行为的新方法的情况,那么开发人员很容易会出错,至少,这意味着添加更多的魔术。字符串。

要解决第1点,并至少缓解第2点的问题,我将创建一个注释BypassCheck(或类似的注释),该注释可用于标记Foo我不想为其执行“启用检查”。这样,我根本不需要魔术字符串,在这种特殊情况下,开发人员正确添加新方法变得容易得多。

使用注释解决方案,代码如下所示:

main 方法:

public static void main(String[] args) {
    Foo foo = Foo.newFoo();
    foo.setEnabled(false);
    foo.bar(); // won't print anything.
    foo.setEnabled(true);
    foo.bar(); // prints "Executing method bar"
}

BypassCheck 注解:

import java.lang.annotation.*;

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

Foo 接口:

public interface Foo {
    @BypassCheck boolean getEnabled();
    @BypassCheck void setEnabled(boolean enable);

    void bar();
    void baz();
    void bat();

    // Needs Java 8 to have this convenience method here.
    static Foo newFoo() {
        FooFactory fooFactory = new FooFactory();
        return fooFactory.makeFoo();
    }
}

FooFactory 类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class FooFactory {

    public Foo makeFoo() {
        return (Foo) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class[]{Foo.class},
                new FooInvocationHandler(new FooImpl()));
    }

    private static class FooImpl implements Foo {

        private boolean enabled = false;

        @Override
        public boolean getEnabled() {
            return this.enabled;
        }

        @Override
        public void setEnabled(boolean enable) {
            this.enabled = enable;
        }

        @Override
        public void bar() {
            System.out.println("Executing method bar");
        }

        @Override
        public void baz() {
            System.out.println("Executing method baz");
        }

        @Override
        public void bat() {
            System.out.println("Executing method bat");
        }

    }

    private static class FooInvocationHandler implements InvocationHandler {

        private FooImpl fooImpl;

        public FooInvocationHandler(FooImpl fooImpl) {
            this.fooImpl = fooImpl;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Foo.class
                    && !method.isAnnotationPresent(BypassCheck.class) // no magic strings
                    && !this.fooImpl.getEnabled()) {

                return null;
            }

            return method.invoke(this.fooImpl, args);
        }
    }
}


 类似资料:
  • 有没有一种好的方法要求类中的每个公共方法使用部分(希望不是每次都写)?

  • 问题内容: 嗨,我想使用WMI类来查找应用程序和产品信息。但是问题是我想使用Java或任何脚本语言(如python,javascript或perl)。我听说过JWMI,这可能是一个选择。有人可以帮我吗??? 问题答案: JavaScript和Java不是一回事。 JavaScript Windows脚本宿主(WSH)下提供了JavaScript。有了它,访问WMI相当容易: jWMI(Java)

  • 问题内容: doIt函数将打印“ dad”。有没有办法让它打印“儿子”? 问题答案: 是。但是就变量而言,它会被覆盖(将新值赋予变量。将新定义赋予函数是Override)。 在父类的块中使用时,该值将得到反映 如果变量是静态的,则在初始化时使用静态块更改其值, 否则更改构造函数。 你还可以稍后在任何块中更改该值。它将反映在超一流

  • 问题内容: 我非常喜欢Visual Studio在完成已编写的代码和所引用的ALSO代码(各种库/程序集)时为您提供注释文档/参数名称的方式。 在完成代码或将方法悬停在方法上时,是否有一种简单的方法可以在Eclipse中获取内联javadoc /参数名称?通过插件?通过某些设置?使用大量的库(就像在Java中经常发生的那样)然后当您将其放在源jar中时,必须去网站或本地javadoc位置查找信息,

  • 问题内容: 我正在建立一个带有flask的网站,其中用户具有帐户并能够登录。我正在使用flask-principal作为登录部分和角色管理。有没有办法让用户的会话在5分钟或10分钟后过期?我在flask文档或flask-principal文档中找不到该文件。 我想到了一种手动方法,在登录时在服务器端设置一个带有时间标签的变量,并在用户执行下一个操作时,服务器会验证该时间戳记上的时间增量并删除会话。

  • 问题内容: PMD可以忽略Java源文件中带有注释的特定警告。 Checkstyle有类似的选择吗? 问题答案: 是的,请查看SuppressionCommentFilter和SuppressWithNearbyCommentFilter选项。