2017-07-19 26 views
-1

TL; DR:我希望超类能够调用子类中的函数,而不需要不同的子类能够调用彼此的重写函数。我可以用一个Handler接口来实现这一点,子接口实例化后调用私有方法,但感觉很乱(见下面的代码)。有没有更好的办法?创建方法的最好方法是什么,只有超级(而不是其他子类)可以调用它们?


我正在写一个简单的Java游戏,玩家将在学习编程时由学生编写游戏。游戏是一对一的,每个玩家都会告诉游戏引擎他们想要采取什么行动,并在周围定购​​单位。

我试图让游戏允许学生调用任何对他们可见的函数。目标是缺乏反思,无法作弊。在大多数情况下,这可以通过将Player子类放在与游戏引擎不同的包中轻松完成。

我遇到的问题是我需要玩家重写诸如doTurn,endGame和newGame等方法。但是,没有办法让这些可见的被覆盖,而不会让其他玩家可以看到它们。玩家需要能够互相访问以检查单位所有权等。

我现在的解决方案是有一个接口让子类把它们的方法包装进来,以便超类可以调用它们。

public abstract class Player { 
    public final String NAME; 
    private Handler handler; 

    public Player(String name) { 
     handler = getHandler(); 
     NAME = name; // note this is initialized AFTER handler 
    } 

    protected abstract Handler getHandler(); 

    // can be called by stuff in this package (engine) 
    // but not another package (where players are) 
    void doTurn() { 
     handler.doTurn(); 
    } 

    protected interface Handler { 
     void doTurn(); 
    } 
} 
另一个包

...

public class BasicPlayer extends Player { 
    public BasicPlayer() { 
     super("BasicPlayer"); 
    } 

    private void secretStrategies() { 
     // Actual logic goes here 
    } 

    @Override 
    protected Handler getHandler() { 
     // Null check makes handler only visible during super 
     return NAME != null ? null : new Handler() { 
      @Override 
      public void doTurn() { 
       secretStrategies(); 
      } 
     }; 
    } 
} 

我想初始化处理程序传递到超级匿名类,但不起作用,因为那么就不能“指的是实例方法/字段,同时显式调用构造函数。“也就是说,直到超级构造函数实际发生时,BasicPlayer和Player实例还不存在。因此,为了避免玩家访问彼此的处理程序,getHandler必须仅适用于super,所以NAME!= null就在那里。这对我来说真的很难受。如果我能够通过匿名课程,即使它仍然有点不可思议,但感觉不会太差。除了是非常奇怪的设计之外,我不能让玩家把他们所有的变量/逻辑放到一个匿名的Handler中传递给超级构造器,因为在Player中有函数需要他们调用,而这些函数是即使声明为final(仍然是“实例方法”)(这对编译器来说是有意义的,但仍然令人讨厌)。

有没有更好的实现方法?

+0

如果没有更多信息,很难提供建议。但是:1.如果两个玩家PlayerA和PlayerB都在自己的包中,那么'PlayerB'不应该仅仅通过引用'Player'来调用PlayerA的'doTurn()'。 2.我会避免让'玩家'互相访问。如果他们需要检查“单位所有权”等,则通过游戏引擎的API来执行。 –

+0

@AdrianShum这实际上是很好的方式,并且JCL中有很多示例([示例](https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#addAll- java.util.Collection-))。你所要做的就是指定一个不同的接口用于与对象交互。查看我的回答 –

+0

@VinceEmigh对不起,我不知道你的评论是指哪一点我的意见 –

回答

0

我不确定我是否了解您的问题。你想授予一个方法的访问权限,但是如果调用者也是一个玩家呢?玩家由Player.class标识。您可以使用默认方法实现接口。该方法可以解释调用层次结构。类似这样的:

public class Player implements PlayerInterface { 

    public void doTurn() { 
     if (callerIsPlayer()) return; 
     secretStrategies(); 
    } 
} 

public interface PlayerInterface { 

default public boolean callerIsPlayer() { 
    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); 
    for (int i = 1; i < stackTraceElements.length; i++) { 
     if (stackTraceElements[i].getClassName().endsWith("Player")) { 
      return true; 
     } 
    } 
    return false; 
} 

也许这有助于某种程度。

+0

谢谢你的堆栈跟踪想法!不幸的是,它不是我想要做的,但它肯定是一个有用的相关工具。 我想授予有限的可视性,而不仅限于有限的访问。我并不担心人们在游戏中作弊(代码没有隐藏或者任何东西),对于我来说,以这种方式实现对我来说更是一种好奇心。 – nameless

0

让我们尝试一下其他方法:隐藏方法的类是什么?像这样:

public class Player { 

    public void doTurn() { 

     SecretStrategies secretstrategies = new SecretStrategies(); 
     secretstrategies.doSomething(); 
    } 

    private class SecretStrategies { 

     public void doSomething() { 
      System.out.println("Secret"); 
     } 

    } 
} 
+0

问题是其他玩家仍然可以拨打doTurn。 – nameless

0

这是接口的用途。

你可以有2个接口:一个用于Player子类进行交互(暴露之类的东西getUnits,正如你所提到的):

interface TargetPlayer { 
    List<Unit> getUnits(); 
} 

和一个用于任何需要打电话给newGame

interface Processable { 
    void newGame(); 
} 

Player将实现这两​​个接口:

abstract class Player implements TargetPlayer, Processable { 
    private List<Unit> units; 

    // can implement newGame, or force subtypes to implement it 

    public final List<Unit> getUnits() { 
     return units; 
    } 
} 

亚型可以通过TargetPlayer接口与其他Player对象交互:

class PlayerSubOne extends Player { 

    // don't use Player, use TargetPlayer 
    public void interactWith(TargetPlayer player) { 
     List<Unit> units = player.getUnits(); // can access units 

     player.newGame(); // error, doesn't exist in TargetPlayer 
    } 

    @Override 
    public void newGame() { 

    } 
} 

这是一个简单的解决。我将用更深入(和坚固)的设计来编辑答案,因为这里仍然存在一些违反原则的行为。


调用在同一类的构造一个抽象方法应避免,因为它may lead to bugs

+0

我明白你在做什么,这是一个有趣的选择!不幸的是,它不适用于我想要做的其他原因。反对调用抽象方法是为什么我问这个问题。虽然我这样做的方式是“安全的”,但这不是一个好的设计。 – nameless

+0

@nameless你这样做的方式是*不安全(请参阅我在答案底部的链接)。如果你不解释这些“其他原因”,我不能提供帮助。你能更具描述性吗?你为什么试图避免调用抽象方法?从来电者的角度来看,这没有什么区别。抽象方法只需要实现,而你已经有了这个实现。所以你要做的就是创建不同的方式来与同一个对象进行交互,具体取决于你正在与之交互的界面。 –

0

我相信最简单的答案是尝试泛型。如果目的只是为了接受作为输入,类和其超类的实例,那么我会建议如下:

public class A{} 
public class B extends <? super A>{} 

这基本上可以归结为B伸出的任何类,它是A或它就像超类目的。

+0

我不太明白这是如何帮助我的,抱歉。 – nameless

相关问题