1. 概述

在这个教程中,我们将讨论如何在Java中实现无状态对象。无状态对象是不包含实例字段的类的实例。

在Java中,我们所有的代码都必须放在一个类中。编写算法时,我们可能只需要在一个类中提供静态方法来实现这一点。

然而,有时我们希望将我们的算法绑定到一个无状态的对象上。

2. 关于状态的对象概述

当我们想到Java中的对象时,通常会想到那些包含在字段中的状态,以及根据这些状态操作的方法来提供行为的对象。

除此之外,我们还可以创建具有不可修改字段的对象。这些对象的状态在创建时定义,然后是不可变的,因为它们的状态不会改变。在并发操作中,不可变对象受益于与无状态对象相同的优点。

最后,我们有既没有字段,或者只有编译时常量字段的对象。这些对象是无状态的。

让我们来看看为什么我们可能希望使用无状态对象。

3. 使用无状态对象

以一个没有状态的排序算法为例,比如我们选择冒泡排序

public void sort(int[] array) {
    int n = array.length;
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j  array[j + 1]) {
                int swap = array[j];
                array[j] = array[j + 1];
                array[j + 1] = swap;
            }
        }
    }
}

3.1. 多个无状态排序实现

现在我们想添加使用其他排序算法的可能性,例如快速排序,它也是无状态的:

public void sort(int[] array) {
    quickSort(array, 0, array.length - 1);
}
    
private void quickSort(int[] array, int begin, int end) {
    if (begin < end) {
        int pi = partition(array, begin, end);
        quickSort(array, begin, pi - 1);
        quickSort(array, pi + 1, end);
    }
}    

private int partition(int[] array, int low, int high) {
    int pivot = array[high];
    int i = low - 1;
    for (int j = low; j < high; j++) {
        if (array[j] < pivot) {
            i++;
            int swap = array[i];
            array[i] = array[j];
            array[j] = swap;
        }
    }
    int swap = array[i + 1];
    array[i + 1] = array[high];
    array[high] = swap;
    return i + 1;
}

3.2. 选择实现之间的切换

假设我们决定在运行时决定使用哪个算法。

我们需要一种在运行时选择正确排序算法的方式。为此,我们使用的一种设计模式称为策略设计模式

为了在我们的例子中实现策略模式,我们将创建一个名为SortingStrategy的接口,其中包含sort()方法的签名:

public interface SortingStrategy {   
    public void sort(int[] array);
}

现在我们可以将每个排序策略实现为实现此接口的无状态对象。这样,我们可以在任何时候切换到喜欢的实现,而消费代码则使用传递给它的任何排序对象:

public class BubbleSort implements SortingStrategy {

    @Override
    public void sort(int[] array) {
        // Bubblesort implementation
    }
}

public class QuickSort implements SortingStrategy {

    @Override
    public void sort(int[] array) {
        // Quicksort implementation
    }
    // Other helpful methods
}

这里,我们的类不包含字段,因此没有状态。但是,由于存在对象,它可以满足我们为所有排序算法定义的通用接口——SortingStrategy

3.3. 单例无状态实现

我们希望为用户提供所选排序策略的方法。由于类是无状态的,我们不需要从它们中获取多个实例。因此,我们可以使用单例设计模式来实现这一点。

对于策略实例,我们可以使用Java的枚举来实现这个模式。让我们将class类型更改为enum,并添加一个常量INSTANCE。这个常量实际上是一个特定排序算法的单例无状态实例。由于枚举可以实现Java接口,这是一种提供策略对象单例的整洁方式:

public enum BubbleSort implements SortingStrategy {
    
    INSTANCE;

    @Override
    public void sort(int[] array) {
        // Bubblesort implementation
    }
}

public enum QuickSort implements SortingStrategy {
    
    INSTANCE;

    @Override
    public void sort(int[] array) {
        // Quicksort implementation
    }
    // Other helpful methods
}

3.4. 测试排序策略

最后,我们编写测试以确保两个排序策略都能正常工作,并且易于维护:

@Test
void givenArray_whenBubbleSorting_thenSorted() {
    int[] arrayToSort = {17, 6, 11, 41, 5, 3, 4, -9};
    int[] sortedArray = {-9, 3, 4, 5, 6, 11, 17, 41};
        
    SortingStrategy sortingStrategy = BubbleSort.INSTANCE;
    sortingStrategy.sort(arrayToSort);
    assertArrayEquals(sortedArray, arrayToSort);
}
    
@Test
void givenArray_whenQuickSortSorting_thenSorted() {
    int[] arrayToSort = {17, 6, 11, 41, 5, 3, 4, -9};
    int[] sortedArray = {-9, 3, 4, 5, 6, 11, 17, 41};
        
    SortingStrategy sortingStrategy = QuickSort.INSTANCE;
    sortingStrategy.sort(arrayToSort);
    assertArrayEquals(sortedArray, arrayToSort);
}

4. 总结

在这篇文章中,我们探讨了Java中的无状态对象。

我们了解到无状态对象对于无需状态的算法很有用,还了解了如何实现策略模式。

如往常一样,代码示例可以在GitHub上找到。