我在Java中实现模板模式。让我们假设以下代码:
public abstract class A {
public final void work() {
doPrepare();
doWork();
}
protected abstract void doPrepare();
protected abstract void doWork();
}
public final class B extends A {
@Override
protected abstract void doPrepare() { /* do stuff here */ }
@Override
protected abstract void doWork() { /* do stuff here */ }
}
public final class Main {
public static void main(String[] args) {
B b = new B();
b.work();
}
}
我认为这是一个问题,人们很容易无意中拨打b.doWork()
而不是总是打电话给b.work()
。如果可能的话,最优雅的解决方案是什么,“隐藏”钩子?
答案 0 :(得分:3)
您确实在使用模板模式。你可以做两件事,然后躲避"来自外部用户的详细信息:
1)了解您的access modifiers:
Access Levels
Modifier | Class | Package | Subclass | World |
--------------------------------------------------
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
no modifier | Y | Y | N | N |
private | Y | N | N | N |
由于缺乏坚定的黑客在过去通过这些保护措施欺负他们的方式,这些可以让偶然的编码人员无意中调用最好的方法。程序员会欣赏这一点。没有人想使用混乱的API。
目前b.doWork()
为protected
。因此,如果您的main()
在课堂上main()
(它不是),子类A
(它不是B
,那么它只会显示在您的b.doWork()
中),或在同一个包中(不清楚,你没有发布任何有关你的代码所在的软件包的线索)。
这引出了一个问题,你是谁试图阻止看到A
?如果您正在创建的包只是您正在开发的包装,那可能并不是那么糟糕。如果很多人在这个包中乱搞许多其他课程,那么他们就不太可能熟悉课程B
和protected
。在这种情况下,您不希望所有人都可以看到所有内容。这里只允许类和子类访问会很好。但是没有允许子类访问的修饰符,也没有允许包访问。只要您将main()
子类化为最佳,您就可以做到。考虑到这一点,不要创建包含大量课程的包。保持包裹紧密和集中。这样,大多数人都会在包装外面工作,看起来很简单。
简而言之,如果您希望b.doWork()
阻止main()
将package some.other.package
public final class Main {
...
}
置于另一个套餐中,
b.doWork()
2)使用当前代码很难理解下一条建议的价值,因为它解决了只有在扩展代码时才会出现的问题。但它确实与保护人们看不到像public static void main(String[] args) {
A someALikeThingy = new B(); // <-- note use of type A
someALikeThingy.work(); //The name is silly but shows we've forgotten about B
//and know it isn't really just an A :)
}
What does "program to interfaces, not implementations" mean?
在你的代码中,这意味着如果main看起来像这样会更好:
A
为什么呢?使用B
类型与类型A
有什么关系?好interface
更接近接口。请注意,此处我并不仅仅指您在使用关键字B
时所获得的内容。我的意思是类型A
是B
类型的具体实现,如果我不必编写针对A
的代码,我真的不愿意,因为谁知道什么是奇怪的非{ {1}}事B
有。这里很难理解,因为main中的代码很简短。甜。此外,A
和B
的公开接口并非完全不同。它们都只有一个公共方法work()
。想象一下,main()
漫长而复杂,想象B
有各种非A
方法悬挂它。现在假设你需要创建一个你想要主要处理的类C
。看到虽然主要是B
,但主要的每一行知道B
只是一些简单的A
之类的东西,这并不令人感到欣慰。除了new
之后的部分,这可能是真的,除了对main()
做任何事情,但更新new
。实际上,这不是为什么使用强类型语言有时候会很好吗?
&LT;咆哮&GT;
如果您认为在使用new
B()
之后更新该部分工作太多,那么您并不孤单。特别值得注意的是,dependency injection movement产生了一系列框架,而这些框架实际上更多地是关于自动化对象构造,而不是任何构造函数可以让你做的简单注入。
&LT; /咆哮&GT;
更新
我从你的评论中看到你不愿意创建小型紧包。在这种情况下,请考虑放弃基于继承的模板模式,以支持基于组合的策略模式。你仍然得到多态性。它不会免费发生。更多的代码编写和间接。但是即使在运行时也可以改变状态。
这似乎反直觉,因为现在doWork()
必须公开。这有什么样的保护?那么现在你可以完全隐藏B
,甚至隐藏在AHandler
内。这是一些人性化的Facade类,它包含曾经是您的模板代码但仅暴露work()
。 A
可以只是interface
,因此AHandler
仍然不知道它有B
。这种方法在依赖注入人群中很受欢迎,但肯定没有普遍的吸引力。有些人每次都盲目地惹恼了许多人。你可以在这里读更多关于它的内容:
Prefer composition over inheritance?
答案 1 :(得分:2)
您可以做的最简单,最明显的事情是使用其他软件包中的A -> B
层次结构。
如果由于某种原因你不能这样做,那么你可以使用一个接口并使你的B
类包装实际来自A
的类。像这样:
public interface Worker {
void work();
}
public abstract class A implements Worker {
@Override
public final void work() {
doPrepare();
doWork();
}
protected abstract void doPrepare();
protected abstract void doWork();
}
public static class B implements Worker { // does not extend A, but implements Worker
private final A wrapped = new A() { // anonymous inner class, so it extends A
@Override
protected void doPrepare() {
System.out.println("doPrepare");
}
@Override
protected void doWork() {
System.out.println("doWork");
}
};
@Override
public void work() { // delegate implementation to descendant from A
this.wrapped.work();
}
}
这样,受保护的方法仍隐藏在从A
扩展的匿名内部类中,B
是B
的私有最终成员。关键是A
不会扩展Worker
,而是通过委托给匿名内部类来实现work()
接口的B
方法。因此,即使在属于同一个包的类中调用work()
&#39; B b = new B();
b.work(); // this method is accessible via the Work interface
方法,受保护的方法也不可见。
doPrepare
doWork
输出:
java -jar jarfilename.jar