В чем разница между абстракцией и обобщением?

Я понимаю, что абстракция заключается в том, чтобы взять что-то более конкретное и сделать его более абстрактным. Это может быть либо структура данных, либо процедура. Например:

  1. абстракции данных: прямоугольник-это абстракция квадрат. Он концентрируется на том, что квадрат имеет две пары противоположных сторон, и игнорирует тот факт, что соседние стороны квадрата равны.
  2. процедурная абстракция: высшее функция заказа map - это абстракция процедуры, которая выполняет некоторый набор операций над списком значений для создания совершенно нового списка значений. Он концентрируется на том факте, что процедура проходит через каждый элемент списка для создания нового списка и игнорирует фактические операции, выполняемые по каждому элементу списка.

8 ответов



очень интересный вопрос. Я нашел в этой статье по теме, в которой кратко говорится, что:

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

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

Book(title, ISBN, borrowed)

мы просто абстрагировались от реальных книг в нашей библиотеке, и свойства, которые интересны нам в контексте нашего приложения.


обобщение, с другой стороны, не пытается удалить детали, но сделать функциональность применимой к более широкому (более общему) диапазону элементов. Универсальные контейнеры - очень хороший пример для этого мышления: вы не хотите написать реализацию StringList, IntList и так далее, поэтому вам лучше написать generic список, который применяется ко всем типам (например,List[T] в Scala). Обратите внимание, что вы не abstracted список, поскольку вы не удалили никаких деталей или операций, вы просто сделали их универсально применимыми ко всем вашим типам.

Раунд 2

@dtldarek's ответ действительно очень хорошая иллюстрация! Основываясь на этом, вот некоторый код, который можно было бы дать дополнительные разъяснения.

помню Book я упомянул? Конечно, в библиотеке есть и другие вещи, которые можно заимствовать (я буду называть набор всех этих объектов Borrowable хотя это, вероятно, даже не слово: D):

http://f.cl.ly/items/3z0f1S3g1h1m2u3c0l0g/diagram.png

все эти элементы будут иметь аннотация представление в нашей базе данных и бизнес-логике, вероятно, похоже на это нашего Book. Кроме того, мы могли бы определить признак, который является общим для всех Borrowables:

trait Borrowable {
    def itemId:Long
}

мы могли бы тогда написать обобщенное логика, которая применяется ко всем Borrowables (в этот момент нам все равно, если это книга или журнал):

object Library {
    def lend(b:Borrowable, c:Customer):Receipt = ...
    [...]
}

подводя итог: мы сохранили абстрактное представление из всех книг, журналов и DVD-дисков в нашей базе данных, потому что точное представление является ни возможным, ни необходимым. Затем мы пошли впереди и сказал

не имеет значения, заимствована ли книга, журнал или DVD клиентом. Это всегда один и тот же процесс.

таким образом мы обобщенное операция заимствования элемента, определяя все вещи, которые можно заимствовать как Borrowables.


Я собираюсь использовать некоторые примеры для описания обобщения и абстракции, и я собираюсь обратиться к этой статьи.

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

обобщение

в в статье говорится, что:

" понятие обобщения в ООП означает, что объект инкапсулирует общее состояние и поведение для категории объектов."

например, если вы применяете обобщение к фигурам, то общими свойствами для всех типов фигур являются площадь и периметр.

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

enter image description here

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

абстрагирование

статья определяет абстракцию as:

" процесс выявления общих закономерностей, имеющих систематический характер вариации; абстракция представляет общий шаблон и обеспечивает средство для указания, какой вариант использовать"(Richard Gabriel)"

в области программирования:

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

следовательно, в примере, приведенном в обобщении раздел выше, форма абстрактна как:

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

однако, а также будучи абстрактной фигурой также является обобщением (потому что он "воплощает общее состояние и поведение для категории объектов" где в этом случае объекты формы).

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


не обращаясь к достоверному / официальному источнику: пример в Scala

Имея "Абстракция"

  trait AbstractContainer[E] { val value: E }

  object StringContainer extends AbstractContainer[String] {
    val value: String = "Unflexible"
  }

  class IntContainer(val value: Int = 6) extends AbstractContainer[Int]

  val stringContainer = new AbstractContainer[String] {
    val value = "Any string"
  }

и "обобщение"

  def specialized(c: StringContainer.type) =
    println("It's a StringContainer: " + c.value)

  def slightlyGeneralized(s: AbstractContainer[String]) =
    println("It's a String container: " + s.value)

  import scala.reflect.{ classTag, ClassTag }
  def generalized[E: ClassTag](a: AbstractContainer[E]) =
    println(s"It's a ${classTag[E].toString()} container: ${a.value}")

  import scala.language.reflectiveCalls
  def evenMoreGeneral(d: { def detail: Any }) =
    println("It's something detailed: " + d.detail)

выполнения

  specialized(StringContainer)
  slightlyGeneralized(stringContainer)
  generalized(new IntContainer(12))
  evenMoreGeneral(new { val detail = 3.141 })

ведет к

It's a StringContainer: Unflexible
It's a String container: Any string
It's a Int container: 12
It's something detailed: 3.141

абстрагирование

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

пример: модель каркаса автомобиля.

обобщение

обобщение использует отношение "is-a" от специализации к класс обобщения. Общую структуру и поведение используются из specializtion для обобщенного класса. На более широком уровне вы можете понять это как наследование. Почему я беру термин наследование, вы можете связать этот термин очень хорошо. Обобщение также называется отношением" есть-есть".

пример: рассмотрим, что существует класс с именем Person. Студент-это человек. Способность - это человек. Поэтому здесь отношения между студентом и человеком, аналогично факультету и человек-это обобщение.


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

давайте начнем с обычного фрагмента императивного кода:

// some data

const xs = [1,2,3];

// ugly global state

const acc = [];

// apply the algorithm to the data

for (let i = 0; i < xs.length; i++) {
  acc[i] = xs[i] * xs[i];
}

console.log(acc); // yields [1, 4, 9]

на следующем шаге я представляю наиболее важную абстракцию в программировании-функции. Абстрактные функции над выражениями:

// API

const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x]; // weird square function to keep the example simple

// some data

const xs = [1,2,3];

// applying

console.log(
  foldr(x => acc => concat(sqr_(x)) (acc)) ([]) (xs) // [1, 4, 9]
)

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

еще один шаг абстракции...

// API

const comp = (f, g) => x => f(g(x));
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];

// some data

const xs = [1,2,3];

// applying

console.log(
  foldr(comp(concat, sqr_)) ([]) (xs) // [1, 4, 9]
);

и еще один:

// API

const concatMap = f => foldr(comp(concat, f)) ([]);
const comp = (f, g) => x => f(g(x));
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];

// some data

const xs = [1,2,3];

// applying

console.log(
  concatMap(sqr_) (xs) // [1, 4, 9]
);

основной принцип теперь должен быть ясен. Я все еще недоволен concatMap хотя бы потому, что он работает только с Arrays. Я хочу, чтобы он работал с каждым типом данных, который складная:

// API

const concatMap = foldr => f => foldr(comp(concat, f)) ([]);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];
const comp = (f, g) => x => f(g(x));

// Array

const xs = [1, 2, 3];

const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);

// Option (another foldable data type)

const None =      r => f => r;
const Some = x => r => f => f(x);

const foldOption = f => acc => tx => tx(acc) (x => f(x) (acc));

// applying

console.log(
  concatMap(foldr) (sqr_) (xs), // [1, 4, 9]
  concatMap(foldOption) (sqr_) (Some(3)), // [9]
  concatMap(foldOption) (sqr_) (None) // []
);

Я расширить приложение of concatMap большей домен типов данных, намолены все складно типы данных. Обобщение подчеркивает общность между различными типами (или, скорее, объектами, сущностями).

Я достиг этого с помощью передачи словаря (concatMapдополнительный аргумент в моем примере). Теперь это несколько раздражает, чтобы передать эти типы диктовок по всему вашему коду. Следовательно, люди Haskell ввели классы типов в, ...ЭМ, аннотация над типом dicts:

concatMap :: Foldable t => (a -> [b]) -> t a -> [b]

concatMap (\x -> [x * x]) ([1,2,3]) -- yields [1, 4, 9]
concatMap (\x -> [x * x]) (Just 3) -- yields [9]
concatMap (\x -> [x * x]) (Nothing) -- yields []

Итак, haskell's generic concatMap выгоды от обоих, абстракции и обобщения.


позвольте мне объяснить самым простым способом.

"все красивые девушки-женщины."это абстракция.

" все красивые девушки накладывают макияж."это обобщение.


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

обобщение не обязательно требует избегать деталей, а скорее иметь некоторый механизм, позволяющий применять одну и ту же функцию к другому аргументу. Например, полиморфные типы в функциональных языках программирования позволяют чтобы не беспокоиться о аргументах, а сосредоточиться на работе функции. Аналогично, в java вы можете иметь общий тип, который является "зонтиком" для всех типов, в то время как функция одинакова.