Puzzle Driven Development

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

PDD,即谜题驱动开发,是一种将编程任务分解为较小的任务并使其能够并行实施的方法。PDD方法广泛应用于xdsd方法论中。该方法正在等待美国专利商标局(USPTO)的专利申请(申请号12/840,306)。

让我们通过一个例子来回顾一下这种方法。假设你是一名程序员,被指定设计和实现一个Java类。这是正式的任务描述:“类DBWriter必须继承java.io.Writer抽象类,并将所有传入的数据保存到数据库中。”

你有一个小时来完成这个任务。显然,一小时是不够的,因为这个问题比分配的时间要复杂,需要更多的工作。此外,还有很多未知因素:

  • 我们需要保存什么信息,以及以什么格式保存?

  • 什么是DB模式?它是一个SQL还是NoSQL数据库?

  • 如何连接到数据库?JDBC?JPA?DAO?

  • 如何处理异常?

让我们在试图在最抽象的层次上解决问题时牢记所有这些未知因素。当然,我们首先进行一次测试:

import org.junit.*;
import static org.mockito.Mockito.*;
public class DBWriterTest {
  @Test
  void testSavesDataIntoDatabase() throws Exception {
    DataBridge mapper = mock(DataBridge.class);
    Writer writer = new DBWriter(mapper);
    try {
      writer.write("hello, world!");
    } finally {
      writer.close();
    }
    verify(mapper).insert("hello, world!");
  }
}

在上述测试中,我们定义了类的预期行为。这个测试无法编译,因为有两个缺失的类:DataBridgeDBWriter。让我们先实现桥接器:

import java.io.IOException;
public interface DataBridge {
  void insert(String text) throws IOException;
}

接下来,作者本身:

import java.io.IOException;
import java.io.Writer;
import java.utils.Arrays;
public class DBWriter implements Writer {
  private DataBridge bridge;
  public DBWriter(DataBridge brdg) {
    this.bridge = brdg;
  }
  @Override
  void flush() throws IOException {
  }
  @Override
  void close() throws IOException {
  }
  @Override
  void write(char[] cbuf, int off, int len) throws IOException {
    String data = new String(Arrays.copyOfRange(cbuf, off, off + len));
    this.bridge.insert(data);
  }
}

使用上述代码,我们解决了这个问题。在这个例子中,我们成功地设计、实现和测试了所需的“DBWriter”类。随后,其他类现在可以立即使用它“原样”。

当然,实现还没有完成,因为我们还没有向数据库写入任何内容。此外,我们仍然没有回答示例场景中提出的大部分问题。例如,我们仍然不知道数据库需要如何连接,它的类型(SQL还是NoSQL),正确的数据格式等等。然而,我们已经做出了一些重要的架构假设,这些假设使我们能够实现这个类并使其可被其他类使用。

现在是时候识别我们代码中的未知元素并用谜题标记它们了。每个谜题都是对改进的请求。我们希望向其他人寻求帮助,以改进和纠正我们的假设。这是我们需要添加的第一个谜题:

public interface DataBridge {
  /**

* @todo #123 I assumed that a simple insert() method will be

* enough to insert data into the database. Maybe it's

* not true, and some sort of transaction support may be

* required. We should implement this interface and create

* an integration test with a database. */ void insert(String text) throws IOException; } ```



这个谜题有三个元素:`@todo`标记,`#123`定位器和一个评论定位器显示如下内容:“在处理票号为123的工单时创建了这个谜题。”

让我们再添加一个谜题

void write(char[] cbuf, int off, int len) throws IOException { // @todo #123 I assumed that the data should be sent to the database // as its received by the writer. Maybe this assumption // is wrong and we should aggregate data into blocks/chunks // and then send them to the data bridge. String data = new String(Arrays.copyOfRange(cbuf, off, off + len)); this.bridge.insert(data); } ```

这个谜题表明我们的担忧之一,因为我们不确定这个架构决策是否正确。实际上,目前的设计非常简陋,很可能是错误的。为了完善它并进行重构,我们需要来自任务说明者的更多信息。

任务已经完成。现在,你可以将你的分支重新集成到“主分支”中,并将票据还给分配给你的人。他现在的任务是找到其他能够解决我们刚刚创建的谜题的人。

现在创建的每个谜题都会产生其他谜题,这些谜题将由其他人解决。因此,我们简单的一小时任务可能会产生数百个其他任务,可能需要几天甚至几年才能完成。尽管如此,你的目标是尽快完成你的具体任务,并将你的分支重新集成到“主分支”中。

Best Practices

有一些简单的规则可以帮助您正确放置拼图。

首先,您应该在代码遇到存根的地方放置@todo注释,例如在单元测试中。您正在实现一个测试,但由于该类尚未实现,所以测试失败。您可以使用@Ignore注释跳过测试,并在其JavaDoc中添加一个@todo拼图。

其次,您的拼图应尽可能靠近击中存根的代码元素。假设您有一个单元测试有三个测试方法。由于该类尚未实现,所有测试方法都失败了。最好的方法是忽略它们中的每一个,并创建三个(!)拼图。每个拼图都应解释您对该类的期望以及应如何实现它。

第三,尽可能详细地描述。您的拼图很快将成为其他人的任务定义。因此,请清楚地解释您期望下一个人实现什么、如何实现、使用哪些文档等。应该提供足够的信息,以便下一个被分配拼图任务的人能够在没有您额外输入的情况下实现所需的类!

顺便说一下,拼图收集过程可以通过我们的PDD Ruby gem和0pdd.com托管服务来自动化。

Translated by ChatGPT gpt-3.5-turbo/36 on 2023-10-12 at 15:50

sixnines availability badge   GitHub stars