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

时间:2015-07-03 19:25:05

标签: java interface encapsulation

查看以下型号:

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,应该对用户隐藏。

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

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

我想知道是否有一个众所周知的问题解决方案或只是看到另一种选择。

2 个答案:

答案 0 :(得分:2)

您可以将面向用户的方法划分为面向用户的界面,其余的则放在另一个界面中。

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(),则可以返回阅读 - 而只是查看:

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字段值的唯一方法是通过反射(或者,可选地,序列化)。但是,如果你只想防范顽皮的开发者偷工减料而不是恶意攻击者,你应该没问题。

答案 1 :(得分:1)

看看这个问题:Protected in Interfaces
这是关于受保护的方法,但很好地解释了这个问题 我会使用新的抽象类:

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

未经测试