简体   繁体   English

概括具有不同一元运算符的两个相似方法

[英]Generalize two similar methods that have different unary operators

Suppose I have the following implementations假设我有以下实现

private static String encryptPassword(String password) {
    String encryptedPassword = "";
    char[] chars = password.toCharArray();
    for (char c : chars) {
        encryptedPassword = encryptedPassword.concat(String.valueOf(++c));
    }
    return encryptedPassword;
}

private static String decryptPassword(String encryptedPassword) {
    String decryptedPassword = "";
    char[] chars = encryptedPassword.toCharArray();
    for (char c : chars) {
        decryptedPassword = decryptedPassword.concat(String.valueOf(--c));
    }
    return decryptedPassword;
}

These methods do very simple stuff that it will increase each character in a string by 1 and this process is considered as encryption.这些方法做的事情非常简单,它将字符串中的每个字符增加 1,这个过程被认为是加密。 As a contrast, the process of decryption will decrease the value.相反,解密过程会降低价值。

I can see that these two methods are pretty much the same, except the unary operators.我可以看到这两种方法几乎相同,除了一元运算符。

How can I generalize these two methods?我如何概括这两种方法?

Furthermore, I also prefer streaming processing to looping.此外,我也更喜欢流式处理而不是循环。 If you have any ideas regarding this point please posting as well.如果您对这一点有任何想法,请也发帖。

You can make use of the Java 8 Functional programming features to provide the behavior expressed as a Function as a method argument.您可以使用 Java 8 函数式编程功能来提供表示为 Function 的行为作为方法参数。

Here's how your logic can be implemented using Stream API, the additional argument the method expects is of type IntUnaryOperator ( which is a function expecting a single int argument, and producing int value as the result ).下面是如何使用 Stream API 来实现您的逻辑,该方法期望的附加参数是IntUnaryOperator类型(这是一个 function 期望单个int参数,并生成int值作为结果)。

public static String processPassword(String password,
                                     IntUnaryOperator operator) {
    return password.chars()
        .map(operator)
        .collect(
            StringBuilder::new,
            StringBuilder::appendCodePoint,
            StringBuilder::append
        )
        .toString();
}

And here's how you can call this method from the client code:以下是如何从客户端代码调用此方法:

String encrypted = processPassword("fooBar", i -> ++i);
System.out.println(encrypted);
String decrypted = processPassword(encrypted, i -> --i);
System.out.println(decrypted);

Output: Output:

gppCbs
fooBar

But, it's error-prone (for instance, a function with ++i can be accidentally instead of a function with --i ), and has a maintainability issue because you might end up with repeating these functions in several places, so it would be harder to change them.但是,它容易出错(例如,带有++i的 function 可能会意外地代替带有 --i 的--i ),并且存在可维护性问题,因为您最终可能会在多个地方重复这些函数,因此它会更难改变它们。

The better approach would be to abstract the client from the actual implementations of the functions used in encrypting/decrypting and store these functions in one place, so that they can be easily modified without effecting the rest of the code.更好的方法是将客户端从加密/解密中使用的函数的实际实现中抽象出来,并将这些函数存储在一个地方,这样就可以很容易地修改它们,而不会影响代码的 rest。

This can be achieved by introducing an enum .这可以通过引入enum来实现。

public enum ProcessType {
    ENCRYPT(i -> ++i),
    DECRYPT(i -> --i);
    
    private IntUnaryOperator operator;

    // constructor, getter
}

And we need to modify the method shown above to make it consume an enum constant instead of a function:我们需要修改上面显示的方法,使其使用枚举常量而不是 function:

public static String processPassword(String password,
                                     ProcessType type) {
    return password.chars()
        .map(type.getOperator())
        .collect(
            StringBuilder::new,
            StringBuilder::appendCodePoint,
            StringBuilder::append
        )
        .toString();
}

And that's how the client code would look like:这就是客户端代码的样子:

String encrypted = processPassword("fooBar", ProcessType.ENCRYPT);
System.out.println(encrypted);
String decrypted = processPassword(encrypted, ProcessType.DECRYPT);
System.out.println(decrypted);

Output: Output:

gppCbs
fooBar

Define a enum to specify operaiton type, based on the enum you can apply business logic.定义一个枚举来指定操作类型,基于枚举你可以应用业务逻辑。

private static enum OperationType {
        ENCRYPTION(1), DECRYPTION(-1);

        int val;

        private OperationType(int val) {
            this.val = val;
        }

    }

    private static String process(final String data, final OperationType operationType) {
        final char[] chars = data.toCharArray();

        final StringBuilder builder = new StringBuilder();
        for (char ch : chars) {

            builder.append(String.valueOf(ch + operationType.val));
        }
        return builder.toString();

    }

To work with streams, you can use string chars method like below.要使用流,您可以使用如下所示的字符串字符方法。

Stream<Character> charStream = data.chars().mapToObj(i->(char)i);

The difference between the two methods encryptPassword and decryptPassword is the value that you are adding to each char in the String . encryptPassworddecryptPassword这两种方法的区别在于您要添加到String中每个char的值。 Therefore I suggest adding a third method that takes two parameters: the first is the String to encrypt (or decrypt) and the second is the value to add to each char .因此,我建议添加第三种方法,该方法采用两个参数:第一个是要加密(或解密)的String ,第二个是要添加到每个char的值。 In the below code, I called this third method adjustPassword .在下面的代码中,我调用了第三种方法adjustPassword

As described in the book Java by Comparison I suggest keeping methods encryptPassword and decryptPassword and have those methods call method adjustPassword with the appropriate value.Java书中所述,我建议保留方法encryptPassworddecryptPassword ,并让这些方法使用适当的值调用方法adjustPassword That way, when you want to encrypt or decrypt a password, you don't need to remember what value to pass for the required operation.这样,当您想要加密或解密密码时,您无需记住为所需操作传递的值。

Explanations after the code.代码后的解释。

public class Solution {

    private static String encryptPassword(String password) {
        return adjustPassword(password, 1);
    }

    private static String decryptPassword(String encryptedPassword) {
        return adjustPassword(encryptedPassword, -1);
    }

    private static String adjustPassword(String password, int adjustment) {
        StringBuilder sb = new StringBuilder(password.length());
        password.chars()
                .map(c -> c + adjustment)
                .forEach(i -> sb.append((char) i));
        return sb.toString();
    }

    public static void main(String[] args) {
        String password = "George";
        System.out.println("Original: " + password);
        String encrypted = encryptPassword(password);
        System.out.println("Encrypted: " + encrypted);
        System.out.println("Decrypted: " + decryptPassword(encrypted));
    }
}

Running the above code produces the following output:运行上面的代码会产生以下 output:

Original: George
Encrypted: Hfpshf
Decrypted: George
  • Since Java 9, class String has method chars() which returns an IntStream since char is essentially an int .由于 Java 9,class String具有方法chars()返回IntStream因为char本质上是一个int
  • Class StringBuilder has overloaded append methods. Class StringBuilder重载append方法。 In the forEach method, the type of the method parameter, ie i , is int , hence the cast to char , otherwise method adjustPassword would return a String that only contained digits.forEach方法中,方法参数的类型,即i ,是int ,因此转换为char ,否则方法adjustPassword将返回一个仅包含数字的String

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM