Практическое использование для структурных типов? - PullRequest
12 голосов
/ 06 ноября 2010

Структурные типы - один из тех, "вау, круто!" Особенности Scala. Тем не менее, для каждого примера, который я могу придумать, где они могут помочь, неявные преобразования и динамическое смешивание часто кажутся более подходящими. Каковы их общие применения и / или советы, когда они уместны?

Ответы [ 4 ]

13 голосов
/ 06 ноября 2010

Помимо редкого случая, когда классы предоставляют один и тот же метод, но не связаны и не реализуют общий интерфейс (например, метод close() - Source, например, не расширяет Closeable), Я не вижу смысла для структурных типов с их существующим ограничением.Однако, если бы они были более гибкими, я мог бы написать что-то вроде этого:

def add[T: { def +(x: T): T }](a: T, b: T) = a + b

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

РЕДАКТИРОВАТЬ

Каким бы бесполезным я ни находил структурные типы, компилятор , однако, использует его для обработки анонимных классов.Например:

implicit def toTimes(count: Int) = new {
  def times(block: => Unit) = 1 to count foreach { _ => block }
}

5 times { println("This uses structural types!") }

Объект, являющийся результатом (неявного) toTimes(5), имеет тип { def times(block: => Unit) }, то есть структурный тип.

Я не знаю, делает ли Scalaчто для каждого анонимного класса - возможно, так и есть.Увы, это одна из причин медленного выполнения pimp моей библиотеки, так как структурные типы используют рефлексию для вызова методов.Вместо анонимного класса следует использовать реальный класс, чтобы избежать проблем с производительностью в моей библиотеке pimp.

3 голосов
/ 06 ноября 2010

Структурные типы - это очень крутые конструкции в Scala. Я использовал их для представления нескольких несвязанных типов, имеющих общий атрибут, с которым я хочу выполнить обычную операцию без нового уровня абстракции.

Я слышал один аргумент против структурных типов от людей, которые строго относятся к архитектуре приложения. Они считают, что применять общую операцию к типам без ассоциативной черты или родительского типа опасно, потому что вы оставляете правило того, какой тип метод должен применять к open-end. Пример Даниэля close() точен, но что если у вас есть другой тип, требующий другого поведения? Кто-то, кто не понимает архитектуру, может использовать ее и вызвать проблемы в системе.

2 голосов
/ 08 ноября 2010

Я думаю, что структурные типы - это одна из тех функций, которые вам не нужны так часто, но , когда вам это нужно, это вам очень помогает.Одной из областей, где структурные типы действительно сияют, является «модернизация», например, когда вам нужно склеить несколько частей программного обеспечения, для которых у вас нет исходного кода, и которые не были предназначены для повторного использования.Но если вы обнаружите, что часто используете структурные типы, вы, вероятно, делаете это неправильно.

[Редактировать]

Конечно, часто подразумеваются последствия,но есть случаи, когда вы не можете: представьте, что у вас есть изменяемый объект, который вы можете изменить с помощью методов, но который скрывает важные части своего состояния, своего рода «черный ящик».Затем вам нужно как-то поработать с этим объектом.

Еще один вариант использования для структурных типов - это когда код опирается на соглашения об именах без общего интерфейса, например, в сгенерированном компьютером коде.В JDK мы также можем найти такие вещи, как пара StringBuffer / StringBuilder (где общие интерфейсы Appendable и CharSequence way to general).

1 голос
/ 13 декабря 2011

Структурные типы дают некоторые преимущества динамических языков статически связанному языку, особенно слабой связи.Если вы хотите, чтобы метод foo() вызывал методы экземпляра класса Bar, вам не нужен интерфейс или базовый класс, общий для foo() и Bar.Вы можете определить структурный тип, который foo() принимает и чей Bar не имеет понятия о существовании.Пока Bar содержит методы, которые соответствуют сигнатурам структурных типов, foo() сможет вызывать.

Это здорово, потому что вы можете поместить foo() и Bar в разные, совершенно не связанные библиотеки,то есть без единого ссылочного контракта.Это снижает требования к сцеплению и, следовательно, еще больше способствует слабой связи.

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

  • Идентификация объекта сохраняется (нет отдельного объекта для экземпляра адаптера, по крайней мере, на семантическом уровне).
  • Вам не нужно создавать экземпляр адаптера - просто передайтеBar экземпляр в foo().
  • Вам не нужно реализовывать методы-оболочки - просто объявите необходимые сигнатуры в структурном типе.
  • Структурный тип не должен знатьфактический класс экземпляра или интерфейс, в то время как адаптер должен знать Bar, чтобы он мог вызывать свои методы.Таким образом, один структурный тип может использоваться для многих реальных типов, тогда как с адаптером необходимо кодировать несколько классов - по одному для каждого фактического типа.

Единственный недостаток структурных типов по сравнению с адаптерами - эточто структурный тип не может использоваться для перевода сигнатур методов.Таким образом, когда подписи не совпадают, вы должны использовать адаптеры, которые будут иметь некоторую логику перевода.Мне особенно не нравится кодировать «интеллектуальные» адаптеры, потому что во многих случаях они больше, чем просто адаптеры и вызывают повышенную сложность.Если клиенту класса нужен какой-то дополнительный метод, я предпочитаю просто добавить такой метод, так как он обычно не влияет на размер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...