Se un enum viene utilizzato senza operazioni bit a bit e tutti i suoi valori sono predefiniti singolarmente, violano il principio di open closed?

3

Ad esempio, se uso un enum, che tutti i valori sono definiti manualmente individualmente, ad esempio:

public enum MyNum{
    Zero(0),
    One(1),
    Two(2);

    private final int value;

    MyNum(int value){
        this.value=value;
    }

    public int getValue(){
        return value;
    }
}

public class Test{
    public static MyNum getUnknownMyNum(){
        return MyNum.Two;
    }

    public static void main(String[] args){
        MyNum n1=MyNum.One;
        MyNum n2=MyNum.Two;
        System.out.println(n1==getUnknownMyNum());
        System.out.println(n2==getUnknownMyNum());
    }
}

e non utilizzare la funzione di operazione bit a bit, ad esempio:

MyNum n=MyNum.Zero | MyNum.One;

, viola i principi di open-closed? Perché penso che in questo caso, aggiungendo un nuovo enum (ad esempio: Tre (3)) è necessario modificare MyNum.java.

Se tutti i valori enum vengono assegnati manualmente e non ho bisogno di operazioni bit a bit quando si utilizza quell'enumerazione, significa che ciascun valore è indipendente l'uno dall'altro, che è diverso da

public enum MyNum{
    Zero,
    One,
    Two
}

che calcola ogni valore da zero, e quindi possiamo sostituire il seguente enum:

public enum MyNum{
    Zero(0),
    One(1),
    Two(2);

    private final int value;

    MyNum(int value){
        this.value=value;
    }

    public int getValue(){
        return value;
    }
}

in singole classi:

public interface MyNum{
}

public class Zero implements MyNum{
    public static final Zero MyNum=new Zero();
    public static final int value=0;
}

public class One implements MyNum{
    public static final One MyNum=new One();
    public static final int value=1;
}

public class Two implements MyNum{
    public static final Two MyNum=new Two();
    public static final int value=2;
}

public class Test{
    public static MyNum getUnknownMyNum(){
        return Two.MyNum;
    }

    public static void main(String[] args){
        MyNum n1=One.MyNum;
        MyNum n2=Two.MyNum;
        System.out.println(n1==getUnknownMyNum());
        System.out.println(n2==getUnknownMyNum());
    }
}

che sostituisce quello che utilizza l'enumerazione nella versione che utilizza un'interfaccia con le classi. E questa volta non abbiamo bisogno di modificare MyNum.java per aggiungere un nuovo enum "Tre", ma creare una nuova classe Three.java:

public class Three implements MyNum{
    public static final Three MyNum=new Three();
    public static final int value=3;
}

che si adatta a principio aperto chiuso, è vero?

Nota: per enumerazioni c ++ per esempio,

enum class MyNum{
    Zero,
    One,
    Two
}

enum class MyNum{
    Zero,
    One=2,
    Two
}

non considerano "tutti i valori sono definiti manualmente singolarmente" perché alcuni valori devono calcolare dal valore precedente

    
posta ggrr 14.06.2017 - 06:33
fonte

3 risposte

3

La risposta è sì e no . Il principio open-close afferma che:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Ma questo principio è inteso per tipi e comportamenti, non per valori (fare riferimento al documento seminale di R.C.Martin - link nell'articolo di Wikipedia).

No: il principio di apertura / chiusura è limitato quando applicato ai valori

Portiamola all'estremo con i tipi della nostra vita quotidiana: interi. In base alle specifiche del linguaggio, un numero intero Java compreso tra -2147483648 e 2147483647. Prendi una qualsiasi delle tue classi conformi a O / C con un membro int privato con un setter pubblico. Ovviamente, non è possibile utilizzarlo con il valore 2147483649. Se quel int è profondamente incorporato nel comportamento della classe, dovrai riscriverne grandi parti per estendere l'intervallo di valori accettabili. Questo caso estremo ti porterà a considerare che questa classe non è completamente compatibile con Open / Close?

Quindi:

  • Il principio di apertura / chiusura deve essere compreso all'interno dei valori validi per i quali il tipo è stato progettato .
  • L'estensione è una specializzazione, quindi potresti limitare ulteriormente i valori accettabili e non ingrandirli (il set ingrandito sarebbe una superclasse).
  • Non essere in grado di aggiungere un nuovo valore a una enumerazione non è quindi una violazione di quel principio!

Sì: il principio di apertura / chiusura è applicabile ai comportamenti

Sfortunatamente mettere troppa intelligenza in un enum è un problema. Per definizione, un java enum è implicitamente definitivo. Quindi non è possibile aggiungere nuovi comportamenti (metodi) o ignorare quelli esistenti.

Questo design dell'utilizzo di un enum con metodi non consente di estendere il tipo per aggiungere un insieme di operatori (ad esempio add() , increment() ) o limitare l'insieme di valori nel setter (ad esempio una percentuale ipotetica tipo diMyBinary che userebbe solo MyNum.Zero e MyNum.One subrange da MyNum ).

A mio avviso, questo contraddice il principio di apertura / chiusura. Un approccio "aperto" (estensibile) userebbe l'enum solo per definire i valori accettabili di base, e quindi usarlo in una classe ordinaria (estensibile) secondo il principio di composizione sull'ereditarietà.

    
risposta data 14.06.2017 - 08:46
fonte
1

Nelle lingue in cui enum non può ereditare da altre enumerazioni non esiste un buon modo per estenderle direttamente. Quindi usarli per cose che potrebbero in seguito necessitare di nuovi valori è in realtà una violazione del principio di open closed.

La soluzione alternativa all'uso degli oggetti con membri statici che implementano un'interfaccia funziona. Tuttavia, c'è un trucco subdolo in cui le enumerazioni implementano un'interfaccia.

L'enum trick estensibile

Per @Joachim Sauer

interface SomeType {
  String name();
}

public enum CoreEnum implements SomeType {
  VALUE1,
  VALUE2;
}

public enum AdditionalEnum implements SomeType {
  VALUE3,
  VALUE4;
}

Per @cletus

interface Digit {
  int getValue();
}

enum Decimal implements Digit {
  ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE;

  private final int value;

  Decimal() {
    value = ordinal();
  }

  @Override
  public int getValue() {
    return value;
  }
}

enum Hex implements Digit {
  A, B, C, D, E, F;

  private final int value;

  Hex() {
    value = 10 + ordinal();
  }

  @Override
  public int getValue() {
    return value;
  }
}

Per @Mykola Golubyev

#include <iostream>
#include <ostream>

class Enum
{
public:
    enum
    {
        One = 1,
        Two,
        Last
    };
};

class EnumDeriv : public Enum
{
public:
    enum
    {
        Three = Enum::Last,
        Four,
        Five
    };
};

int main()
{
    std::cout << EnumDeriv::One << std::endl;
    std::cout << EnumDeriv::Four << std::endl;
    return 0;
}
    
risposta data 14.06.2017 - 07:48
fonte
0

Anche quando non si eseguono operazioni bit a bit su un enumerro simile, la definizione dei valori numerici per i suoi membri può essere utile. Lo usiamo quando comunichiamo con altri sistemi, ad es. c'è un enum per gli identificatori dei messaggi e un enum per i codici di risposta.

È "chiuso per modifica": non è possibile modificare un numero senza modificare il sistema esterno. Ma nello stesso senso, è non "aperto per l'estensione", come anche qui il sistema esterno richiede un'estensione equivalente.

    
risposta data 24.07.2017 - 09:29
fonte

Leggi altre domande sui tag