Home > Net >  Best way in Java to hide public setters from client applications
Best way in Java to hide public setters from client applications

Time:04-19

I have a Spring Application which is built on top of DynamoDB. I am trying to write a saveFoo() Repository method that will save an object of type Foo to the database. This method will be called from the application layer.

What I'm struggling with is that the Foo class has fields within it that are Dynamo specific. I don't want clients of the saveFoo() class to think they need to create an object of type Foo with these fields. Nor do I want them to attempt to ever set these values themselves.

These database-specific fields need public getters and setters in order to work with the DynamoDB SDK.

The database model class looks like the following:

@DynamoDBTable(tableName = "foo")
public class Foo {
  // Fields the client should be setting
  private String bar;
  private String baz;

  // Fields the client should not access and should not care about. They are internal fields used for DynamoDB purposes. 
  private Long version;
  private String gsiIndexKey;

  // Empty constructor needed for deserialization of data from dynamodb
  public Foo() {
  }

  // Getters and setters for all of the above fields.
}

And the repository method to save the object to the DB:

public class FooRepositoryImpl {
  public Foo saveFoo(WhatClassShouldThisBe foo) {
    // Code that saves a new Foo item to the database and returns it.
  }
}

My current idea is to create a FooWrapper interface that the saveFoo() method can take as a parameter. The wrapper will allow the client to set the fields that they should have control over, but does not expose fields related to the database internals. Something like the below:

/**
* Allows us restrict visibility of the Foo fields to the Application by hiding database internal fields.
**/
public interface FooWrapper {
  Foo getFoo()
}

public class FooWrapperImpl implements FooWrapper {
  private final Foo foo;
  
  public FooWrapperImpl(String bar, String baz) {
    foo = new Foo();
    foo.setBar(bar);
    foo.setBaz(baz);
  }
  
  @Override
  public Foo getFoo() {
    return foo;
  }
}

public class FooRepositoryImpl {
  public Foo saveFoo(FooWrapper fooWrapper) {
    Foo foo = fooWrapper.getFoo(); // Save this item to db
    // Code that saves a new Foo item to the database and returns it.
  }
}

What are your thoughts on this approach? Does anyone know of some better techniques I could try? I can't help but feel that I am over-engineering things here.

CodePudding user response:

Expose only an interface to clients that has the methods you want. Internally you can call the other public methods of the implementation, but the client code won't know them.

If you are using modern Java, don't export your implementation classes outside of your module. Only export the public interface.

You could also keep an interface for internal use that has more methods than the public API.

public interface Foo {
    public void setBar(String bar);
    public void setBaz(String baz);
}
@DynamoDBTable(tableName = "foo")
public class FooImpl implements Foo {
  // Fields the client should be setting
  private String bar;
  private String baz;

  // Fields the client should not access and should not care about. They are internal fields used for DynamoDB purposes. 
  private Long version;
  private String gsiIndexKey;

  // Empty constructor needed for deserialization of data from dynamodb
  public FooImpl() {
  }

  // Getters and setters for all of the above fields.
  @Override
  public void setBar(String bar) {
    this.bar = bar;
  }

  @Override
  public void setBaz(String baz) {
    this.baz = bar;
  }

  // Not part of the Foo interface
  public void setVersion(Long version) {
    this.version = version;
  }

  public void setGsiIndexKey(String indexKey) {
    this.gsiIndexKey = indexKey;
  }
}
  • Related