在代码中使用接口但对最终用户隐藏内部方法的最佳实践
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
字段值的唯一方法是通过反射(或者,可选地,序列化)。但是如果你只想防御偷工减料的顽皮开发人员而不是恶意攻击者,你应该没问题。
查看以下模型:
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
字段值的唯一方法是通过反射(或者,可选地,序列化)。但是如果你只想防御偷工减料的顽皮开发人员而不是恶意攻击者,你应该没问题。