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, или Puzzle Driven Development, - это метод, используемый для разбиения программных задач на более мелкие и обеспечения их параллельной реализации. Метод PDD широко используется в методологии XDSD. Метод ожидает патента USPTO (заявка № 12/840,306).

Давайте рассмотрим метод на примере. Предположим, что вы программист и вам поручено разработать и реализовать класс на языке Java. Формальное описание задачи звучит следующим образом: “класс DBWriter должен расширять абстрактный класс java.io.Writer и сохранять все входящие данные в базе данных”.

У вас есть один час для выполнения этой задачи. Вам очевидно, что одного часа недостаточно, потому что проблема намного больше и требует больше работы, чем позволяет отведенное время. Кроме того, существует множество неизвестных:

  • Какую информацию нам необходимо сохранить и в каком формате?

  • Что такое схема базы данных? Это 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!");
  }
}

В указанном выше тесте мы определяем ожидаемое поведение класса. Тест не компилируется, потому что отсутствуют два класса: DataBridge и DBWriter. Давайте сначала реализуем мост.

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); } ```

Эта головоломка указывает на одну из наших озабоченностей, потому что мы не уверены, что архитектурное решение верное. На самом деле, дизайн в данный момент очень примитивный и, скорее всего, неправильный. Для его уточнения и рефакторинга нам требуется больше информации от спецификатора задачи.

Задача завершена. Теперь вы можете интегрировать свою ветку в master и вернуть билет тому, кто вам его назначил. Его задача теперь найти других людей, которые смогут решить головоломки, которые мы только что создали.

Каждая созданная сейчас головоломка породит другие головоломки, которые будут решаться другими людьми. Следовательно, наша простая задача продолжительностью в один час может потенциально породить сотни других задач, которые могут занять дни или даже годы для завершения. Тем не менее, ваша цель в работе с вашей конкретной задачей - закончить ее как можно скорее и интегрировать свою ветку в master.

Best Practices

Есть несколько простых правил, которые помогут вам правильно разместить головоломки.

Во-первых, вы должны поместить свои аннотации @todo в той точке, где ваш код натыкается на заглушку. Например, в модульном тесте. Вы реализуете тест, и он не проходит, потому что класс еще не реализован. Вы пропускаете тест с помощью аннотации @Ignore и добавляете головоломку @todo в его JavaDoc.

Во-вторых, ваша головоломка должна оставаться как можно ближе к элементу кода, который натыкается на заглушку. Предположим, у вас есть модульный тест, в котором есть три метода тестирования. Все они сейчас не проходят, потому что класс не реализован. Лучший подход будет проигнорировать каждый из них и создать три (!) головоломки. Каждая из головоломок должна объяснять, что вы ожидаете от класса и как его следует реализовать.

В-третьих, старайтесь быть максимально описательным. Ваша головоломка вскоре станет заданием для кого-то другого. Поэтому четко объясните, что вы ожидаете от следующего исполнителя, как это сделать, какую документацию использовать и так далее. Должно быть достаточно информации, чтобы следующий исполнитель мог реализовать требуемые классы без дополнительной информации от вас!

Кстати, сборка головоломок может быть автоматизирована с помощью нашей библиотеки Ruby PDD и хостинг-сервиса 0pdd.com.

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

sixnines availability badge   GitHub stars