Non sono riuscito a trovare una domanda che non fosse specifica per alcuni casi, quindi cercherò di renderla molto generica.
Abbiamo bisogno di una classe base di estrazione per un insieme di documenti, per esempio. Ogni documento ha le sue proprietà specifiche, ma in definitiva sono documenti. Quindi vogliamo fornire operazioni di estrazione comuni per tutti loro.
Anche se sono tutti documenti, come ho detto, sono in qualche modo diversi. Alcuni potrebbero avere alcune proprietà, ma alcuni potrebbero non farlo.
Immaginiamo di avere la classe astratta base Document
e le classi FancyDocument
e NotSoFancyDocument
che ereditano da essa.
Il FancyDocument
ha un SectionA
, il NotSoFancyDocument
no.
Detto questo, cosa difenderebbe come il modo migliore di implementarlo? Ecco le due opzioni:
- Svuota metodi virtuali nella classe base
I metodi virtuali vuoti nella classe base consentirebbero al programmatore di ignorare solo i metodi che hanno senso per i diversi tipi di documenti. Avremmo quindi un comportamento predefinito sulla classe base astratta, che restituirebbe default
per i metodi, in questo modo:
public abstract class Document
{
public virtual SectionA GetDocumentSectionA()
{
return default(SectionA);
}
}
public class FancyDocument : Document
{
public override SectionA GetDocumentSectionA()
{
// Specific implementation
}
}
public class NotSoFancyDocument : Document
{
// Does not implement method GetDocumentSectionA because it doesn't have a SectionA
}
- Metodi vuoti concreti o metodi concreti che lanciano un
NotImplementedException
Poiché NotSoFancyDocument
non ha un SectionA
, ma gli altri lo fanno, potremmo semplicemente restituire default per il metodo in esso, oppure potrebbe lanciare un NotImplementedException
. Ciò dipenderà dal modo in cui è stato scritto il programma e da altre cose. Potremmo inventare qualcosa di simile:
//// Return the default value
public abstract class Document
{
public abstract SectionA GetDocumentSectionA();
}
public class FancyDocument : Document
{
public override SectionA GetDocumentSectionA()
{
// Specific implementation
}
}
public class NotSoFancyDocument : Document
{
public override SectionA GetDocumentSectionA()
{
return default(SectionA);
}
}
o
//// Throw an exception
public abstract class Document
{
public abstract SectionA GetDocumentSectionA();
}
public class FancyDocument : Document
{
public override SectionA GetDocumentSectionA()
{
// Specific implementation
}
}
public class NotSoFancyDocument : Document
{
public override SectionA GetDocumentSectionA()
{
throw new NotImplementedException("NotSoFancyDocument does not have a section A");
}
}
Personalmente, penso che l'approccio al metodo astratto sia migliore, dal momento che significa "Ehi, ho bisogno che tu sia in grado di ottenere una SezioneA come documento. Non mi interessa come." mentre il metodo virtuale significa "Ehi, io ho questa SezioneA qui. Se non è abbastanza buono per te, sentiti libero di cambiare il modo in cui lo ottengo".
Penso che il primo sia un segno di odore di programmazione orientato agli oggetti.
Quali sono le tue opinioni su questo?