将Serializable引入现有代码

时间:2016-11-21 12:23:09

标签: java

我认为这应该是一个非常常见的案例,但我无法找到任何最佳做法。假设我有以下课程:

public class Equation {

    private Operator operator;
    private Object leftValue;
    private Object rightValue;

    // getters and setters
}

public enum Operator  {...}

这门课已经和我们一起使用了好几年了,并且使用得很好。现在我需要使它可序列化。我该怎么做?

只需添加implements Serializable

即可

在这种情况下,Equation类仅在值为Serializable时才有效。方程式只对数字(可能是日期和字符串?)真正起作用。但是值可以是任何类型Object,因此必须有更好的方法。

制作值Serializable

public class Equation implements Serializable{

    private Operator operator;
    private Serializable leftValue;
    private Serializable rightValue;

    // getters and setters 
}

这在任何情况下都有效,但这些更改是API中断。无论我做什么,我都需要使用类更改所有代码,这可能导致更多的API中断。对于可能需要很长时间的大型软件系统。

创建值Serializable,保持getter和setter不变

public void setLeftValue(Object leftValue) {
    if (!(leftValue instanceof Serializable)) 
        throw new IllegalArgumentException("Value must be Serializable!");
    this.leftValue = leftValue;
}

此代码不会破坏现有的API,但会更改代码的行为方式。然而,如果我假设所有的值都是Serializable,我觉得这可能是要走的路。我甚至可以将新的setter放在旧的setter旁边,并弃用它们以使未来的开发人员明白要使用的对象。

制作值transient

至少是Sonar建议的那个。然而,它导致一个无法使用的课程,至少在我们实际需要EquationSerializable的所有情况下。

创建Serializable的实施:

public class SerializableEquation extends Equation implements Serializable{

    private Serializable leftValue;
    private Serializable rightValue;

    // override getters and setters
}

这样我们就不得不使用整个不同的类来进行序列化,这看起来有点难看,不是吗?

问题:

处理此用例的好方法是什么?我理想情况下不想打破API。看到Java尚未打破API,必须有办法处理这样的情况。

1 个答案:

答案 0 :(得分:1)

在这种问题中,正确的实现应该使用接口来限制行动领域。

UrlConnection添加到String有什么意义?嗯,这应该是这样的:

public class Equation {
    Operator operator;
    Operand leftOp, rightOp;

    ...
}

interface Operand {

    ...
}

然后,对于特定类型的数据,您将实现特定的类

public IntegerOperand implements Operand {

    public Integer value;
    ...
}

由此,您只需将Serialiszable添加到OperatorOperand。这将是开发人员需要遵循的合同,因此每个实现都需要可序列化(完全可序列化,因为接口要求它),这将很容易使用JUnit进行测试。

但是

在您的情况下,您无法更新代码,因为这会破坏兼容性。所以我会把序列化放到测试中,这意味着我会检查对象实例是否可序列化,如果没有,那么你就可以用它做你想做的事。

您也可以根据需要(在序列化之前)使用方法检查,以防止无法使用此数据或使用这两个值的setter来完成此操作,从而限制可能性。

public boolean isSerialisable(){ 
    return Serializable.class.isAssignable(leftValue.class)
            && Serializable.class.isAssignable(rightValue.class);
}

这将在您需要序列化实例之前调用,作为警告或错误。 (或者如果你想打破一切,直接在制定者中;)

最后,你自己序列化数据,你可以使用一些库生成不同类型的结构,XML,JSON,..或者可能直接以字节为单位(没有例子)

编辑:

以下是使用Object

进行序列化的快速(丑陋示例)
public class Main implements Serializable{

    public Object value;

    public Main(Object o){ this.value = o; }

    public static void main(String[] args){
        Main m = new Main(new A());

        try {
             FileOutputStream fileOut = new FileOutputStream("test.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(m);
             out.close();
             fileOut.close();
        }catch(IOException i) {
            i.printStackTrace();
        }

        m = null;
        try {
            FileInputStream fileIn = new FileInputStream("test.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            m = (Main) in.readObject();
            in.close();
            fileIn.close();
        }catch(Exception i) {
            i.printStackTrace();
            return;
        }
        System.out.println(" M : " + m);
    }

    @Override
    public String toString() {
        return value == null ? "null" : value.toString();
    }

    static class A implements Serializable{
        String s = "foo";

        @Override
        public String toString() {
            return s;
        }
    }
}

如果从serializable A移除实施,则会失败。但是像这样,我们发现这是预期的。