La logica condizionale dovrebbe sempre essere codificata tramite il sistema di tipi, ove possibile?

4

Ho una scelta.

Opzione 1:

public class Sample
{
    bool IsRelevant { get; set; }
}

Opzione 2:

public class Sample
{
}

public class RelevantSample : Sample
{
}

C'è una chiara regola ben nota su come prendere questa decisione?

La mia ricerca: ho già sentito parlare del refactoring "Sostituisci condizionale con polimorfismo", ma di solito si occupa di istruzioni switch di grandi dimensioni:

link

link

link

C'è una domanda in qualche modo correlata che descrive una situazione diversa (flag come argomento di un metodo piuttosto che come parte di un'entità di dominio): È sbagliato usare un parametro booleano per determinare il comportamento?

    
posta Den 30.11.2015 - 23:39
fonte

3 risposte

8

Penso che la scelta della lingua sia irrilevante. Ciò che è importante nel prendere la decisione è come vengono utilizzate le informazioni.

Se Sample s si comporta diversamente a seconda del valore di isRelevent , allora ha assolutamente senso suddividerlo in tre (non due) classi. La classe base Sample , una classe RelevantSample e una classe IrrelevantSample . Tutti gli oggetti di esempio verrebbero istanziati da una delle due classi successive (seguendo quindi l'euristica che le classi di base dovrebbero essere astratte.) Altri oggetti possono quindi informare gli oggetti di esempio degli eventi senza preoccuparsi se il campione è rilevante o meno.

Tuttavia, se si tratta più di altri oggetti che si comportano in modo diverso a seconda della pertinenza del campione, allora si vorrebbe andare con l'opzione 1 e rendere isRelevant un campo che può essere interrogato.

    
risposta data 01.12.2015 - 00:24
fonte
11

Hai già due buone risposte; una terza ragione per rimanere fedeli alla proprietà è che il modello che stai descrivendo è un modello "one shot". Tutto diventa a forma di pera quando aggiungi un secondo booleano. Iniziamo con:

public class Sample
{
    public bool IsRelevant { get; protected set; }
}

e lo rifattiamo in:

public abstract class Sample {}
public class RelevantSample : Sample {}
public class IrrelevantSample : Sample {}

E ora ci rendiamo conto, oh, aspetta, i campioni possono essere anche frobby o antifrobby:

public abstract class Sample 
{
    public bool IsFrobby { get; protected set; }
}
public class RelevantSample : Sample {}
public class IrrelevantSample : Sample {}

E ora come spostiamo quel nel sistema dei tipi?

public class RelevantFrobbySample : RelevantSample {}
public class RelevantAntifrobbySample : RelevantSample {}
public class IrrelevantFrobbySample : IrrelevantSample {}
public class IrrelevantAntifrobbySample : IrrelevantSample {}

E ora voglio fare un metodo che richiede solo campioni fasulli. Come faccio?

I linguaggi di ereditarietà singoli ti richiedono di scegliere il tuo "pivot di ereditarietà" con estrema attenzione perché ne ottieni solo uno.

    
risposta data 15.12.2015 - 17:10
fonte
3

Non in C #.

Codificare il comportamento implicito in tipi è malvagio in C # e linguaggi simili, perché l'unico modo per ottenere informazioni dal tipo è if x is T tipi di controlli (o trucco con dynamic o riflessione). Quindi qualsiasi modifica ad esso (aggiungendo una nuova variante, cambiando il comportamento di un tipo) significa che devi entrare in tutti i tuoi consumatori, violando il principio Open Closed.

    
risposta data 30.11.2015 - 23:48
fonte

Leggi altre domande sui tag