Разработчики в Java по сравнению с C++?

В Буферном API Протокола Google для Java они используют этих хороших Разработчиков, которые создают объект (см. здесь):

Person john =
  Person.newBuilder()
    .setId(1234)
    .setName("John Doe")
    .setEmail("jdoe@example.com")
    .addPhone(
      Person.PhoneNumber.newBuilder()
        .setNumber("555-4321")
        .setType(Person.PhoneType.HOME))
    .build();

Но соответствующий API C++ не использует таких Разработчиков (см. здесь),

C++ и API Java, как предполагается, делают то же самое, таким образом, я задаюсь вопросом, почему они не использовали разработчиков в C++ также. Есть ли причины языка позади этого, т.е. это не идиоматично, или это осуждено в C++? Или вероятно просто персональное предпочтение человека, который записал версию C++ Буферов Протокола?

11
задан Frank 19 February 2010 в 07:40
поделиться

5 ответов

В C++ вам придется явно управлять памятью, что, вероятно, сделает эту идиому более болезненной для использования - либо build() должен вызывать деструктор для построителя, либо вы должны держать его рядом, чтобы удалить после построения объекта Person. И то, и другое меня немного пугает.

0
ответ дан 3 December 2019 в 08:03
поделиться

Правильный способ реализовать нечто подобное в C++ - использовать сеттеры, возвращающие ссылку на *this.

class Person {
  std::string name;
public:
  Person &setName(string const &s) { name = s; return *this; }
  Person &addPhone(PhoneNumber const &n);
};

Класс можно использовать следующим образом, предполагая аналогичное определение PhoneNumber:

Person p = Person()
  .setName("foo")
  .addPhone(PhoneNumber()
    .setNumber("123-4567"));

Если нужен отдельный класс-строитель, то это тоже можно сделать. Такие строители должны быть выделены в стеке, конечно.

8
ответ дан 3 December 2019 в 08:03
поделиться

Я бы пошел с «не идиоматическим», хотя я видел примеры таких стилей свободного интерфейса в Код C ++.

Это может быть связано с тем, что существует несколько способов решения одной и той же основной проблемы. Обычно здесь решается проблема именованных аргументов (точнее, их отсутствия). Возможно, более C ++ - подобным решением этой проблемы может быть библиотека параметров Boost .

4
ответ дан 3 December 2019 в 08:03
поделиться

В продолжение моего комментария ...

struct Person
{
   int id;
   std::string name;

   struct Builder
   {
      int id;
      std::string name;
      Builder &setId(int id_)
      {
         id = id_;
         return *this;
      }
      Builder &setName(std::string name_)
      {
         name = name_;
         return *this;
      }
   };

   static Builder build(/* insert mandatory values here */)
   {
      return Builder(/* and then use mandatory values here */)/* or here: .setId(val) */;
   }

   Person(const Builder &builder)
      : id(builder.id), name(builder.name)
   {
   }
};

void Foo()
{
   Person p = Person::build().setId(2).setName("Derek Jeter");
}

Это заканчивается компиляцией примерно в тот же ассемблер, что и эквивалентный код:

struct Person
{
   int id;
   std::string name;
};

Person p;
p.id = 2;
p.name = "Derek Jeter";
1
ответ дан 3 December 2019 в 08:03
поделиться

Ваше утверждение, что "C++ и Java API должны делать одно и то же", безосновательно. Они не документированы для выполнения одних и тех же действий. Каждый язык вывода может создать свою интерпретацию структуры, описанной в файле .proto. Преимущество этого в том, что то, что вы получаете в каждом языке, идиоматично для этого языка. Это минимизирует ощущение, что вы, скажем, "пишете Java на C++". Это определенно было бы то, как я бы себя чувствовал, если бы для каждого класса сообщений существовал отдельный класс строителя.

Для целочисленного поля foo вывод C++ из protoc будет включать метод void set_foo(int32 value) в классе для данного сообщения.

Выход Java вместо этого сгенерирует два класса. Один из них непосредственно представляет сообщение, но имеет только геттеры для поля. Другой класс является классом-строителем и имеет только сеттеры для поля.

Вывод в Python все же отличается. Созданный класс будет включать поле, которым можно управлять напрямую. Я ожидаю, что подключаемые модули для C, Haskell и Ruby также сильно отличаются. До тех пор, пока все они могут представлять структуру, которую можно перевести в эквивалентные биты на проводе, они выполняют свою работу. Помните, что это "буферы протокола", а не "буферы API".

Исходный текст плагина для C++ поставляется с дистрибутивом protoc. Если вы хотите изменить возвращаемый тип для функции set_foo, вы можете это сделать. Я обычно избегаю ответов, которые сводятся к "Это открытый исходный код, поэтому любой может его изменить", потому что обычно не очень полезно рекомендовать кому-то изучать совершенно новый проект настолько хорошо, чтобы вносить в него значительные изменения только для решения проблемы. Однако я не думаю, что в данном случае это будет очень сложно. Самым сложным будет найти участок кода, который генерирует сеттеры для полей. Как только вы его найдете, внесение необходимых изменений, вероятно, будет простым. Измените тип возврата и добавьте оператор return *this в конец сгенерированного кода. После этого вы сможете писать код в стиле, приведенном в ответе Hrnt.

1
ответ дан 3 December 2019 в 08:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: