不要重复自己 - 代码级别的实用提示
#java #cleancode #opp #dry

重复是软件中所有邪恶的根源,因为很容易复制代码,这可能会导致许多问题,例如代码维护困难,增加错误潜力和降低生产率。

重复是一个如此大的问题,以至于创建了原则。它的名字是不重复或干燥的。干燥的前提是,通过不在整个系统中重复相同的代码,开发人员可以提高代码的质量并使管理随着时间的流逝更加容易。

在这篇博客文章中,我与您分享了一些练习,您可以申请最小化代码重复。

以下是一些提示,尤其是在Java中:

  • 模块化您的代码:将代码分解为较小的可重复使用的功能或类。这使您可以封装特定功能并在多个位置使用,而无需重复代码。我详细撰写了有关此内容的详细信息here

  • 使用函数作为参数:而不是编写相同的代码,而是创建可以通过接受参数来处理不同方案的函数。这样,您可以使用不同的输入重复使用该功能,从而减少重复。

  • 采用库和框架:Java提供了大量的库和框架,可提供预构建功能。而不是重新发明轮子,而是利用这些库执行常见任务。这不仅节省了时间,还减少了重复代码的需求。

  • 抽象通用代码:确定代码库中的模式或共同功能,并将它们抽象成可重复使用的组件。通过创建抽象,您可以将重复的代码合并到一个地方并在需要的任何地方引用。

  • 单独关注:保持您的代码组织并保持清晰的关注点。代码库的不同部分应处理特定任务,而不会重叠职责。这有助于防止重复并促进更模块化和可维护的代码库。我详细撰写了有关此内容的详细信息here

在这篇文章中,我们将看到使用函数作为参数的干燥示例,然后查看如何抽象公共代码。

使用函数作为参数以达到干燥的示例

将函数用作方法的参数是减少代码重复并遵循编程中的干原理的常见方法。该技术称为通过行为这是一个示例,以说明使用函数参数如何帮助我们消除代码重复。

问题

假设我们有两个在列表上执行相同操作的函数,唯一的区别是它们在列表中的单个元素上操作的逻辑。

class NumberFilter {
    public static List<Integer> even(List<Integer> list) {
        List<Integer> evenList = new ArrayList<>();
        for (Integer i : list) {
            if (i % 2 == 0) {
                evenList.add(i);
            }
        }
        return evenList;
    }

    public static List<Integer> odd(List<Integer> list) {
        List<Integer> oddList = new ArrayList<>();
        for (Integer i : list) {
            if (!(i % 2 == 0)) {
                oddList.add(i);
            }
        }
        return oddList;
    }
}

解决方案

我们将创建接受整数列表和函数作为参数的方法过滤器()。该函数用于定义用于过滤数字的所需行为(甚至是奇数或任何其他过滤器)。

class NumberFiler {
    public static List<Integer> filter(List<Integer> list, Predicate<Integer> predicate) {
        return list.stream()
                .filter(el ->  predicate.test(el))
                .collect(Collectors.toList());
    }
}

这是主要方法

    public static void main(String[] args) {
        Predicate<Integer> even = num -> num % 2 == 0;
        NumberFiler.filter(Arrays.asList(1, 2, 3), even);
    }

当一种方法接受功能接口作为参数时,我们可以定义不同的行为并将其传递到在数据上执行不同操作的方法,而无需多次重复相同的逻辑。在这一部分的最后,我鼓励您对Java功能接口API进行研究,这是一个很好的开始point

可以使用策略模式

可以实现同样的级别。

Java中的示例如何抽象常见代码以达到干燥

抽象的常见代码并不意味着我们必须拥有一个抽象类,而只是如何做的方法之一。 抽象意味着隐藏无关紧要的代码来创建可重复使用的代码,该代码可以在各种上下文中使用,而不是为每个特定上下文重复相似的代码。让我们在实践中看看。

问题

要将数据保存在DB中,我们需要创建与数据库的连接,准备SQL语句,执行并收集结果,因此我们每次需要保存某些内容时重复这些代码块,这意味着我们有重复。<<<<<<<<<<< br>

try {
    Connection con=ConnectionProvider.getCon();
    Statement st=con.createStatement();
    ResultSet rs=st.executeQuery("Select * from  Appointments where DoctorID=123");
    while(rs.next()) {

解决方案

在第一步之后(识别重复的代码)。下一步是用隐藏的细节(抽象)提取它,并使其可以从一个点(可重复使用)访问。在Java中,这可以是一种方法或类。

public final class Repository {
    private static Repository repository = new Repository();

    private Connection connection;

    private Repository() {
        this.connection = ConnectionProviderDefault.getCon();
    }

    public static Repository getInstance() {
        return repository;
    }

    public <RESULT_ITEM> List<RESULT_ITEM> executeQuery(String query, Function<ResultSet, RESULT_ITEM> function) {
        List result = new ArrayList();
        try {
            Statement st= this.connection.createStatement();
            ResultSet resultSet = st.executeQuery(query);
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            while (resultSet.next()) {
                result.add(function.apply(resultSet));
            }
        } catch (Exception ex) {
            throw new RepositoryException();
        } finally {
            try {
                this.connection.close();
            } catch (SQLException e) {
                throw new RepositoryConnectionCloseException();
            }
        }
        return result;
    }

}

要提取重复的代码,隐藏无关紧要的细节并有一个新的访问点,我决定与Java Singleton类一起。

我正在使用另一条规则来达到干燥,您能找到它,这是很小的帮助。

public <RESULT_ITEM> List<RESULT_ITEM> executeQuery(String query, Function<ResultSet, RESULT_ITEM> function) {

什么时候应该去抽象类或接口?

如果您有一些变化,请选择抽象类或接口。
我们可能需要将数据保存到文件中,因此解决方案可以像这样

interface Repository {
   boolean save();
}

abstract class DBRepository implements Repository {
    private Connection connection; 
    // and other relevant DB things 
}

class FileRepository implements Repository {

    @Override
    public boolean save() {
        return false; // TODO implement
    }
}

干燥的挑战

尽管这篇文章的目标是与最佳实践分享您可以在代码级别应用以实施干燥原则的最佳实践。我必须指出,干燥(不重复自己)原理更宽,这是关于处理知识我们不想复制知识或意图,我们也不想表达在两个不同的地方,尤其是完全不同的方式。

我们所有人都面临的挑战是,并非所有代码重复都是知识重复。

如果您需要违反干燥的尝试来定位影响,以免违规暴露于外界。