Używanie atrybutów z kodem Ruby

Andreas Larsson / Folio Images / Getty Images

Spójrz na dowolny kod zorientowany obiektowo, a wszystkie mniej więcej podążają za tym samym wzorcem. Utwórz obiekt, wywołaj niektóre metody na tym obiekcie i uzyskaj dostęp do atrybutów tego obiektu. Niewiele więcej można zrobić z obiektem, z wyjątkiem przekazania go jako parametru do metody innego obiektu. Ale to, o co tutaj chodzi, to atrybuty.

Atrybuty są jak zmienne instancji, do których można uzyskać dostęp za pomocą notacji kropkowej obiektu. Na przykład, imię osoby uzyska dostęp do nazwiska osoby. Podobnie często możesz przypisać do atrybutów takich jak person.name = "Alice". Jest to funkcja podobna do zmiennych członkowskich (np. W C ++), ale nie do końca taka sama. Nie ma w tym nic specjalnego, atrybuty są implementowane w większości języków za pomocą "getters" i "setters" lub metod, które pobierają i ustawiają atrybuty ze zmiennych instancji.

Ruby nie dokonuje rozróżnienia między atrybutami pobierającymi i ustawiającymi, a normalnymi metodami. Ze względu na elastyczną metodę wywoływania metody Ruby nie trzeba wprowadzać rozróżnienia. Na przykład, imię osoby i imię osoby() jesteście tą samą rzeczą, nazywacie to Nazwa metoda z zerowymi parametrami. Jeden wygląda jak wywołanie metody, a drugi wygląda jak atrybut, ale tak naprawdę to po prostu to samo. Oboje po prostu dzwonią Nazwa metoda. Podobnie każda nazwa metody, która kończy się znakiem równości (=), może być używana w przypisaniu. Twierdzenie person.name = "Alice" to naprawdę to samo co person.name = (alice), mimo że pomiędzy nazwą atrybutu a znakiem równości jest spacja, wciąż po prostu wywołujemy znak name = metoda.

Wdrażanie atrybutów

Możesz łatwo zaimplementować atrybuty samodzielnie. Definiując metody ustawiające i pobierające, możesz zaimplementować dowolny atrybut. Oto przykładowy kod implementujący Nazwa atrybut dla klasy osoby. Przechowuje nazwę w @Nazwa Zmienna instancji, ale nazwa nie musi być taka sama. Pamiętaj, że w tych metodach nie ma nic szczególnego.

 #! / usr / bin / env ruby ​​class Person def initialize (name) @name = name end def name @name end def name = (name) @name = name end def say_hello stawia "Witaj, # {@ nazwa}" koniec koniec 

Jedną rzeczą, którą zauważysz od razu, jest to, że jest to dużo pracy. Dużo pisania wystarczy powiedzieć, że chcesz mieć atrybut o nazwie Nazwa który uzyskuje dostęp do @Nazwa Zmienna instancji. Na szczęście Ruby oferuje pewne metody wygody, które definiują te metody dla ciebie.

Korzystanie z attr_reader, attr_writer i attr_accessor

Istnieją trzy metody w Moduł klasa, której możesz używać wewnątrz deklaracji klasy. Pamiętaj, że Ruby nie rozróżnia pomiędzy środowiskiem wykonawczym a "czasem kompilacji", a każdy kod wewnątrz deklaracji klasy może nie tylko definiować metody, ale także wywoływać metody. Dzwonić do attr_reader, attr_writer i attr_accessor metody z kolei definiują setery i gettery, które definiowaliśmy w poprzedniej sekcji.

The attr_reader Metoda działa tak, jak brzmi, jak to się stanie. Przyjmuje dowolną liczbę parametrów symbolu i dla każdego parametru definiuje metodę "getter", która zwraca zmienną instancji o tej samej nazwie. Tak, możemy zastąpić nasze Nazwa metoda z poprzedniego przykładu z attr_reader: name.

Podobnie, attr_writer metoda definiuje metodę "ustawiającą" dla każdego przekazywanego do niej symbolu. Należy zauważyć, że znak równości nie musi być częścią symbolu, a jedynie nazwa atrybutu. Możemy wymienić name = metoda z poprzedniego przykładu z wywołaniem attr_writier: name.

I zgodnie z oczekiwaniami attr_accessor wykonuje pracę obu attr_writer i attr_reader. Jeśli potrzebujesz zarówno narzędzia ustawiającego, jak i pobierającego dla atrybutu, zwykle nie należy wywoływać tych dwóch metod osobno, a zamiast tego wywoływać attr_accessor. Możemy wymienić obie Nazwa i name = metody z poprzedniego przykładu za jednym wywołaniem attr_accessor: name.

 #! / usr / bin / env ruby ​​def person attr_accessor: name def initialize (name) @name = name end def say_hello umieszcza "Hello, # {name}" koniec 

Dlaczego definiować Setters i Getters ręcznie?

Dlaczego warto definiować setery ręcznie? Dlaczego nie skorzystać z attr_ * metody za każdym razem? Ponieważ łamią hermetyzację. Hermetyzacja jest zasadą, która stwierdza, że ​​żadna zewnętrzna jednostka nie powinna mieć nieograniczonego dostępu do wewnętrznego stanu twoich obiektów. Wszystko powinno być dostępne za pomocą interfejsu, który uniemożliwia użytkownikowi uszkodzenie wewnętrznego stanu obiektu. Korzystając z powyższych metod, stworzyliśmy wielką dziurę w naszej ścianie enkapsulacji i pozwoliliśmy, aby wszystko zostało ustawione na nazwę, nawet oczywiście nieprawidłowe nazwy.

Jedną z rzeczy, które często widzisz, jest to attr_reader zostanie użyty do szybkiego zdefiniowania obiektu pobierającego, ale zdefiniowany zostanie niestandardowy obiekt ustawiający, ponieważ często chce on być w stanie wewnętrznym obiektu czytać bezpośrednio ze stanu wewnętrznego. Seter jest następnie definiowany ręcznie i sprawdza, czy ustawiona wartość ma sens. Albo, być może częściej, w ogóle nie definiuje się żadnego setera. Inne metody w funkcji klasy ustawiają zmienną instancji za geterem w inny sposób.

Możemy teraz dodać wiek i poprawnie wdrożyć Nazwa atrybut. The wiek atrybut można ustawić w metodzie konstruktora, odczytać za pomocą wiek getter, ale manipulowane tylko przy użyciu have_birthday metoda, która zwiększy wiek. The Nazwa atrybut ma normalny getter, ale ustawiający upewnia się, że nazwa jest pisana wielką literą i ma postać Imię Nazwisko.

 #! / usr / bin / env ruby ​​class Person def initialize (name, age) self.name = name @age = age end attr_reader: name,: age def name = (nowa_nazwa) if new_name = ~ / ^ [AZ] [ az] + [AZ] [az] + $ / @name = new_name else puts "'# {new_name}" nie jest prawidłową nazwą! " koniec końców def have_birthday umieszcza "Happy birthday # {@ name}!" @age + = 1 koniec def whoami stawia "Jesteś # {@ name}, wiek # {@ wiek}" koniec strony p = Person.new ("Alice Smith", 23) # Kim jestem? p.whoami # Wyszła za mąż p.name = "Alice Brown" # Próbowała zostać ekscentrycznym muzykiem p.name = "A" # Ale nie udało się # Ma trochę starszą p.have_birthday # Kim ja jestem? p.whoami