Подтвердить что ты не робот

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

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

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

Итак, мой вопрос таков: как абстракция отличается от обобщения? Я ищу ответы, в основном связанные с функциональным программированием. Однако, если есть параллели в объектно-ориентированном программировании, я также хотел бы узнать об этом.

4b9b3361

Ответ 1

Объект:

portal cake photo

Абстракция:

enter image description here

Обобщение:

many desserts

Пример в Haskell:

Реализация сортировки сортировки с использованием очереди приоритетов с тремя различными интерфейсами:

  • открытый интерфейс с реализацией очереди в виде отсортированного списка,
  • абстрактный интерфейс (поэтому детали скрыты за слоем абстракции),
  • обобщенный интерфейс (детали все еще видны, но реализация более гибкая).
{-# LANGUAGE RankNTypes #-}

module Main where

import qualified Data.List as List
import qualified Data.Set as Set

{- TYPES: -}

-- PQ new push pop
-- by intention there is no build-in way to tell if the queue is empty
data PriorityQueue q t = PQ (q t) (t -> q t -> q t) (q t -> (t, q t))
-- there is a concrete way for a particular queue, e.g. List.null
type ListPriorityQueue t = PriorityQueue [] t
-- but there is no method in the abstract setting
newtype AbstractPriorityQueue q = APQ (forall t. Ord t => PriorityQueue q t)


{- SOLUTIONS: -}

-- the basic version
list_selection_sort :: ListPriorityQueue t -> [t] -> [t]
list_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
  where
    mypop [] = Nothing -- this is possible because we know that the queue is represented by a list
    mypop ls = Just (pop ls)


-- here we abstract the queue, so we need to keep the queue size ourselves
abstract_selection_sort :: Ord t => AbstractPriorityQueue q -> [t] -> [t]
abstract_selection_sort (APQ (PQ new push pop)) list = List.unfoldr mypop (List.foldr mypush (0,new) list)
  where
    mypush t (n, q) = (n+1, push t q)
    mypop (0, q) = Nothing
    mypop (n, q) = let (t, q') = pop q in Just (t, (n-1, q'))


-- here we generalize the first solution to all the queues that allow checking if the queue is empty
class EmptyCheckable q where
  is_empty :: q -> Bool

generalized_selection_sort :: EmptyCheckable (q t) => PriorityQueue q t -> [t] -> [t]
generalized_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
  where
    mypop q | is_empty q = Nothing
    mypop q | otherwise  = Just (pop q)


{- EXAMPLES: -}

-- priority queue based on lists
priority_queue_1 :: Ord t => ListPriorityQueue t
priority_queue_1 = PQ [] List.insert (\ls -> (head ls, tail ls))
instance EmptyCheckable [t] where
  is_empty = List.null

-- priority queue based on sets
priority_queue_2 :: Ord t => PriorityQueue Set.Set t
priority_queue_2 = PQ Set.empty Set.insert Set.deleteFindMin
instance EmptyCheckable (Set.Set t) where
  is_empty = Set.null

-- an arbitrary type and a queue specially designed for it
data ABC = A | B | C deriving (Eq, Ord, Show)

-- priority queue based on counting
data PQ3 t = PQ3 Integer Integer Integer
priority_queue_3 :: PriorityQueue PQ3 ABC
priority_queue_3 = PQ new push pop
  where
    new = (PQ3 0 0 0)
    push A (PQ3 a b c) = (PQ3 (a+1) b c)
    push B (PQ3 a b c) = (PQ3 a (b+1) c)
    push C (PQ3 a b c) = (PQ3 a b (c+1))
    pop (PQ3 0 0 0) = undefined
    pop (PQ3 0 0 c) = (C, (PQ3 0 0 (c-1)))
    pop (PQ3 0 b c) = (B, (PQ3 0 (b-1) c))
    pop (PQ3 a b c) = (A, (PQ3 (a-1) b c))

instance EmptyCheckable (PQ3 t) where
  is_empty (PQ3 0 0 0) = True
  is_empty _ = False


{- MAIN: -}

main :: IO ()
main = do
  print $ list_selection_sort priority_queue_1 [2, 3, 1]
  -- print $ list_selection_sort priority_queue_2 [2, 3, 1] -- fail
  -- print $ list_selection_sort priority_queue_3 [B, C, A] -- fail
  print $ abstract_selection_sort (APQ priority_queue_1) [B, C, A] -- APQ hides the queue 
  print $ abstract_selection_sort (APQ priority_queue_2) [B, C, A] -- behind the layer of abstraction
  -- print $ abstract_selection_sort (APQ priority_queue_3) [B, C, A] -- fail
  print $ generalized_selection_sort priority_queue_1 [2, 3, 1]
  print $ generalized_selection_sort priority_queue_2 [B, C, A]
  print $ generalized_selection_sort priority_queue_3 [B, C, A]-- power of generalization

  -- fail
  -- print $ let f q = (list_selection_sort q [2,3,1], list_selection_sort q [B,C,A])
  --         in f priority_queue_1

  -- power of abstraction (rank-n-types actually, but never mind)
  print $ let f q = (abstract_selection_sort q [2,3,1], abstract_selection_sort q [B,C,A]) 
          in f (APQ priority_queue_1)

  -- fail
  -- print $ let f q = (generalized_selection_sort q [2,3,1], generalized_selection_sort q [B,C,A])
  --         in f priority_queue_1

Код также доступен через pastebin.

Стоит отметить экзистенциальные типы. Как уже указывал @lukstafi, абстракция похожа на экзистенциальный квантор, а обобщение аналогично универсальному квантору. Заметим, что существует нетривиальная связь между тем, что ∀xP (x) подразумевает ∃xP (x) (в непустом вселенном) и что редко существует обобщение без абстракции (даже С++ - как перегруженные функции формы своего рода абстракция в некотором смысле).

Кредиты: Картонный торт Solo. Десертный стол djttwo. Символ был основан на Portal.

Ответ 2

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

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

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

Book(title, ISBN, borrowed)

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


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

Раунд 2

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

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

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

trait Borrowable {
    def itemId:Long
}

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

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

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

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

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

Ответ 3

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

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

Обобщение

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

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

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

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

enter image description here

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

Абстрактные

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

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

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

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

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

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

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

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

Ответ 4

Не ссылаться на достоверный/официальный источник: пример в 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 a StringContainer: " + c.value)

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

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

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

выполнение

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

приводит к

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

Ответ 5

Абстрактные

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

Пример: Проводная модель автомобиля.

Обобщение

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

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

Ответ 6

Я хотел бы предложить ответ для максимально возможной аудитории, поэтому я использую Lingua Franca в Интернете, 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]

Ответ 7

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

"Все симпатичные девушки - женщины". является абстракцией.

"Все симпатичные девушки одеваются". является обобщением.

Ответ 8

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

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