爪哇(FX) - 只允许1类从一个单独的类调用一个方法(Java(FX) - Only allow

2019-10-21 16:35发布

我目前工作的地方我使用一个单独的类来改变用户的视图的项目。 这DISPLAYMANAGER(单身)具有类似的方法addView(View view)replaceView(View view) 。 但它也有一个称为方法displayRootView()其可仅被调用一次(INIT期间),并通过只有1类,即扩展了应用类的启动类。

任何想法,我怎么能阻止谁使用来自调用单身其他类displayRootView()方法?

我已经考虑堆栈跟踪,但似乎并不理想。 我虽然可能通过使用启动类的标记接口,以使其与其他单独的呢?

任何建议将不胜感激。

Answer 1:

呃,这是难以避免调用您的方法中的某些类,因为它打破了一些核心的原则OOP。 该方法不应该关心调用它。 这是关注基本分离 - 你的方法有关于它做什么,不是什么是当时的JVM的状态的明确合同。

考虑下列问题:

  • 如果你继承会发生什么StartUp ? 例如分离桌面,移动和网络平台?
  • 你单位将如何测试不涉及这种方法StartUp
  • 如果你需要的抽象存在的另一层会发生什么?
  • 那么如果当你添加代理服务器(通过AOP或Spring代理)?
  • 如果你需要调用从该方法会发生什么Timer ? 它仍然打算从所谓的StartUp类源(和是正确的),但它不会出现在堆栈跟踪。

和其他类似的考虑。

抛出某种异常(例如IllegalStateException ,或者自定义),如果该方法被称为第二次是绝对有效恕我直言。

这看起来像你可能想在你的代码的静态检查 ,而不是在代码或运行时检查。 我不认为这将是非常困难添加自定义规则Findbugz或PMD找到一个方法的所有直接调用和查询调用类(如果它是从其他地方调用构建失败)。 但是,我不认为这样的检查是真正有用的。

最后,也有很多,你需要方法的合法使用上述类外,比有有人会不小心使用不当,他们已经被警告和适当的Javadoc创建后的机会。



Answer 2:

的JavaFX应用程序源确实通过解析堆一些时髦的东西迹线,以确定并检查主叫方(例如,以确保它是仅由延伸的应用类的称呼)。

/**
 * Launch a standalone application. This method is typically called
 * from the main method(). It must not be called more than once or an
 * exception will be thrown.
 * This is equivalent to launch(TheClass.class, args) where TheClass is the
 * immediately enclosing class of the method that called launch. It must
 * be a subclass of Application or a RuntimeException will be thrown.
 *
 * <p>
 * The launch method does not return until the application has exited,
 * either via a call to Platform.exit or all of the application windows
 * have been closed.
 *
 * <p>
 * Typical usage is:
 * <ul>
 * <pre>
 * public static void main(String[] args) {
 *     Application.launch(args);
 * }
 * </pre>
 * </ul>
 *
 * @param args the command line arguments passed to the application.
 *             An application may get these parameters using the
 *             {@link #getParameters()} method.
 *
 * @throws IllegalStateException if this method is called more than once.
 */
public static void launch(String... args) {
    // Figure out the right class to call
    StackTraceElement[] cause = Thread.currentThread().getStackTrace();

    boolean foundThisMethod = false;
    String callingClassName = null;
    for (StackTraceElement se : cause) {
        // Skip entries until we get to the entry for this class
        String className = se.getClassName();
        String methodName = se.getMethodName();
        if (foundThisMethod) {
            callingClassName = className;
            break;
        } else if (Application.class.getName().equals(className)
                && "launch".equals(methodName)) {

            foundThisMethod = true;
        }
    }

    if (callingClassName == null) {
        throw new RuntimeException("Error: unable to determine Application class");
    }

    try {
        Class theClass = Class.forName(callingClassName, true,
                           Thread.currentThread().getContextClassLoader());
        if (Application.class.isAssignableFrom(theClass)) {
            Class<? extends Application> appClass = theClass;
            LauncherImpl.launchApplication(appClass, args);
        } else {
            throw new RuntimeException("Error: " + theClass
                    + " is not a subclass of javafx.application.Application");
        }
    } catch (RuntimeException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

该LauncherImpl代码使在一个私人静态的AtomicBoolean使用getAndSet操作,以确保应用程序没有启动一次以上。

// Ensure that launchApplication method is only called once
private static AtomicBoolean launchCalled = new AtomicBoolean(false);

/**
 * This method is called by the standalone launcher.
 * It must not be called more than once or an exception will be thrown.
 *
 * Note that it is always called on a thread other than the FX application
 * thread, since that thread is only created at startup.
 *
 * @param appClass application class
 * @param preloaderClass preloader class, may be null
 * @param args command line arguments
 */
public static void launchApplication(final Class<? extends Application> appClass,
        final Class<? extends Preloader> preloaderClass,
        final String[] args) {

    if (launchCalled.getAndSet(true)) {
        throw new IllegalStateException("Application launch must not be called more than once");
    }

    if (! Application.class.isAssignableFrom(appClass)) {
        throw new IllegalArgumentException("Error: " + appClass.getName()
                + " is not a subclass of javafx.application.Application");
    }

    if (preloaderClass != null && ! Preloader.class.isAssignableFrom(preloaderClass)) {
        throw new IllegalArgumentException("Error: " + preloaderClass.getName()
                + " is not a subclass of javafx.application.Preloader");
    }

    // Create a new Launcher thread and then wait for that thread to finish
    final CountDownLatch launchLatch = new CountDownLatch(1);
    Thread launcherThread = new Thread(new Runnable() {
        @Override public void run() {
            try {
                launchApplication1(appClass, preloaderClass, args);
            } catch (RuntimeException rte) {
                launchException = rte;
            } catch (Exception ex) {
                launchException =
                    new RuntimeException("Application launch exception", ex);
            } catch (Error err) {
                launchException =
                    new RuntimeException("Application launch error", err);
            } finally {
                launchLatch.countDown();
            }
        }
    });
    launcherThread.setName("JavaFX-Launcher");
    launcherThread.start();

    // Wait for FX launcher thread to finish before returning to user
    try {
        launchLatch.await();
    } catch (InterruptedException ex) {
        throw new RuntimeException("Unexpected exception: ", ex);
    }

    if (launchException != null) {
        throw launchException;
    }
}

因此,它是一种复杂的,怪异的,但如果你想有一个成熟的解决方案,适用于JavaFX的代码库,你可以尝试通过它来解析明白是怎么回事,它适应您的情况。

我只能说这引入额外的复杂应用程序,如果它是非常重要的东西为你的应用有。

Orodous使得如何阻塞性这样的逻辑可以是用于单元测试的一些优秀的点。 例如看看这个建议在部分的单元测试JavaFX应用程序 。 由于在发射怪异检查,独立测试他们的应用程序的功能,开发人员需要经过奇怪箍绕过调用任何发射代码(例如使用JavaFX基于JFXPanel而是因为所涉及的应用程序的一个Swing初始化应用程序只能使用一次推出)。



Answer 3:

我会扔在displayRootView()被调用时不止一次一个IllegalStateException。



Answer 4:

你可以考虑使用给出的“罗密欧与朱丽叶”伎俩在这里 ,原本在Java模拟(从C ++)的“朋友”的机制。



文章来源: Java(FX) - Only allow 1 class to call a method from a singleton class