如何避免API /库中的并行继承层次结构

时间:2017-11-06 20:12:16

标签: java design-patterns

我正在开发一个加密库,以简化我的团队对api凭据使用加密以及从其他团队/项目接收加密消息的方式。

我从这些顶级接口开始定义域名:

public interface Key {
    public byte[] getBytes();
}

public interface Message {
    public byte[] getBytes();
}

public interface Secret {
    public byte[] getBytes();
}

public interface Decrypter {
    public String decrypt(Secret secret, Key key);
}

public interface Encrypter {
    public Secret encrypt(Message message, Key key);
}

这适用于包装RSA加密:

public class KeyPair {
    private final Key publicKey;
    private final Key privateKey;

    public Key getPublicKey() {
        return publicKey;
    }

    public Key getPrivateKey() {
        return privateKey;
    }
}

public class RsaEncrypter implements Encrypter {

    @Override
    public Secret encrypt(Message message, Key publicKey) {
        // Perform Encryption
    }
}

public class RsaDecrypter implements Decrypter {
    @Override
    public String decrypt(Secret secret, Key privateKey) {
        // Perform the decryption
    }
}

但是现在我将它应用于AES加密用例我遇到了问题。秘密包含InitializationVector,因为我们在CBC模式下使用AES。

所以我得到了这个:

public class AesSecret implements Secret {
    private byte[] cipherText;
    private byte[] initilizationVector;

    @Override
    public byte[] getBytes() {
        return cipherText;
    }

    public byte[] getInitilizationVector() {
        return initilizationVector;
    }
}

public class AesDecrypter implements Decrypter {
    @Override
    public String decrypt(Secret secret, Key key) {
        try {
            return decrypt((AesSecret) secret, key);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("AesDecrypter only supports subclasses of AesSecret", e);
        }
    }

    public String decrypt(AesSecret secret, Key key) {
        // Do the AES Decryption
    }
}

ClassCastException使我认为我违反了Liskov替换原则,并引入了并行层次结构代码 - 气味。我已经阅读过访问者模式是这种代码味道的常见解决方案,但我还没弄清楚它是如何适用于我的情况的。

有什么建议吗?还是我过度思考这个?

我已将这些课程添加到要点:https://gist.github.com/mmeier/c493c28cbcd57a73d08419066cd23484

2 个答案:

答案 0 :(得分:1)

你可以让你的Decryptor变得通用。像这样:

interface Decryptor<S extends Secret>{
    public String decrypt(S secret, Key publicKey);
}

这样,Decryptor的每个实现都可以定义自己的秘密。这样你就可以摆脱你的铸造。

答案 1 :(得分:-1)

密码API“通常”具有init函数,然后是单独的解密/加密。如果您正在开发API,那么研究其他API以获取灵感总是一个好主意(Java已经有了一个可以使用的Cipher API)。另一方面,我也建议不要编写你的密码,而是使用像bouncycasle这样的库。