Cofnąłbym się tutaj. Koncentrujesz się na wybrednych szczegółach kodu, ale brakuje większego obrazu. Rzućmy okiem na jedną z twoich przykładowych pętli:
int offset = 0;
while(true)
{
Record r = Read(offset);
if(r == null)
{
break;
}
// do work
offset++;
}
Jakie jest znaczenie tego kodu? Oznacza to: „popracuj nad każdym rekordem w pliku”. Ale nie tak wygląda kod . Kod wygląda jak „zachowaj przesunięcie. Otwórz plik. Wejdź w pętlę bez warunku zakończenia. Przeczytaj rekord. Testuj pod kątem nieważności”. Wszystko to zanim przejdziemy do pracy! Pytanie, które powinieneś zadać, brzmi: „w jaki sposób mogę dopasować wygląd tego kodu do semantyki? ” Ten kod powinien być:
foreach(Record record in RecordsFromFile())
DoWork(record);
Teraz kod brzmi jak jego intencja. Oddziel swoje mechanizmy od semantyki . W swoim oryginalnym kodzie łączysz mechanizm - szczegóły pętli - z semantyką - pracą wykonaną dla każdego rekordu.
Teraz musimy wdrożyć RecordsFromFile()
. Jaki jest najlepszy sposób na wdrożenie tego? Kogo to obchodzi? To nie jest kod, na który ktoś będzie patrzył. Jest to podstawowy kod mechanizmu i jego dziesięć linii. Napisz, jak chcesz. Co powiesz na to?
public IEnumerable<Record> RecordsFromFile()
{
int offset = 0;
while(true)
{
Record record = Read(offset);
if (record == null) yield break;
yield return record;
offset += 1;
}
}
Teraz, gdy manipulujemy leniwie obliczoną sekwencją rekordów, możliwe są wszelkiego rodzaju scenariusze:
foreach(Record record in RecordsFromFile().Take(10))
DoWork(record);
foreach(Record record in RecordsFromFile().OrderBy(r=>r.LastName))
DoWork(record);
foreach(Record record in RecordsFromFile().Where(r=>r.City == "London")
DoWork(record);
I tak dalej.
Za każdym razem, gdy piszesz pętlę, zadaj sobie pytanie: „czy ta pętla czyta się jak mechanizm czy jak znaczenie kodu?” Jeśli odpowiedź brzmi „jak mechanizm”, spróbuj przenieść ten mechanizm do własnej metody i napisz kod, aby znaczenie było bardziej widoczne.