Prywatne pola w klasie.

Tym razem poruszę temat który do tej pory wydawał mi się błahy, oczywisty i niewart opisywania. Zdecydowałem się jednak na publikację tego postu, ponieważ aktualnie robię refaktoryzację kodu, który używa prywatnych pól w niewłaściwy sposób. Ale o co chodzi?

Wyobraźmy sobie klasę, która jest prostym kalkulatorem:

    class Calc
    {
        private int val;
        private int val2;
        private int result;
        
        public int Add(int val, int val2)
        {
            this.val = val;
            this.val2 = val2;

            DoAdd();

            return result;
        }

        private void DoAdd()
        {
            result = val + val2;
        }
    }

Oczywiście, implementacja taka nie ma sensu i wygląda okropnie. Da się ją jednak rozczytać, ponieważ jest stosunkowo nieduża. W moim jednak przypadku klasy są dość rozbudowane, a logika uzyskania wartości pola rozbudowana. Powoduje to całkowite zaciemnienie przepływu danych w klasie i tak naprawdę nigdy nie wiemy skąd wzięła się wartość w polu, czy w ogóle została ustawiona i czy nie zmieniła się od ostatniego sprawdzenia.

Zauważyłem, że najczęstszym powodem wprowadzania takich pól jest brak pomysłu na rozwiązanie kilku problemów:

  1. konieczność zwrócenia z metody więcej niż jednej wartości
  2. potrzeba skorzystania z wartości w wielu miejscach klasy
  3. podział skomplikowanej metody na mniejsze

Z każdym z tych przypadków, można sobie jednak poradzić w stosunkowo łatwy sposób.

konieczność zwrócenia z metody więcej niż jednej wartości:
Przykładem takiej sytuacji może być np. metoda walidująca dane, z której oprócz wyniku walidacji potrzebujemy również informację dlaczego dane walidacji nie przeszły. W takiej sytuacji należy stworzyć niemutowalną klasę “ValidationResult” i zakapsułkować w niej wynik działania metody.

potrzeba skorzystania z wartości w wielu miejscach klasy:
Załóżmy że mam klasę która przetwarza dane. Klasa ta na samym początku przetwarzania generuje wartości, które później są nam potrzebne. Pojawia się wtedy pole, które jest inicjowane w metodzie Init i wykorzystywane w pozostałych np. Validate i Eksport. Wartości takich mamy więcej, więc przekazywanie ich jako parametr do każdej metody mija się z celem, a poza tym nie mamy jak tego wszystkiego zwrócić z metody init.

Najlepszym, w takiej sytuacji, rozwiązaniem jest zastosowanie wzorca “method object”. W konstruktorze naszego obiektu wykonujemy kod “init” a wartości zapisujemy w prywatnych polach READONLY. Dzięki temu mamy pewność że kod który jest wykonywany będzie pracował na zainicjowanych i niezmiennych danych.

podział skomplikowanej metody na mniejsze:
Tak samo sprawa ma się w tym przypadku, jeżeli metoda jest skomplikowana i zawiera zmienne, które są potrzebne praktycznie przez cały czas przetwarzania, należy z fragmentu metody stworzyć “method object” i zainicjować go pobranymi danymi.

W ogólności uważam, że używanie pól innych niż “readonly” jest złym pomysłem i nie należy tego robić. W polach powinny być trzymana tylko dane i zależności, które nie zmieniają się przez cały okres życia obiektu.

Comments:0

Leave a Reply

Your email address will not be published. Required fields are marked *