Data questa classe astratta:
public abstract class File
{
public abstract string Name { get; set; }
public abstract void Add(File newFile);
}
È possibile generare la base di un composito:
public class LogFile : File
{
private string _name;
public override string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public override void Add(File newFile)
{
throw new NotImplementedException();
}
}
public class LogFiles : File
{
private IList<File> _files = new List<File>();
public override string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Add(File newFile)
{
_files.Add(newFile);
}
}
Appena creato, viene generato un codice di eccezione. Il più delle volte quando scrivi il codice iniziale, questo a portata di mano. Passando dalla classe di raccolta alla classe dell'oggetto (che è principalmente ciò di cui mi occupo) puoi facilmente dimenticare quale tipo di oggetto hai a che fare. Tanto più che le variabili vengono in genere denominate usando il nome astratto della classe:
File thisFile = new LogFile {Name = "Monday.log"};
File thatFile = new LogFiles();
thatFile.Add(new LogFile() { Name = "Tuesday.log" });
thatFile.Add(new LogFile() { Name = "Wednesday.log" });
thatFile.Name = "My files"; // Runtime error
Tuttavia, se questo codice viene lasciato in, quando questi oggetti sono consumati a monte (come compositi in buona fede), ci possono essere problemi quando viene utilizzato il costruttore senza parametri e una proprietà non implementata genera un'eccezione. Un esempio comune sono le varie forme di serializzazione:
static void SerializeThis(File fileSet)
{
var json = new JavaScriptSerializer().Serialize(fileSet);
}
In questo caso, il codice può essere semplicemente rifattorizzato per rimuovere le eccezioni:
public class LogFile : File
{
private string _name;
public override string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public override void Add(File newFile)
{
}
}
public class LogFiles : File
{
private IList<File> _files = new List<File>();
public override string Name
{
get
{
return string.Empty;
}
set
{
}
}
public override void Add(File newFile)
{
_files.Add(newFile);
}
}
Ma questo rimuove le misure di salvaguardia per le classi derivate e lascia il codice un po 'crufty. Mi sembra una dicotomia tra la ragion d'essere di un composito: "Non mi importa se sono un oggetto o una collezione di oggetti" e mi assicuro che ogni classe derivata sia usata correttamente (come una classe derivata) .
Non posso fare a meno di pensare che mi manca qualcosa di fondamentale qui - qualche pensiero?
Modifica
Per inciso, alcuni strumenti di codifica come ReSharper implementano le proprietà automatiche piuttosto che getter e setter contenenti eccezioni. Anche se questo non è completamente infallibile, è sicuramente un approccio migliore.