В чем разница между префиксами имени метода «to» и «as»? [закрыто]

78

В чем разница между "to" и "as" такими префиксами имен методов, как

  • составлять список(),
  • asList (),
  • и т.д...

Когда использовать что при разработке метода?

Даниэль Хари
источник

Ответы:

120

Ожидается, что toXYZ()функция будет выполнять преобразование и возвращать новый независимый объект (хотя неизменность позволяет оптимизировать, java.lang.String.toString()просто возвращает объект).
В качестве примера, в C ++ мы std::bitset::to_ulong()можем легко потерпеть неудачу, и целый ряд to_string()всех (более или менее) выполняет сложное преобразование и выделение памяти.

С asXYZ()другой стороны, ожидается, что функция будет возвращать (потенциально другое) представление об источнике, выполняя минимальную работу.
В качестве примера, в C ++ мы имеем, std::as_const()который просто возвращает постоянную ссылку, и более сложный, std::forward_as_tupleкоторый также ссылается на свои аргументы по ссылке.

Deduplicator
источник
19
Оказывается, существует какой-то прецедент для этого. В общем, As [что-то] будет похоже на приведение, в то время как To [что-то] создает новый объект (другого типа) из существующего. В случае ToList это будет O (n) и почти наверняка дороже, чем «Как». См. Stackoverflow.com/questions/17968469/…
Роберт Харви
5
Это часть выбора описательных имен, и даже если это конкретное различие не кодифицировано в стандарте кодирования, нарушение его нарушает Принцип наименьшего удивления .
Дедупликатор
11
Я бы описал это как, инстинктивно, я бы подумал о «к» как «изменить к» и «как», как «относиться к». Первое подразумевает работу, чтобы изменить вещь, второе подразумевает, что это просто разница в том, как вы работаете с этим.
Латти
6
Если это что-то значит, это должно означать это. Я бы предположил, что это часто ничего не значит, поэтому я бы не стал полагаться на это в случайном стороннем коде (не видя чего-либо в документации). Это было бы хорошее соглашение, чтобы принять в основе кода, хотя.
Бен
4
Этот ответ также точен в C ++: например, std::to_string(x)создает новый строковый объект, но std::as_const(x)создает ссылку (представление) на существующий объект.
Quuxplusone
13

Честно говоря, это может быть просто несоответствие имен. Если посмотреть на стандартных библиотечных объектов в Smalltalk, например, все методы , которые делают «сложные преобразования» или «возвращать простые представления в другом типе» начинаются с as, как asString, asFloat, asSecondsи стандартный метод для преобразования ничего к чему -то еще, as: aClass.

В Ruby, одни и те же виды методов с префиксом to_, как и в to_s, to_a, to_h, сокращение string, arrayи hashсоответственно.

Кажется, что ни одна из стандартных библиотек не проводит различий между различными видами преобразований, вероятно, потому, что это следует рассматривать как деталь реализации.

Однако в Java мы видим много путаницы. Как уже упоминалось, есть toString, asListи так далее. Я считаю, что это просто несоответствие именования, потому что, если вы попытаетесь определить разные значения для каждого префикса, вы всегда найдете контрпример в другом месте стандартной библиотеки.

Так что в любом случае я бы сказал, что для вас и вашей команды важно выбрать один префикс и использовать его последовательно во всем коде. Последовательность является ключом, поэтому людям не остается удивляться, как и вам.

MichelHenrich
источник
1
Я не верю в: выбор одного стиля для всей команды, а скорее выбор одного стиля для похожих случаев в модуле, последовательно.
Даниэль Хари
12
В Java toStringсоздает новую строку, полностью отключенную от объекта (и нет другого пути из-за неизменности). То же самое верно, например, для Collection#toArray, пока Arrays#asListвозвращает представление массива, который связан двунаправленно (изменение массива изменяет список и наоборот). Так что это довольно непротиворечиво, хотя могут быть и исключения. Выбор одного префикса был бы неправильным. Если бы было Arrays#toList, то я бы ожидал, что это создаст новый список с новым базовым массивом.
Маартин
Я думаю, что Smalltalk просто допустил ошибку, они не поняли соглашение. Это сложное соглашение, чтобы понять (и даже заметить), потому что это настолько абстрактно, и это не особенно эффективно. Даже сам акт постановки этого вопроса требует некоторого понимания.
USR
3
@usr Smalltalk, возможно, был разработан до того, как он станет конвенцией
Берги
6

Хотя уже есть принятый ответ, похоже, что он сосредоточен на C ++, а вопрос помечен java . В Java первым примером, который приходит на ум для такого рода вещей, является Arrays.asList , который возвращает, по сути, представление массива, заключенного в список. Базовый массив и список все еще связаны; изменения в массиве отражаются в списке и наоборот. Однако массив, возвращаемый методом toArray списка, не зависит от исходного массива и списка:

String[] wordArray = {"one", "fine", "day"};
List<String> wordList = Arrays.asList(wordArray);

// changes to the array are visible in the list
System.out.println(wordList); // prints "[one, fine, day]"
wordArray[1] = "horrible";
System.out.println(wordList); // prints "[one, horrible, day]"

// changes to the list are visible in the array
wordList.set(1, "beautiful");
System.out.println(wordArray[1]); // prints "beautiful"

// but changes to the list or array don't affect the 
// result from the list's toArray method.
String[] moreWords = wordList.toArray(new String[] {});
wordList.set(0, "the");
wordArray[1] = "best";
for (int i=0; i<3; i++) {
  System.out.println(moreWords[i]); // prints "one", "beautiful", and "day"
}

Все это говорит о том, что нет гарантии, что каждый разработчик библиотеки будет следовать этому соглашению, поэтому вам все равно нужно проверить документацию, чтобы выяснить, будет ли это поведение, которое вы получите от неизвестного кода.

Другое место, которое я видел как методы ... (), часто используемые в преобразовании типов в подтипы. Например, если у вас есть перечислимый набор подтипов, то вы можете получить такой код:

  /**
   * Every Node is either an ANode or a BNode.
   */
  interface Node {

    /**
     * Returns this Node as an ANode.
     * 
     * @return this node
     */
    default ANode asANode() {
      if (this instanceof ANode) {
        return (ANode) this;
      }
      else {
        throw new UnsupportedOperationException();
      }
      // Or, in Java8 style, perhaps:
      // return Optional.of(this)
      //     .filter(ANode.class::isInstance)
      //     .map(ANode.class::cast)
      //     .orElseThrow(UnsupportedOperationException::new);
    }

    /**
     * Returns this Node as a BNode.
     * 
     * @return this node
     */
    default BNode asBNode() {
      if (this instanceof BNode) {
        return (BNode) this;
      }
      else {
        throw new UnsupportedOperationException();
      }
    }
  }
Джошуа Тейлор
источник
1

Разница, которую я заметил (только сейчас, подумав об этом)

  • Как правило, преобразуется в другой тип ссылки (несколько сложный объект)
  • Как правило, возвращает простой тип значения

Итак, мы видим AsInteger и AsString и видим ToArray и ToStringList.

Это подразумевает преобразование, которое имеет смысл (это движение, процесс). Как подразумевается представление, способ выражения исходного объекта.

Еще один способ взглянуть на это:

  • Это операция, поэтому вы должны использовать ее для методов.
  • Как простое представление, так что вы бы использовали его для свойств.

И затем есть "предшествующий уровень техники" (или наследство), чтобы иметь дело с. До того, как языки были полностью OO с нуля, у вас были бы библиотечные функции, такие как StrToInt () и IntToStr (). Они выполняли преобразования, они были операциями, поэтому имело смысл называть их SomethingToSomethingelse (). В конце концов, To более активен, чем As. Я особенно думаю о Delphi здесь.

Когда C # был спроектирован с амбициями полностью перейти на OO, имело смысл иметь метод для целочисленного объекта, который преобразует целое число в строку. Хотя у нас также есть класс Convert, преобразование в строку настолько распространено, что он стал виртуальным методом для объекта. Дизайнеры, возможно, решили, что ToString будет более знакомым людям из старой парадигмы, и, возможно, именно поэтому мы получили виртуальный метод ToString (), а не виртуальное свойство AsString.

Мартин Маат
источник
3
Как насчет toString()?
Дедупликатор
Тут ты меня подловил. Я думаю, что AsString был бы более уместным. У меня есть некоторые дополнительные мысли, я обновлю свой ответ.
Мартин Маат
Я не думаю, что этот ответ действительно соответствует соглашениям в C #. Например. ToList () и AsEnumerable () возвращают сложные объекты, разница в том, что ToList () всегда возвращает новый объект, в то время как AsEnumerable () возвращает представление или адаптер поверх исходного объекта.
JacquesB
@JaquesB Видимо разные идеи были применены здесь и там. Delphi имеет свойства AsInteger и AsString для объектов TField (которые инкапсулируют поля базы данных). Я мог быть предвзятым из-за этого. Но опять же, вопрос помечен Java, а не C # или Delphi.
Мартин Маат