らくでのプライベート属性とパブリック属性およびアクセサの混在


12
#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)

これまでのところ、合理的で、

#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

「=」割り当ての即時性が好きですが、マルチメソッドが提供するサイドアクションを簡単にまとめることが必要です。これらは2つの異なる世界であり、混ざらないことを理解しています。

しかし-私はなぜ$ cv(43)に行けないのか理解していませんpublic属性を設定するには

  1. 楽はこれらの2つのモードを混合しないように私を誘導しているように感じます-一部の属性はプライベートと一部の属性であり、プレッシャーはメソッドメソッドに向けられています(いくつかはコロンからの砂糖です)-これはラクのデザインの意図ですか?
  2. 何か不足していますか?

プロキシを使用できます
user0721090601

プロキシをどのように使用できますか?つまり、is rwが指定されている場合、アクセサーは既にコンテナーを返します。プロキシを返すことで、アクセサで許可されるパラメータの数が変更されることはありません。
Elizabeth Mattijsen

@ElizabethMattijsenたぶん私は質問を誤解した。しかし、これは、彼が(両方とも有効に欲しいもののために動作しているようです= fooし、.(foo)設定のための)と、両方のケースで行う必要が副作用を有効にする(ただし、場合にのみフェッチ):tio.run/...
user0721090601

回答:


13

これは楽のデザインの意図ですか?

楽がこの分野で完全に否定されているわけではないと言っても過言ではない。あなたの質問は、Rakuのデザインの2つのテーマに触れていますが、どちらも少し議論する価値があります。

楽はファーストクラスのl値を持っています

楽は、ファーストクラスのものであるl値を十分に活用します。私たちが書くとき:

has $.x is rw;

生成されるメソッドは次のとおりです。

method x() is rw { $!x }

is rwここでは、メソッドが戻っていることを示しているL値である、に割り当てることができる何かを- 。したがって、私たちが書くとき:

$obj.x = 42;

これは構文上の砂糖ではありません。実際にはメソッド呼び出しであり、その結果に代入演算子が適用されます。メソッド呼び出しがScalar属性のコンテナーを返すため、これを割り当てることができるので、これはうまくいきます。バインディングを使用してこれを2つのステップに分割し、それが簡単な構文変換ではないことを確認できます。たとえば、これ:

my $target := $obj.x;
$target = 42;

オブジェクト属性に割り当てます。この同じメカニズムは、リストの割り当てを含む他の多くの機能の背後にあります。たとえば、これ:

($x, $y) = "foo", "bar";

Listコンテナ$xとを含むを作成し、$y次にこの場合の代入演算子を使用して、両側をペアで反復して代入を実行します。つまり、rwそこでオブジェクトアクセサーを使用できます。

($obj.x, $obj.y) = "foo", "bar";

そして、それはすべて自然に機能します。これは、配列とハッシュのスライスへの割り当ての背後にあるメカニズムでもあります。

を使用Proxyして、読み取りと書き込みの動作を制御できるl値コンテナーを作成することもできます。したがって、サイドアクションをに入れることができますSTORE。しかしながら...

楽は「セッター」よりも意味論的方法を奨励する

OOについて説明すると、「カプセル化」や「データの非表示」などの用語がよく出てきます。ここでの重要なアイデアは、オブジェクト内の状態モデル(つまり、動作(メソッド)を実装するために必要なデータを表すために選択する方法)が、たとえば新しい要件を処理するために自由に進化できるということです。オブジェクトが複雑になるほど、これはより自由になります。

ただし、ゲッターとセッターは、状態と暗黙的に関連するメソッドです。状態に直接アクセスするのではなく、メソッドを呼び出しているためにデータの非表示を実現していると主張するかもしれませんが、私の経験では、外部コードがsetter呼び出しのシーケンスを行って操作を実行している場所にすぐに到達します。アンチパターンをうらやましい特徴の形。そして、それを実行している場合は、オブジェクトの外部でゲッター操作とセッター操作を組み合わせて操作を実行するロジックになってしまうことはほぼ確実です。実際、これらの操作は、達成されていることを説明する名前を持つメソッドとして公開されている必要があります。これは、並行設定の場合はさらに重要になります。適切に設計されたオブジェクトは、多くの場合、メソッドの境界で保護するのがかなり簡単です。

とは言っても、の多くの用途はclass、実際にはレコード/製品タイプです。これらは、一連のデータ項目を単にグループ化するために存在します。.シギルがアクセサーを生成するだけでなく、次のことも偶然ではありません。

  • 属性をデフォルトのオブジェクト初期化ロジックによって設定されるように選択し(つまり、をclass Point { has $.x; has $.y; }としてインスタンス化できますPoint.new(x => 1, y => 2))、.rakuダンプメソッドにそれをレンダリングします。
  • 属性をデフォルトの.Captureオブジェクトにオプトインします。つまり、構造化で使用できます(例:)sub translated(Point (:$x, :$y)) { ... }

これは、より手続き的または機能的なスタイルで作成classし、レコードタイプを定義する手段として使用する場合に必要なものです。

楽のデザインは、セッターで巧妙なことを行うために最適化されていません。これは、レコードタイプに必要なものを超えています。一部の言語では、割り当てられているものの検証を行いたいと主張することができますが、Rakuでは、そのためにsubset型を使用できます。同時に、実際にオブジェクト指向の設計を行っている場合は、コッターの失敗につながる傾向があるゲッター/セッターの観点から考えるのではなく、状態モデルを隠す意味のある動作のAPIが必要です。とにかくOOを行うポイントの多くは、データと動作です。


Proxys に警告するのは良い点です(私がhaを提案したとしても)。私がそれらがひどく役立つと思っLanguageTagたのは、私の場合だけです。内部的には、$tag.region型のオブジェクトを返しますRegion(それが内部的に保存されているもの)が、現実には、それは言うために人々のために無限に、より便利で、ある$tag.region = "JP"オーバー$tag.region.code = "JP"。そして、私がStrタイプで強制を表現できるようになるまで、それは本当に一時的なものです。たとえば、has Region(Str) $.region is rw(2つの別個の計画されているが優先度の低い機能が必要です)
user0721090601

時間をかけて設計の根拠を詳しく説明してくれてありがとう@Jonathan-私はある種の油と水の側面を疑っていました、私のような非CSボディでは、隠された状態の適切なOOとクラスesのアプリケーションの違いを本当に理解していますC ++で博士号を取得する難解なデータホルダーを構築するためのよりフレンドリーな方法。そして、user072に... Proxyについてのあなたの考えのために。以前にProxyについて知っていましたが、それら(および/または特性)は油と水の混合を防ぐために意図的に非常に面倒な構文であると疑っていました...
p6steve

p6steve:Rakuは、最も一般的で簡単なことを非常に簡単に行えるように設計されています。一般的なモデルから逸脱しても、それはいつでも可能です(これまでにやりたいことを実行するための3つの異なる方法を見てきたと思います...そして、それ以外にも確実にあります)。あなたがやっていることは本当にあなたが望んでいることを確認してください。しかし、トレイト、プロキシ、スラングなどを使用して、必要なときにいくつかのクールなものを実際に有効にするために追加の文字を数個追加するだけで作成できます。
user0721090601

7

しかし-私はなぜ$ cv(43)に行けないのか理解していませんpublic属性を設定するには

まあ、それは本当に建築家次第です。しかし、真剣に、いや、それは単に楽が動作する標準的な方法ではありません。

今、作成する完全に可能だろうAttributeモジュール空間、のようなもので特色is settable別のアクセサメソッド作成し、値を設定するための単一の値を受け入れます。コアでこれを行うことの問題は、そのようなミューテーターの戻り値について、世界には基本的に2つのキャンプがあると思います:新しい値を返すか、古い値を返すか?

このような特性をモジュール空間に実装することに興味がある場合は、私に連絡してください。


1
@Elizabethに感謝します-これは興味深い角度です-私はあちこちでこの質問をしているだけで、トリック(または私の側のスキル)を実行するための特性を構築するのに十分な見返りがありません。私は本当にコーディングのベストプラクティスに頭を動かし、それに合わせようとしていました。そしてRakuのデザインがベストプラクティスに合わせられることを望んでいました。
p6steve

6

私は現在、あなたが混乱したばかりだと思います。1それに触れる前に、混乱していないことから始めましょう。

=割り当ての即時性が好きですが、複数のメソッドが提供するサイドアクションを簡単にまとめることが必要です。...なぜ行けないのか分からない$c.v( 43 )公開属性を設定するには

これらすべてを行うことができます。つまり=、割り当てと複数のメソッドを使用$c.v( 43 )し、必要に応じて「ただ実行」するだけです。

class C {
  has $!v;
  multi method v                is rw {                  $!v }
  multi method v ( :$trace! )   is rw { say 'trace';     $!v }
  multi method v ( $new-value )       { say 'new-value'; $!v = $new-value }
}
my $c = C.new;
$c.v = 41;
say $c.v;            # 41
$c.v(:trace) = 42;   # trace
say $c.v;            # 42
$c.v(43);            # new-value
say $c.v;            # 43

考えられる混乱の原因1

舞台裏で、次のhas $.foo is rwように属性と単一のメソッドを生成します。

has $!foo;
method foo () is rw { $!foo }

上記は正しくありません。私たちがしているシーイングの行動を考えると、コンパイラの自動生成foo方法は、何らかの形で、このような方法で宣言されている任意の新しい同じ名前無言の方法も。2

したがって、属性と同じ名前の1つ以上のカスタムメソッドが必要な場合、通常はその動作を維持したい場合は、自動生成されたメソッドを手動で複製する必要あります。

脚注

1プライベートゲッター/セッターとパブリックゲッター/セッターについての楽の意見、およびパブリックゲッター/セッターを宣言するときの裏での動作(つまり、を書くhas $.foo)についての明確で完全な権威ある説明については、jnthnの回答を参照してください。

2属性の自動生成されたアクセサーメソッドが宣言されているonly場合、同じ名前のメソッドが宣言されていると、Rakuは例外をスローします。宣言されているmulti場合、新しいメソッドも宣言されている場合はシャドウしないでください。宣言されてmultiいない場合は、例外をスローします。自動生成されたアクセサは、どちらを使用して宣言されているのでonlymultiなく、代わりにサイレントシャドウイングを可能にするいくつかの方法で。


Aha-@raiphに感謝-それは私が欠けていると感じたものです。今では理にかなっています。Jnthnによると、私はおそらく、より優れた真のOOコーダーになり、純粋なデータコンテナーのセッタースタイルを維持しようとします。しかし、これがツールボックスにあることを知っておくのは良いことです!
p6steve
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.