在代码中使用接口但对最终用户隐藏内部方法的最佳实践

Best practices for using interfaces in code, but hiding internal methods from end user

查看以下模型:

interface Context {
    BeanFactory getBeanFactory(); // public method
    void refresh(); // public method
    void destroy(); // public method
}

interface BeanFactory {
    <T> T getBean(String id); // public method
    void destroyBeans(); // should be private method for user, but visible for Context
}

class ContextImpl implements Context {
    private BeanFactory beanFactory;

    @Override
    public void destroy() {
        beanFactory.destroyBeans();
    }
}

ContextImpl 使用 BeanFactory 接口,这就是方法 destroyBeans() 放在那里的原因。但我不希望它在那里,因为它是内部的 API 并且应该对用户隐藏。

我想在 Context 中使用 AbstractBeanFactory 引用和受保护的 destroyBeans() 方法。这将解决向最终用户公开方法的问题,但会将接口替换为抽象 class.

另一种变体是制作另一个界面,它将扩展最终用户界面,并在 Context 中使用它。这将破坏用户创建自己的 BeanFactory 实现的能力。

我想知道这个问题是否有众所周知的解决方案,或者看看其他替代方案。

看这道题:Protected in Interfaces
这是关于受保护的方法,但是很好地解释了问题。
我会像这样使用新的摘要 class:

abstract ABeanFactory {
    abstract <T> T getBean(String id);
    final void destroyBeans(){}
}

或者像这样使用第二个界面:

interface Context {
    BeanFactoryPrivate getBeanFactory();
    void refresh(); // public method
    void destroy();
}

interface BeanFactory {
    <T> T getBean(String id);
}

interface BeanFactoryPrivate extends BeanFactory{
    void destroyBeans();
}

class ContextImpl implements Context {
    private BeanFactoryPrivate beanFactory;

    @Override
    public void destroy() {
        beanFactory.destroyBeans();
    }
}

未测试

您可以将面向用户的方法分离到一个面向用户的界面中,其余的在另一个界面中。

interface Context {
    BeanFactory getBeanFactory(); // public method
    void refresh(); // public method
    void destroy(); // public method
}

interface BeanFactory {
    <T> T getBean(String id); // public method
}

interface DestroyableBeanFactory extends BeanFactory {
    void destroyBeans(); // should be private method for user, but visible for Context
}

class ContextImpl implements Context {
    private DestroyableBeanFactory beanFactory;

    // internally we demand a DestroyableBeanFactory but we only
    // expose it as BeanFactory
    public BeanFactory getBeanFactory() {
        return beanFactory;
    }
    @Override
    public void destroy() {
        beanFactory.destroyBeans();
    }
}

更新: 如果您担心来电者将您的 BeanFactory 转换为 DestroyableBeanFactory 并在其上调用 destroyBeans(),您可以return 改为只读视图:

class ContextImpl implements Context {
    private DestroyableBeanFactory beanFactory;

    // to be extra safe, we create a read-only wrapper
    // for our bean factory
    public BeanFactory getBeanFactory() {
        return new BeanFactory() { //written as an anon inner class for brevity, ideally you should cache this read-only wrapper instance
             public <T> T getBean(String id) { 
                 return beanFactory.getBean(id);
             }
        };
    }
    ...
  }

有了这个,访问 beanFactory 字段值的唯一方法是通过反射(或者,可选地,序列化)。但是如果你只想防御偷工减料的顽皮开发人员而不是恶意攻击者,你应该没问题。