我正在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()是一个问题。什么是最优雅的解决方案,如果可能的话,“隐藏”钩子?
发布于 2016-02-21 01:31:16
您可以做的最简单和更明显的事情是使用来自另一个包的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()方法也是从属于同一个包的类调用的,受保护的方法也是不可见的。
B b = new B();
b.work(); // this method is accessible via the Work interface输出:
doPrepare
doWork发布于 2016-02-20 23:04:33
您确实在使用模板模式。要对外部用户“隐藏”详细信息,您可以做两件主要事情:
1)理解你的访问修饰符
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 {
...
}2)用当前代码很难理解下一条建议的价值,因为只有在扩展代码的情况下才能解决问题。但是,它确实与保护人们不看b.doWork()这样的东西的想法密切相关。
在您的代码中,这意味着如果main看起来像这样会更好:
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 :)
}为什么?使用A类型和B类型有什么关系?好吧,A更接近于成为一个接口。注意,这里我并不仅仅是指当您使用关键字interface时得到了什么。我的意思是,类型B是A类型的一个具体实现,如果我不一定要针对B编写代码,我真的不想这样做,因为谁知道B有什么奇怪的非A东西。这在这里很难理解,因为主要的代码是简短和甜蜜的。而且,A和B的公共接口也没有什么不同。他们都只有一个公共方法work()。假设main()是长而复杂的,想象B有各种各样的非A方法挂在上面。现在假设您需要创建一个类C,您希望能够处理这个类。虽然main被喂给了B,但是main中的每一行都知道B只是一些简单的A一样的东西,这难道不是一件令人欣慰的事情吗?除了new之后的部分之外,这可能是正确的,并且可以避免对main()做任何事情,但是要更新该new。事实上,这难道不是为什么使用强类型语言有时会很好吗?
如果您认为即使是在new之后用B()更新该部分也是太多的工作,那么您并不孤单。正是这种特别的痴迷让我们得到了依赖注入运动,它产生了许多框架,它们实际上更多地是关于自动化对象构造,而不是任何构造函数都可以让您完成的简单注入。
更新:
我从您的评论中看到,您不愿意创建一个小的、紧凑的包。在这种情况下,考虑放弃基于继承的模板模式,转而采用基于组合的策略模式。你还是会有多态。这不是免费的。更多的代码编写和间接。但即使在运行时也可以改变状态。
这似乎违反了直觉,因为现在doWork()必须是公开的。那是什么样的保护?现在,您可以将B完全隐藏在AHandler后面,甚至可以隐藏在AHandler中。这是一些友好的facade类,它保存了以前的模板代码,但只公开了work()。A可以只是一个interface,所以AHandler仍然不知道它有一个B。这种方法在依赖注入人群中很流行,但肯定没有普遍的吸引力。有些人每次都盲目地做这件事,这使许多人感到厌烦。你可以在这里读到更多关于它的信息:喜欢构图而不是继承?
https://stackoverflow.com/questions/35529279
复制相似问题