Nickemannen skrev:
Det jag kan tycka är tråkigt är att om jag har dessa objekt, Musiker och skivor.
Säg att jag vill kunna lista alla skivor en musiker har på flera olika sorteringar, och kanske vilja ta ut antalet, kanske filtrera listan på olika sätt. Då blir jag tvungen att lägga många metoder i musiker som jag annars hade kunnat lagt i en egendefinerad collection.
Du kan nöja dig med en metod för filtrering om du tillämpar Specification pattern (PDF-länk).
Då definierar du alltså ett interface:
public interface RecordSpecification
{
bool IsSatisfiedBy(Record record);
}
som du sedan använder som formell parameter till en generell filtreringsmetod:
public class Musician
{
public ReadOnlyCollection<Record> getFilteredSubsetOfRecords(
RecordSpecification recordSpecification
)
{
List<Record> filteredListOfRecords = new List<Record>();
foreach(Record record in this.records) {
if (recordSpecification.IsSatisfiedBy(record))
{
filteredListOfRecords.Add(record);
}
}
return new ReadOnlyCollection<Record>(filteredListOfRecords);
}
// ....
private List<Record> records = new List<Record>();
}
och som aktuell (konkret) parameter till metoden använder du implementationer av interfacet, t.ex. en sån här implementation:
public class NumberOfSongsSpecification: RecordSpecification
{
private int min;
private int max;
public NumberOfSongsSpecification(int min, int max)
{
this.min = min;
this.max = max;
}
public bool IsSatisfiedBy(Record record)
{
return this.min <= record.NumberOfSongs && record.NumberOfSongs <= this.max;
}
}
och anropet kan då se ut så här t.ex. för att filtrera ut alla skivor där antalet låtar är 9-11:
ReadOnlyCollection<Record> records = musician.getFilteredSubsetOfRecords(
new NumberOfSongsSpecification(9, 11)
);
Ett alternativ till att skapa ett eget interface är att använda .NET's inbyggda stöd för Specification, m.h.a. "Predicate<T>".
Exempel:
public class Musician
{
public ReadOnlyCollection<Record> getFilteredSubsetOfRecords(Predicate<Record> recordFilterPredicate)
{
List<Record> filteredSubsetOfRecords = records.FindAll(recordFilterPredicate);
return new ReadOnlyCollection<Record>(filteredSubsetOfRecords);
}
// ...
private List<Record> records = new List<Record>();
}
Ett anrop kan då se ut så här:
ReadOnlyCollection<Record> records = musician.getFilteredSubsetOfRecords(
delegate(Record record) {
return 9 <= record.NumberOfSongs && record.NumberOfSongs <= 11;
}
);
När det gäller sortering så kan man använda "Comparison<T>" så här:
public ReadOnlyCollection<Record> getFilteredSubsetOfRecords(
Predicate<Record> recordFilterPredicate,
Comparison<Record> recordSortComparison
)
{
List<Record> filteredSubsetOfRecords = records.FindAll(recordFilterPredicate);
filteredSubsetOfRecords.Sort(recordSortComparison);
return new ReadOnlyCollection<Record>(filteredSubsetOfRecords);
}
Två anrops-exempel med både filtrering och sortering:
ReadOnlyCollection<Record> records = musician.getFilteredSubsetOfRecords(
delegate(Record record) { return record.TotalLengthInSeconds >= 2400; },
delegate(Record x, Record y) { return -1*(x.NumberOfSongs - y.NumberOfSongs); }
);
ReadOnlyCollection<Record> records = musician.getFilteredSubsetOfRecords(
delegate(Record record) { return record.NumberOfSongs >= 10; },
delegate(Record x, Record y) { return 1 * (x.TotalLengthInSeconds - y.TotalLengthInSeconds); }
);
/ Tomas