killbill-api
killbill-api copied to clipboard
Create POJOs for (some) interfaces
We would like to start by creating POJOs matching the interfaces we have defined in our catalog module. For instance, given the interface Plan, we would like to create a class ApiPlan (this is a suggestion for the naming, perhaps there is something better) that implements the interface.
The goal is to make it easier for folks writing plugin and avoid having them to write all kinds of POJOs themselves.
Each of these POJO would need to have:
- Default CTOR
- CTOR with all fields
- All fields
private final toStringmethodequalsandhashCodemethod
Proposal
Scenario A: POJO
// Existing interface -- simple POJO
interface POJO {
DateTime getValueA();
Long getValueB();
}
// Provided default immutable implementation for POJO -- ready to use with builder pattern
class DefaultPOJO implements POJO {
private final DateTime valueA;
private final Long valueB;
public DefaultPOJO(final DateTime valueA,
final Long valueB) {
this.valueA = valueA;
this.valueB = valueB;
}
@Override
public DateTime getValueA() {
return valueA;
}
@Override
public Long getValueB() {
return valueB;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("DefaultPOJO{");
sb.append("valueA=").append(valueA);
sb.append(", valueB=").append(valueB);
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DefaultPOJO that = (DefaultPOJO) o;
// Need to use compareTo for DateTime, LocalDate, etc.
if ((valueA != null &&
that.valueA != null &&
valueA.compareTo(that.valueA) != 0) ||
((valueA != null && that.valueA == null) ||
(valueA == null && that.valueA != null))) {
return false;
}
// Otherwise, use Objects.equals
return Objects.equals(valueB, that.valueB);
}
@Override
public int hashCode() {
int result = Objects.hashCode(valueA.hashCode());
result = 31 * result + Objects.hashCode(valueB.hashCode());
return result;
}
public static class DefaultPOJOBuilder {
private DateTime valueA = null;
private Long valueB = null;
public DefaultPOJOBuilder withValueA(final DateTime valueA) {
this.valueA = valueA;
return this;
}
public DefaultPOJOBuilder withValueB(final Long valueB) {
this.valueB = valueB;
return this;
}
public DefaultPOJO build() {
return new DefaultPOJO(valueA, valueB);
}
}
}
// Example usage
class MyPlugin {
public static void main() {
final DefaultPOJO defaultPOJO = new DefaultPOJOBuilder().withValueA(DateTime.now())
.withValueB(0L)
.build();
}
}
Scenario B: POJO with abstract methods
// Existing interface -- POJO with abstract methods to implement
interface POJOWithMethod {
int getValue();
int doSomething(boolean input);
}
// Provided default implementation for POJOWithMethod -- ready to use
class DefaultPOJOWithMethod implements POJOWithMethod {
private final int value;
public <T extends DefaultPOJOWithMethodBuilder<T>> DefaultPOJOWithMethod(final DefaultPOJOWithMethodBuilder<?> builder) {
this.value = builder.value;
}
@Override
public int getValue() {
return value;
}
@Override
public int doSomething(final boolean input) {
// Default implementation
throw new UnsupportedOperationException("doSomething must be implemented");
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("DefaultPOJOWithMethod{");
sb.append("value=").append(value);
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DefaultPOJOWithMethod that = (DefaultPOJOWithMethod) o;
return value == that.value;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
// Generified, to allow for custom builders (optional): https://stackoverflow.com/a/17165079
public static class DefaultPOJOWithMethodBuilder<T extends DefaultPOJOWithMethodBuilder<T>> {
private int value;
public T withValue(final int value) {
this.value = value;
return (T) this;
}
public DefaultPOJOWithMethod build() {
return new DefaultPOJOWithMethod(this);
}
}
}
// Example usage with custom implementation
class MyPOJOWithMethod extends DefaultPOJOWithMethod {
public MyPOJOWithMethod(final DefaultPOJOWithMethodBuilder<?> builder) {
super(builder);
}
@Override
public int doSomething(final boolean input) {
return input ? 0 : -1;
}
}
class MyPlugin {
public static void main() {
final MyPOJOWithMethod myPOJOWithMethod = new MyPOJOWithMethod(new DefaultPOJOWithMethodBuilder().withValue(0));
}
}