In quali situazioni ha senso utilizzare un'enumerazione durante la scrittura di codice orientato agli oggetti?

5

Enumerazioni 1 sono spesso associate al codice procedurale piuttosto che al codice orientato agli oggetti. Tendono a dare origine a dichiarazioni switch simili disseminate nel codice e, in generale, queste sono sostituite dal polimorfismo nel codice orientato agli oggetti.

Ad esempio, vedi Sostituisci codice tipo con classe , Sostituisci codice tipo con sottoclassi e Sostituisci codice tipo con stato / strategia in < em> Refactoring di Martin Fowler. Strettamente correlato, vedi Sostituisci condizionale con polimorfismo nello stesso volume; Bob Martin ha anche un bel po 'da dire sugli svantaggi delle istruzioni switch in Clean Code (ad esempio, euristico G23 Preferisci polimorfismo a If / Else o Switch / Case ).

Tuttavia, poiché l'orientamento all'oggetto, come ogni altro buon paradigma, può essere uno strumento potente ma non un proiettile d'argento, ci sono momenti in cui l'uso di un'enumerazione è una buona decisione?

1 Uso ampiamente questo termine; non solo per qualcosa che è strettamente un enum in un linguaggio basato su C, ma per qualsiasi insieme di entità che sono usate per rappresentarlo (una classe con un insieme di membri statici, ecc ...).

    
posta Kazark 17.09.2013 - 23:29
fonte

5 risposte

18

PEP 435 era la proposta di aggiungere enumerazioni a Python. La motivazione per aggiungere enumerazioni:

The properties of an enumeration are useful for defining an immutable, related set of constant values that may or may not have a semantic meaning. Classic examples are days of the week (Sunday through Saturday) and school assessment grades ('A' through 'D', and 'F'). Other examples include error status values and states within a defined process.

Le enumerazioni hanno lo stesso scopo nel codice orientato agli oggetti come nel codice procedurale.

    
risposta data 18.09.2013 - 01:16
fonte
2

. . . are there times when using an enumeration is a good decision?

Ho usato un enum (tramite C #) per definire un set fisso e ordinato di "attività". I valori interi sottostanti hanno definito l'ordine. Una classe wrapping utilizzava l'enum per esempio l'uguaglianza. Una classe di raccolta utilizzava l'enumerazione per l'ordinamento e l'applicazione dell'unicità w / nella raccolta. Queste classi sostituirono il codice che era irrimediabilmente rotto funzionalmente e concettualmente.

Quella enum era effettivamente una struttura di dati "core" al centro di strutture di dati compositi con funzioni più elevate. Nel catturare l'essenza degli oggetti del dominio al centro i compositi erano puliti e la funzionalità aziendale era deliziosamente concisa.

    
risposta data 18.09.2013 - 14:35
fonte
1

Come hai detto, le enumerazioni tendono a dare origine a casi di discriminazione ... dopo tutto, in quale altro modo hai senso dei diversi valori dell'enumerazione?

Ma in OOP, abbiamo già un modo perfettamente valido di fare discriminazioni tra maiuscole e minuscole: invio di messaggi polimorfici. Questo è tutto ciò di cui hai bisogno. Non hai bisogno di case o switch o anche if o for o while . Smalltalk non ne ha nessuno, non ha condizionali, non ha loop, non ha discriminazioni. Solo invio di messaggi polimorfici. E funziona perfettamente in questo modo.

Quindi, dove dovresti fare qualcosa di simile in una lingua senza invio di messaggi polimorfici:

enum Weekday {
  Monday, 
  Tuesday, 
  Wednesday, 
  Thursday, 
  Friday, 
  Saturday, 
  Sunday
}

def weekdayToString(day: Weekday) = match day {
  case Weekday.Monday    => "Monday"
  case Weekday.Tuesday   => "Tuesday"
  case Weekday.Wednesday => "Wednesday"
  case Weekday.Thursday  => "Thursday"
  case Weekday.Friday    => "Friday"
  case Weekday.Saturday  => "Saturday"
  case Weekday.Sunday    => "Sunday"
}

def printWeekday(day: Weekday) = println(weekdayToString(day))

printWeekday(Weekday.Wednesday)
// Wednesday

In una lingua con invio di messaggi polimorfici, lo farebbe in questo modo:

sealed trait Weekday
object Monday    extends Weekday { override def toString() = "Monday"    }
object Tuesday   extends Weekday { override def toString() = "Tuesday"   }
object Wednesday extends Weekday { override def toString() = "Wednesday" }
object Thursday  extends Weekday { override def toString() = "Thursday"  }
object Friday    extends Weekday { override def toString() = "Friday"    }
object Saturday  extends Weekday { override def toString() = "Saturday"  }
object Sunday    extends Weekday { override def toString() = "Sunday"    }

def printWeekday(day: Weekday) = println(day)

printWeekday(Wednesday)
// Wednesday

(Questo è in realtà un codice Scala eseguibile.)

In altre parole: puoi sempre sostituire un'enumerazione con discriminazione dei casi con una gerarchia di ereditarietà con il polimorfismo.

È interessante notare che Scala ha ha enumerazioni, ma ci sono degli sforzi per rimuoverle dalla lingua. Semplicemente non aggiungono alcun significativo potere espressivo a un linguaggio che ha già degli oggetti.

    
risposta data 18.09.2013 - 11:22
fonte
1

Le enumerazioni hanno fondamentalmente due scopi:

  1. Ti aiutano a rafforzare la sicurezza del tipo
  2. Possono fungere da contenitore per costanti mutuamente esclusive

Considerati tali scopi, a cosa serve l'enumerazione, anche in linguaggi orientati agli oggetti?

Secondo me la codifica consiste nel trovare il modo giusto per esprimere qualcosa in un contesto specifico.

Spesso non hai bisogno di una lezione in piena regola. Non si introduce una classe se è necessario incapsulare dati statici costanti senza o con un comportamento minimo. Un enum è un modo migliore per esprimere i tuoi bisogni semantici piuttosto che derivare dal tipo di sottoclasse, che è praticamente l'unica alternativa che coinvolge il polimorfismo (e porta anche a cambiare le istruzioni sui tipi di sottoclassi).

Inoltre, si mantiene il controllo sui valori disponibili tutto il tempo. Non puoi semplicemente mettere il buon vecchio Liskov da usare e intrufolarti in un'altra sottoclasse con un nuovo comportamento. In alcuni scenari, questo è un comportamento molto desiderato. Si dovrebbe anche notare che in alcune lingue, come le enumerazioni di Java, possono comportare comportamenti. In modo che possano portare il comportamento che desideri nel posto che vuoi che sia. Come ha detto Jon Skeet:

An enum in Java is a fixed set of objects, basically. The benefit is that you know that if you have a reference of that type, it's always either null or one of the well-known set.

In pratica, nella maggior parte dei casi in cui dovresti semplicemente restituire valori arbitrari ben definiti (come i codici di errore) dovresti passare a un enum per rafforzare la sicurezza del tipo. In questo modo si limita la quantità di errore che viene introdotta restituendo un valore errato per errore, in quanto il compilatore può controllarlo. Soprattutto se c'è poca necessità di comportamento e i dati associati sono statici.

    
risposta data 18.09.2013 - 00:08
fonte
0

Sì, ci sono usi validi delle enumerazioni, anche in sistemi che sono principalmente orientati agli oggetti.

In un'architettura a più livelli, un'enumerazione può essere preziosa. la separazione delle preoccupazioni tra strati o moduli. Ad esempio, un lexer e un parser possono parlare tra loro in token, potrebbe essere una sorta di enumerazione.

Analogamente, in un'architettura orientata ai servizi, quando esiste una ragione per un certo tipo di codice di tipo per attraversare più servizi e questo non può essere realizzato con più eventi, potrebbe esserci una buona ragione per usare un'enumerazione.

Su una nota leggermente diversa, potrebbe essere un'utile misura temporanea nel design emergente in cui stai aspettando di essere sicuro di ottenere l'abstract giusto prima di passare a una soluzione polimorfica.

Ma ancora, usa le enumerazioni con cura.

    
risposta data 17.09.2013 - 23:29
fonte

Leggi altre domande sui tag