Zdarzało się, że krytykowano mnie za to, że mam za dużo nienazwanych stałych w kodzie.
Słyszałem takie opinie, że każda liczba oprócz 0 i 1 oraz każdy string powinny być przypisane do zmiennych i używane tylko za pośrednictwem tych zmiennych, natomiast niedopuszczalne jest przekazanie np. literału 7
jako argumentu dla funkcji.
Mam problem z zaakceptowaniem takiego sposobu myślenia...
Problem polega na tym, że w moim rozumieniu napis
const jakaśNazwa = 7;
JakiśKod(jakaśNazwa);
ma znaczenie mniej więcej takie: „Programisto, możesz dowolnie zmieniać tę wartość w zależności od potrzeb, kod wykorzystujący tę zmienną jest na tyle ogólny, że zadziała sensownie dla każdej wartości. Przypisanie const jakaśNazwa = 7
będzie równie poprawne, co const jakaśNazwa = 1000
, co z kolei będzie równie poprawne, co const jakaśNazwa = -5
.” Innymi słowy, tego rodzaju przypisania traktuję jako parametr konfiguracyjny programu.
Podczas gdy napis:
JakiśKod(7);
ma znaczenie mniej więcej takie: „Programisto, dla jakiejś przyczyny tutaj musi być 7
i tylko 7
, nie możesz zmieniać tej wartości, dopóki nie zrozumiesz, jak działa i co zakłada JakiśKod()
i skąd wartość 7
się wzięła; prawdopodobnie zmiana tej stałej będzie wymagać modyfikacji funkcji JakiśKod
.”
Na przykładzie. Niedawno na własne potrzeby napisałem krótki programik układający paski komiksowe, po 7 na stronę A4. Dlaczego akurat 7? Ponieważ znając z góry wymiary tych pasków rozrysowałem je sobie na kartce papieru, doszedłem do wniosku, że akurat 7 wygląda ładnie i ustaliłem, na jaki sposób jest sens je rozłożyć.
Napisałem więc funkcję
Image<Rgba32> UłóżPaskiNaStronie(List<Image<Rgba32>> paski)
{
// implementacja
}
Co istotne, funkcja zakłada, że lista będzie miała dokładnie 7 pasków. Nie próbowałem jej nawet generalizować na inną liczbę pasków, raz dlatego, że nie mam takiej potrzeby, dwa, bo musiałyby one wtedy być rozmieszczone na stronie zupełnie inaczej, a trudno, żebym ręcznie rozrysowywał sensowne układy dla każdej liczby pasków na stronie od 1 do 1000 albo trenował w tym celu jakiś model sztucznej inteligencji.
(Inna rzecz, że ta funkcja - z tych samych powodów - zakłada także przybliżone wymiary tych pasków, a w szczególności, że pierwszy na liście będzie "niedzielny", a zatem 2x większy)
Kod wołający tę funkcję:
List<Image<Rgba32>> PodzielNaStrony(IEnumerable<Image<Rgba32>> paski)
{
return paski.SplitToChunks(7).Select(chunk => UłóżPaskiNaStronie(chunk)).ToList();
}
(gdzie SplitToChunks
to prosta funkcyjka napisana przeze mnie, dzieląca listę na podlisty o długości n elementów).
Tragedia! Magic Value! Należy napisać raczej:
const int PaskówNaStronę = 7;
List<Image<Rgba32>> PodzielNaStrony(IEnumerable<Image<Rgba32>> paski)
{
return paski.SplitToChunks(PaskówNaStronę).Select(chunk => UłóżPaskiNaStronie(chunk)).ToList();
}
No ale właśnie program źle zadziała, jeśli zrobimy tu const paskówNaStronę = 8;
... a przecież napis const paskówNaStronę = 7;
aż zaprasza do zmiany tej liczby na inną.
(Z drugiej strony funkcja SplitToChunks(7)
już przyjmuje 7
jako parametr a nie ma tej stałej zahardkodowanej, bo ma zadziałać równie poprawnie dla każdej wartości)
Przypuszczam, że tak rozumując, przeczę ogólnie ustalonym konwencjom... Więc w jaki inny sposób zakomunikować, czy jakąś stałą można dowolnie się bawić, czy też jest ona przez coś wymuszona?
SplitToChunks
->Chunk