Saltar al contenido

¿Cuál es la diferencia entre abstracción y generalización?

Solución:

Ciertamente una pregunta muy interesante. Encontré este artículo sobre el tema, que afirma de manera concisa que:

Mientras que la abstracción reduce la complejidad al ocultar detalles irrelevantes, la generalización reduce la complejidad al reemplazar múltiples entidades que realizan funciones similares con una sola construcción.

Tomemos el viejo ejemplo de un sistema que administra libros para una biblioteca. Un libro tiene toneladas de propiedades (número de páginas, peso, tamaño (s) de fuente, portada, …) pero para el propósito de nuestra biblioteca es posible que solo necesitemos

Book(title, ISBN, borrowed)

Simplemente extrajimos los libros reales de nuestra biblioteca y solo tomamos las propiedades que nos interesaban en el contexto de nuestra aplicación.


La generalización, por otro lado, no intenta eliminar los detalles, sino hacer que la funcionalidad sea aplicable a una gama más amplia (más genérica) de elementos. Los contenedores genéricos son un muy buen ejemplo de esa mentalidad: no querría escribir una implementación de StringList, IntListy así sucesivamente, por lo que prefieres escribir un genérico Lista que se aplica a todos los tipos (como List[T] en Scala). Tenga en cuenta que no lo ha hecho abstraído la lista, debido a que no eliminó ningún detalle u operación, simplemente los hizo aplicables genéricamente a todos sus tipos.

La ronda 2

¡La respuesta de @dtldarek es realmente una muy buena ilustración! En base a esto, aquí hay un código que podría proporcionar más aclaraciones.

Recuerda el Book ¿Yo mencione? Por supuesto, hay otras cosas en una biblioteca que uno puede tomar prestadas (llamaré al conjunto de todos esos objetos Borrowable aunque probablemente ni siquiera sea una palabra: D):

Todos estos elementos tendrán un abstracto representación en nuestra base de datos y lógica empresarial, probablemente similar a la de nuestro Book. Además, podríamos definir un rasgo que sea común a todos Borrowables:

trait Borrowable {
    def itemId:Long
}

Entonces podríamos escribir generalizado lógica que se aplica a todos Borrowables (en ese momento no nos importa si es un libro o una revista):

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

Para resumir: almacenamos un representación abstracta de todos los libros, revistas y DVD de nuestra base de datos, porque una representación exacta no es factible ni necesaria. Luego seguimos adelante y dijimos

No importa si un cliente toma prestado un libro, una revista o un DVD. Siempre es el mismo proceso.

Así nosotros generalizado la operación de pedir prestado un artículo, definiendo todas las cosas que uno puede pedir prestado como Borrowables.

Objeto:

foto de la torta del portal

Abstracción:

símbolo de la torta

Generalización:

muchos postres

Ejemplo en Haskell:

La implementación del orden de selección mediante el uso de una cola de prioridad con tres interfaces diferentes:

  • una interfaz abierta con la cola que se implementa como una lista ordenada,
  • una interfaz abstraída (por lo que los detalles están ocultos detrás de la capa de abstracción),
  • una interfaz generalizada (los detalles aún son visibles, pero la implementación es más flexible).
{-# 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

El código también está disponible a través de pastebin.

Vale la pena notar los tipos existenciales. Como ya señaló @lukstafi, la abstracción es similar al cuantificador existencial y la generalización es similar al cuantificador universal. Observe que hay una conexión no trivial entre el hecho de que ∀xP (x) implica ∃xP (x) (en un universo no vacío), y que rara vez hay una generalización sin abstracción (incluso c ++ – como las funciones sobrecargadas de la forma una especie de abstracción en cierto sentido).

Créditos:
Pastel portal de Solo. Mesa de postres de djttwo. El símbolo es el icono de pastel de material.io.

Voy a utilizar algunos ejemplos para describir la generalización y la abstracción, y me referiré a este artículo.

Que yo sepa, no existe una fuente oficial para la definición de abstracción y generalización en el dominio de la programación (Wikipedia es probablemente lo más cercano a una definición oficial en mi opinión), así que en su lugar he utilizado un artículo que considero creíble.

Generalización

El artículo dice que:

“El concepto de generalización en OOP significa que un objeto encapsula el estado y el comportamiento común de una categoría de objetos”.

Entonces, por ejemplo, si aplica la generalización a las formas, las propiedades comunes para todos los tipos de forma son el área y el perímetro.

Por lo tanto, una forma generalizada (por ejemplo, Forma) y sus especializaciones (por ejemplo, un Círculo) se pueden representar en clases de la siguiente manera (tenga en cuenta que esta imagen se ha tomado del artículo mencionado anteriormente)

ingrese la descripción de la imagen aquí

De manera similar, si estuviera trabajando en el dominio de los aviones a reacción, podría tener un Jet como generalización, que tendría una propiedad de envergadura. Una especialización de un Jet podría ser un FighterJet, que heredaría la propiedad de envergadura y tendría su propia propiedad exclusiva de los aviones de combate, por ejemplo, NumberOfMissiles.

Abstracción

El artículo define la abstracción como:

“el proceso de identificar patrones comunes que tienen variaciones sistemáticas; una abstracción representa el patrón común y proporciona un medio para especificar qué variación usar” (Richard Gabriel) “

En el dominio de la programación:

Una clase abstracta es una clase padre que permite la herencia, pero nunca se puede crear una instancia.

Por lo tanto, en el ejemplo dado en la sección Generalización anterior, una forma es abstracta como:

En el mundo real, nunca calcula el área o el perímetro de una forma genérica, debe saber qué tipo de forma geométrica tiene porque cada forma (por ejemplo, cuadrado, círculo, rectángulo, etc.) tiene sus propias fórmulas de área y perímetro.

Sin embargo, además de ser una forma abstracta es también una generalización (porque “encapsula el estado común y el comportamiento de una categoría de objetos” donde en este caso los objetos son formas).

Volviendo al ejemplo que di sobre Jets y FighterJets, un Jet no es abstracto como una instancia concreta de un Jet es factible, ya que uno puede existir en el mundo real, a diferencia de una forma, es decir, en el mundo real no se puede sostener una forma. mantenga una instancia de una forma, por ejemplo, un cubo. Entonces, en el ejemplo de la aeronave, un Jet no es abstracto, es una generalización, ya que es posible tener una instancia “concreta” de un jet.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *