当我们运行DrawerOpenTest.java时,它会抛出StackOverflow错误,而我们希望传递测试用例。当DrawerOpen类是singleton时,assertThat(actualState,is(expectedState));在测试用例中也应如此。
请注意'状态'是3种基本方法的接口。
DrawerOpenTest类
import static org.hamcrest.CoreMatchers.*;
import org.junit.*;
public class DrawerOpenTest {
@Test
public void openCloseButtonPushedPositiveTest(){
DVDPlayer cut = DVDPlayer.getInstance(DrawerOpen.getInstance());
State expectedState = DrawerClosedNotPlaying.getInstance();
State actualState = cut.openCloseButtonPushed();
assertThat(actualState, is(sameInstance(expectedState)));
}
}
DrawerOpen Class
public class DrawerOpen implements State {
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
private static State state;
private DrawerOpen() {}
public static State getInstance() {
if(state == null)
state = new DrawerOpen();
return state;
}
@Override
public void openCloseButtonPushed() {
player.close();
player.changeState(DrawerClosedNotPlaying.getInstance());
}
@Override
public void playButtonPushed() {
player.close();
player.play();
player.changeState(DrawerClosedPlaying.getInstance());
}
@Override
public void stopButtonPushed()
}
}
DVDPlayer类
public class DVDPlayer {
private DVDPlayer() {}
private static DVDPlayer player = null;
private State state;
public State getState() {
return state;
}
public static DVDPlayer getInstance(State stateParam) {
//making it singleton
if(player == null)
{
player = new DVDPlayer();
player.state = DrawerClosedNotPlaying.getInstance();
}
else
player.state = stateParam;
return player;
}
public void changeState(State newState) {
this.state=newState;
}
public State openCloseButtonPushed(){
state.openCloseButtonPushed();
return state;
}
public State playButtonPushed() {
state.playButtonPushed();
return state;
}
public State stopButtonPushed() {
state.stopButtonPushed();
return player.state;
}
public void open() {
System.out.println("DVDPlayer is opening.....");
}
public void close() {
System.out.println("DVDPlayer is closing.....");
}
public void play() {
System.out.println("DVDPlayer is playing.....");
}
public void stop() {
System.out.println("DVDPlayer is stopping.....");
}
}
结果:它导致StackOverflowError继续初始化自身。请帮助如何正确地通过这项测试。
java.lang.StackOverflowError
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
答案 0 :(得分:0)
此:
public class DrawerOpen implements State {
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
// ...
}
表示每次尝试创建DrawerOpen.getInstance()
时都会调用DrawerOpen
,因为player
是成员变量。
要在DrawerOpen.getInstance()
中创建实例,您必须创建DrawerOpen
的实例;但是再次调用DrawerOpen.getInstance()
,要求您创建DrawerOpen
,调用DrawerOpen.getInstance()
...
您可以通过将DrawerOpen.getInstance()
分配给静态字段来避免此递归调用:
public class DrawerOpen implements State {
private static DrawerOpen INSTANCE = DrawerOpen.getInstance();
private DVDPlayer player = DVDPlayer.getInstance(INSTANCE);
// ...
}
然而,还有另一个问题,即您在致电INSTANCE
时未能指定DVDPlayer.getInstance(INSTANCE)
,因此您最终会调用DVDPlayer.getInstance(null)
。
解决此问题的一种方法是致电:
private DVDPlayer player = DVDPlayer.getInstance(this);
但是,这是所谓的 unsafe publication 的示例,其中您在this
类完全初始化之前泄漏了DrawerOpen
引用。这可能会导致进一步的意外问题。
你的代码有点混乱。你应该仔细考虑一下你为什么要使用单身人士,因为这些对我来说不像单身人士属性。
例如,有点狡猾,你有一个&#34;单身&#34;这取决于一个参数:如果使用不同的DVDPlayer.getInstance
实例调用State
,则当前忽略新的状态实例,并使用前一个实例。这很可能导致令人困惑或令人惊讶的行为:我给了你这个状态实例,但DVDPlayer正在使用那个状态实例。
答案 1 :(得分:0)
更改这两行:
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
private DrawerOpen() {}
到此:
private DVDPlayer player;
private DrawerOpen() {
player = DVDPlayer.getInstance(this);
}
这将摆脱递归初始化循环。
说明:构造函数中的this
引用正在构造的对象。这是DrawerOpen.getInstance()
返回的 的单例...如果对象是在代码中的那个点构造的。