Home » Java » How to create two interfaces to a Java class one read-only, one read-write?

How to create two interfaces to a Java class one read-only, one read-write?

Posted by: admin December 28, 2021 Leave a comment

Questions:

I’m writing a game engine in Java for a two player card game for which my students are going to write AI players.

The AI players will take turns playing cards onto their ‘field’ a part of the ‘table’ in front of them. They can attack with a card from their field a card in the other player’s field. Cards may be face up or face down.

The GameEngine class allows an AI player to take his/her turn by calling the GamePlayer.TakeTurn(GameEngine eng) method. The player can ask the game engine for the defending player’s field so the player can make decisions based on the number of cards there and which ones are face up. Let’s say this method is GameEngine.GetDefendingField()

Now, I want to make sure that the attacking player cannot modify the defending player’s field or the cards in the defending player’s field and that the attacking player can only identify the face up cards in the defending players field.

I have classes for Card, Field (an ArrayList of Cards), GamePlayer and GameEngine. Is there any way to create an interface for Field so that the GameEngine can return the defending player’s field without the attacking player being able to change it? and without the attacking player being able to recast the defending player’s field into something that can be changed.

Can I have GameEngine.GetDefendingField() return a read-only interface to a Field that cannot be recast to a Field?

tia,

Sh-

Answers:

If you want to achieve this without copying and without the possibility to cast the read-only interface reference to a corresponding read-write interface, you could try to use a wrapper approach.

For an interface like this:

interface Info {
    String getInfoA();
    void setInfoA(String infoA);
    String getInfoB();
    void setInfoB(String infoB);
}

You could create a readonly wrapper which ignores the setters or throws exceptions, like:

class ReadonlyInfo implements Info {
    final Info instance;
    ReadonlyInfo(Info info) { 
         if (null == info) { throw new InvalidArgumentException(); } 
         instance = info; 
    }
    String getInfoA() { return instance.getInfoA(); }
    void setInfoA(String infoA) { /** silent ignore */ }
    String getInfoB() { return instance.getInfoB(); }
    void setInfoB(String infoA) { throw new IllegalStateException(); }
}

By using the wrapper approach you can use polymorphism on the client side, be safe against casting the reference to get more access rights and chose between silently ignore called setters, or throwing an illegal access exception.

Note: of course you will not be safe against code that uses reflection to retrieve the wrapped object.

###

You’re talking basic Model-View-Controller here, with your Model being Card and Field, the Controller is GamePlayer and GameEngine, and the View is as-of-yet undefined. Your Field is your Model, and you would want to create 2 different interfaces that would access the Field model, one read-only and one read-write. Your read-write implementation would likely just return the Field object. Your read-only implementation would rip thru the elements in the Field and only return the ones that they have access to, doing a deep copy of each Card in the Field returned.

###

I like the idea of using type safety and polymorphism to control this sort of access control at compile time, especially when teaching.

A good way to approach it would be to have two Field interfaces. Maybe Field and MutableField, where the latter extends the former and adds the change methods. Your FieldImpl could of course implement both.

The problem, of course, is that you want to return different declared types (Field or MutableField) from your game engine based on whose field you are using. If you have a single model, you could use something like getMyField() and getOtherPlayerField() since it appears from the description that when making a call to getField() one knows exactly which field it is that they want.

###

You could create a read-only interface that only has getters. Extending that interface would be read-writer interface that adds setters.

###

You can definitely do this. It’s an approach called attenuation in the Object Capabilities literature. As long as the restricted type isn’t defined as a sub-type of the powerful type, casting the object won’t provide more authority.

###

You cannot define a Java interface that enforces read-only access to fields/members as that is an implementation detail defined in a class. (Indeed, how you store the data internally is an implementation detail)

I would suggest creating two implementations of your Field interface, one with read-only access and one that has read/edit. You can have each class implement the Field interface, perhaps with an AbstractField class as the parent class of each to cover common functionality.

You can then have constructors for each concrete class (read-only and read/write) to convert between the two, if necessary.

###

You could create two contracts – one that allows both get and set while the other supporting only get.

Example)

 public interface Info {
    void setA(String data);
    String getA();
 }

public interface ReadableInfo {
   String getA();
}

public class ReadableInformation implements ReadableInfo {
  private Info underlyingInfo;

  public ReadableInformation(Info info) {
    this.underlyingInfo = info;
  }

  @Override
  public String getA() {
    return underlyingInfo.getA();
  } 
}

With this, you can convert a mutable contract (Info) to one which only supports accessors (ReadableInfo). But this requires handing over object of a different type (Info vs ReadableInfo) based on who the client is.