У меня есть класс данных, который использует конструктор для создания объекта и сохраняет данные в буфере в сериализованной форме. Я планирую изменить класс, чтобы добавить и удалить некоторые поля. Существуют системы, которые будут использовать как версию класса для создания данных, то есть текущую версию со всеми полями, так и более новую версию с удаленными / добавленными полями. Я пытаюсь выяснить, каков наилучший способ сделать это, чтобы он был обратно совместим (не нарушая никакого потребителя)?
У меня есть пара предложений о том, как это сделать, но мне трудно выбрать одно из другого.
Требования:
Сохраненные данные должны быть в двоичном формате.
Длина сериализованной записи одинакова в обеих версиях
Существующий код
public class A implements Comparable<A>, Serializable {
private final Buffer buffer;
public static final Builder {
private Header header//header with version
private long creationTime;
private SomeObject someObject;//this is removed in next version
private OtherObject otherObject;//this is added in next version
public Builder() { }
//bunch of getters setters for fields
public A build() {return new A(this);}
private A(Builder b) {
//build the object and put into the buffer
validate()
}
private void validate() {//validates the object}
public A(Buffer buf) {
this.buffer=buf;
validate();
}
public A(String encodedString) {
this(ByteBuffer.wrap(encodedString));
}
}
// consumers use this to get creationTime for object A
public long getCreationTime() {
return buffer.getLong(OFFSET_CREATION_DATE);
}
}
Solution1: добавить новые поля в компоновщик и использовать версию в заголовке, чтобы решить, какие поля использовать во время компоновки (в методе компоновки) для создания объекта. Проблема этого подхода заключается в том, что все методы будут существовать во время компиляции для потребителей, и если они не протестируют свой код, каждый объект будет действительным. Поэтому будет сложно определить, какие поля требуются для какой версии во время сборки.
Solution2: Добавьте нового построителя в классе с полями, которые вы хотите. Будут повторяющиеся поля в существующем компоновщике.
Потребители могут тогда использовать строителя, которого они хотят. Это кажется чище, потому что строители будут полностью независимы. Проблема этого подхода заключается в том, что, поскольку мы добавляем и удаляем поля, поля будут иметь разные смещения, поэтому получателям придется изменить, чтобы использовать смещение, основанное на versionType. Это также проблематично для будущих версий, потому что тогда у нас будет этот гигантский класс с множеством компоновщиков и логикой в геттерах для каждой версии
Solution3: Создайте новый класс (скажем, B), который расширяет A и имеет свой собственный конструктор. Таким образом, код является более модульным. Проблема в том, что теперь где-то должна быть логика, чтобы различать и знать, какой конструктор вызывать. Например, если потребитель передает base64 для получения объекта A, ему необходимо выяснить, какая это версия.
String encodedString = "someString form of A"
A a = new A(encodedString);
Есть ли рекомендуемый способ кодирования этих классов данных с помощью шаблонов компоновщика, чтобы сделать его совместимым как в будущем, так и в обратном направлении.