#Private attribute example
class C {
has $!w; #private attribute
multi method w { $!w } #getter method
multi method w ( $_ ) { #setter method
warn “Don’t go changing my w!”; #some side action
$!w = $_
}
}
my $c = C.new
$c.w( 42 )
say $c.w #prints 42
$c.w: 43
say $c.w #prints 43
#but not
$c.w = 44
Cannot modify an immutable Int (43)
jak dotąd, tak rozsądnie, a potem
#Public attribute example
class C {
has $.v is rw #public attribute with automatic accessors
}
my $c = C.new
$c.v = 42
say $c.v #prints 42
#but not
$c.v( 43 ) #or $c.v: 43
Too many positionals passed; expected 1 argument but got 2
Podoba mi się natychmiastowość przypisania „=”, ale potrzebuję łatwości łączenia działań bocznych zapewnianych przez wiele metod. Rozumiem, że są to dwa różne światy i że się nie mieszają.
ALE - nie rozumiem, dlaczego nie mogę po prostu przejść $ cv (43) Aby ustawić atrybut publiczny
- Wydaje mi się, że raku prowadzi mnie do nie mieszania tych dwóch trybów - niektórych atrybutów prywatnych i niektórych publicznych, i że presja jest skierowana na metodę metody (z niektórymi: cukier z okrężnicy) - czy taki jest zamysł projektu Raku?
- Czy coś brakuje?
is rw
jest określony. Zwrócenie serwera proxy nie spowoduje zmiany liczby dopuszczalnych parametrów na akcesorium.= foo
i.(foo)
ustawianie) i umożliwianie działań niepożądanych w obu przypadkach (ale nie tylko po pobraniuOdpowiedzi:
Można śmiało powiedzieć, że Raku nie jest całkowicie pozbawiony ludzi w tej dziedzinie. Twoje pytanie dotyczy dwóch tematów w projekcie Raku, które są warte krótkiej dyskusji.
Raku ma pierwszorzędne wartości l
Raku obficie wykorzystuje wartości l, które są pierwszorzędne. Kiedy piszemy:
Generowana metoda to:
is rw
Tutaj wskazuje, że metoda jest zwrócenie l-wartość - to znaczy, że coś może być przypisany. Kiedy więc piszemy:To nie jest cukier składniowy: tak naprawdę jest to wywołanie metody, a następnie do jego wyniku stosuje się operator przypisania. Działa to, ponieważ wywołanie metody zwraca
Scalar
kontener atrybutu, do którego można następnie przypisać. Można użyć wiązania, aby podzielić to na dwa kroki, aby przekonać się, że nie jest to trywialna transformacja składniowa. Na przykład:Byłoby przypisanie do atrybutu obiektu. Ten sam mechanizm kryje się za wieloma innymi funkcjami, w tym przypisywaniem list. Na przykład:
Polega na konstruowaniu
List
zawierające pojemniki$x
i$y
, a następnie operator przypisania w tym przypadku parami iteracje każdym boku do wykonania zadania. Oznacza to, że możemy tam użyćrw
akcesoriów do obiektów:I to wszystko po prostu działa naturalnie. Jest to również mechanizm przypisywania do wycinków tablic i skrótów.
Można również użyć
Proxy
do utworzenia kontenera o wartości l, w którym zachowanie odczytu i zapisu jest pod twoją kontrolą. W ten sposób możesz umieścić działania poboczneSTORE
. Jednak...Raku zachęca metody semantyczne do „seterów”
Kiedy opisujemy OO, często pojawiają się terminy takie jak „enkapsulacja” i „ukrywanie danych”. Kluczową ideą jest to, że model stanu wewnątrz obiektu - to znaczy sposób, w jaki wybiera on reprezentację danych potrzebnych do wdrożenia swoich zachowań (metod) - może ewoluować, na przykład w celu sprostania nowym wymaganiom. Im bardziej złożony obiekt, tym bardziej staje się on wyzwalający.
Jednak metody pobierające i ustawiające są metodami, które mają niejawne połączenie ze stanem. Chociaż możemy twierdzić, że osiągamy ukrywanie danych, ponieważ wywołujemy metodę, nie uzyskując bezpośredniego dostępu do stanu, z mojego doświadczenia wynika, że szybko kończymy w miejscu, w którym kod zewnętrzny wykonuje sekwencje wywołań ustawiających w celu osiągnięcia operacji - która jest forma anty-wzorca zazdrości funkcji. A jeśli robimy to , to całkiem pewne, będziemy skończyć z logicznego zewnątrz obiektu, który robi mieszankę getter i setter operacji osiągnąć operację. Naprawdę, operacje te powinny były zostać ujawnione jako metody o nazwach opisujących osiągane cele. Staje się to jeszcze ważniejsze, jeśli jesteśmy w równoległym otoczeniu; dobrze zaprojektowany obiekt jest często dość łatwy do ochrony na granicy metody.
To powiedziawszy, wiele zastosowań
class
jest naprawdę typami rekordów / produktów: istnieją po prostu zgrupować kilka elementów danych. To nie przypadek, że.
sigil nie tylko generuje akcesor, ale także:class Point { has $.x; has $.y; }
można utworzyć instancję jakoPoint.new(x => 1, y => 2)
), a także renderuje to w.raku
metodzie zrzutu..Capture
obiekt, co oznacza, że możemy go użyć do destrukcji (npsub translated(Point (:$x, :$y)) { ... }
.).Czego chciałbyś, gdybyś pisał w bardziej proceduralny lub funkcjonalny sposób i używał
class
jako środka do zdefiniowania typu rekordu.Projekt Raku nie jest zoptymalizowany do robienia sprytnych rzeczy w seterach, ponieważ uważa się to za kiepską rzecz do optymalizacji. Wykracza to poza to, co jest potrzebne do typu rekordu; w niektórych językach moglibyśmy argumentować, że chcemy dokonać weryfikacji tego, co zostało przypisane, ale w Raku możemy do tego użyć
subset
typów. Jednocześnie, jeśli naprawdę projektujemy OO, to chcemy interfejsu API znaczących zachowań, który ukrywa model stanu, zamiast myśleć w kategoriach pobierających / ustawiających, co zwykle prowadzi do niepowodzenia kolokacji dane i zachowanie, co zresztą w dużej mierze ma sens w OO.źródło
Proxy
(nawet jeśli zasugerowałem to ha). Jedyny raz, kiedy uznałem je za niezwykle przydatne, jest dla mnieLanguageTag
. Wewnętrznie$tag.region
zwraca obiekt typuRegion
(jak jest przechowywany wewnętrznie), ale w rzeczywistości jest to o wiele bardziej wygodne dla ludzi, aby powiedzieć$tag.region = "JP"
na$tag.region.code = "JP"
. A to naprawdę tylko tymczasowe, dopóki nie będę mógł wyrazić przymusu zStr
tego typu, np.has Region(Str) $.region is rw
(Który wymaga dwóch osobnych funkcji planowanych, ale o niskim priorytecie)Cóż, to naprawdę zależy od architekta. Ale tak na poważnie, nie, to po prostu nie jest standardowy sposób działania Raku.
Teraz byłoby całkowicie możliwe stworzenie
Attribute
cechy w przestrzeni modułów, coś w rodzajuis settable
, co stworzyłoby alternatywną metodę akcesorium, która zaakceptowałaby pojedynczą wartość w celu ustawienia wartości. Problem z robieniem tego w istocie polega na tym, że myślę, że na świecie istnieją zasadniczo 2 obozy dotyczące wartości zwrotu takiego mutatora: czy zwróciłoby nową wartość, czy starą wartość?Proszę o kontakt, jeśli jesteś zainteresowany wdrożeniem takiej cechy w przestrzeni modułów.
źródło
Podejrzewam, że właśnie się zdezorientowałeś. 1 Zanim dotknę tego, zacznijmy od tego, o czym się nie mylisz:
Państwo może zrobić wszystkie te rzeczy. To znaczy, że używasz
=
przypisania i wielu metod, i „po prostu idź$c.v( 43 )
”, wszystko w tym samym czasie, jeśli chcesz:Możliwe źródło zamieszania 1
Za kulisami
has $.foo is rw
generuje atrybut i pojedynczą metodę w następujący sposób:Powyższe nie jest jednak właściwe. Biorąc pod uwagę zachowanie, które obserwujemy, autogenerowana
foo
metoda kompilatora jest w jakiś sposób deklarowana w taki sposób, że każda nowa metoda o tej samej nazwie cicho ją zacienia . 2)Jeśli więc chcesz mieć jedną lub więcej niestandardowych metod o tej samej nazwie jako atrybut, musisz ręcznie zreplikować automatycznie wygenerowaną metodę, jeśli chcesz zachować zachowanie, za które normalnie byłaby odpowiedzialna.
Przypisy
1 Zobacz odpowiedź jnthna, aby uzyskać jasne, dokładne i autorytatywne rozliczenie opinii Raku na temat prywatnych / publicznych osób pobierających / ustawiających i tego, co robi za kulisami, kiedy ogłaszasz publiczne osoby pobierające / ustawiające (tj. Pisz
has $.foo
).2 Jeśli zostanie automatycznie zadeklarowana metoda akcesorium dla atrybutu
only
, to Raku, jak sądzę, rzuci wyjątek, jeśli zadeklarowana zostanie metoda o tej samej nazwie. Jeśli został zadeklarowanymulti
, nie powinien być przesłaniany, jeśli nowa metoda została zadeklarowanamulti
, i powinien zgłosić wyjątek, jeśli nie. Tak więc autogenerowane akcesorium jest zadeklarowane za pomocą anionly
animulti
zamiast, ale w jakiś sposób, który pozwala na ciche cieniowanie.źródło