トップ回答者
データクラスのカプセル化について

質問
-
テーブルのレコードを扱うクラス(以下アクセスクラス)にて、取得したレコード情報をクラス(以下データクラス)として保持しています。
データクラスのメンバの一部を読み取り専用で外部に公開しようとしています。
現状、データクラスの各メンバ(パブリックなGet/Setプロパティ)をパブリックなGetのみのプロパティとして、アクセスクラスに実装しています。
ただ、データクラスのメンバ数が多いので、コードが冗長になっています。
アクセスクラスのデータクラスインスタンスをGetプロパティとして公開しても、データクラスのプロパティはGet/Setであるため、読み取り専用の条件を満たしません。
この場合、
1.現状のようにデータクラスの各メンバをGetのみで公開する。
2.データクラスのディープコピーのインスタンスをGetのみで公開する。
3.Getのみのプロパティを持つデータクラスを作成し、そのインスタンスをGetのみで公開する。
以外の手法は無いでしょうか?ご存知の方がいらっしゃれば、知恵をいただきたいのですが。
データクラスのインスタンスを公開するが、そのメンバに関してはアクセス制限をかけるような手法を模索しています。上記の各手法は、次の理由から実装に消極的です。
1.コードが冗長になる。(使用する側も冗長となる)
2.プロパティにアクセスするたびに新しいインスタンスが作成され、効率が悪い。
3.データクラス自体は他のクラスでも使用するため、Get/Setプロパティで統一したい。VisualStudio 2005
Framework 2.0
C#
回答
-
データクラスにGetプロパティだけ持たすという発想ではなく、Get/Setプロパティは実装するのだけれど、Setプロパティが働かないようにする方法とかかかなぁ? 例えば、データクラスにSetプロパティEnabledみたいなプロパティを持たせて、そこがfalseであればSet時に例外を返すようなイメージです。
あとはインターフェース経由でGetのみを公開するようにするとか、アクセスクラスでデータクラスをラップしてGetのみを公開するオブジェクトを公開するとか思いつきますが、コード的にはやっぱり冗長になるんでダメですね・・・(自己完結)
# こういう発想は依存プロパティ(.NET Framework 3.0以降)へとつながっていくんでしょうが、Get/Setを単純に切り替えたいという場合は、依存プロパティでも冗長度は増しそうですね。
すべての返信
-
データクラスにGetプロパティだけ持たすという発想ではなく、Get/Setプロパティは実装するのだけれど、Setプロパティが働かないようにする方法とかかかなぁ? 例えば、データクラスにSetプロパティEnabledみたいなプロパティを持たせて、そこがfalseであればSet時に例外を返すようなイメージです。
あとはインターフェース経由でGetのみを公開するようにするとか、アクセスクラスでデータクラスをラップしてGetのみを公開するオブジェクトを公開するとか思いつきますが、コード的にはやっぱり冗長になるんでダメですね・・・(自己完結)
# こういう発想は依存プロパティ(.NET Framework 3.0以降)へとつながっていくんでしょうが、Get/Setを単純に切り替えたいという場合は、依存プロパティでも冗長度は増しそうですね。
-
よこけんさん、trapemiyaさんレスありがとうございます。
>プロパティのセッターのみ可視性を internal にするといったことができます。データクラスのメンバに関してでしょうか?
>3.データクラス自体は他のクラスでも使用するため、Get/Setプロパティで統一したい。
とありますように、他のアセンブリにてデータクラスを使用する(Setできる必要がある)のでinternalは使用できないと考えています。
>データクラスにGetプロパティだけ持たすという発想ではなく、Get/Setプロパティは実装するのだけれど、Setプロパティが働かないようにする方法とかかかなぁ?
その通りです。データクラスのインスタンスおよび、そのメンバまでをうまくカプセル化(アクセス制御)したいわけです。
うーん。いま、読み返すと分かりにくい文章だな(w
>例えば、データクラスにSetプロパティEnabledみたいなプロパティを持たせて、そこがfalseであればSet時に例外を返すようなイメージです。
この手法が一番近そうですね。SetプロパティEnabledプロパティのアクセス制限を実装すればよさそうですね。 -
サイ さんからの引用 この手法が一番近そうですね。SetプロパティEnabledプロパティのアクセス制限を実装すればよさそうですね。
そのSetプロパティのEnabledプロパティを外部から変更される恐れはありませんか?
これのパターンならinternalでいけるのであれば、問題にならないかもしれません。
.NET Framework周りでありえそうな実装としては、SetReadOnlyメソッドでそのインスタンスを読み取り専用としてフリーズしてしまい、二度と書き換えられないインスタンスに仕立て上げてから返すみたいなことかなぁ。
「Getでインスタンスのコピーが走るのが嫌」ということであれば、「Set時に読み取り専用のコピーを作る」とかいうことになってしまいますが。
#GetよりはSetされる回数の方が少ない場合は効果があるか?
-
レスが遅くなりましたが...
trapemiyaさん、Azuleanさんレスありがとうございます。
>ほんとだ(^^; 言われてみればそうですね。この路線で行くのであれば、コンストラクタの引数で制御してしまいましょうか?
私も、コンストラクタの引数で制御しようと考えていました。
>.NET Framework周りでありえそうな実装としては、SetReadOnlyメソッドでそのインスタンスを読み取り専用としてフリーズしてしまい、二度と書き換えられないインスタンスに仕立て上げてから返すみたいなことかなぁ。
実装的には、Azuleanさんの意見と同じなのでしょうか?
フリーズさせて、書き換え不可のインスタンスに仕立てるという部分(の実装)が理解できませんでした。
まずはAzuleanさんの手法(Set時に例外を返すON/OFFのプロパティを設ける and コンストラクタでそのプロパティを制御)で実装したいと考えております。
今回のパターンの場合は、公開するインスタンスのみ読み取り専用となればよいので、コンストラクタ制御で問題ないかと考えております。(ディープコピーや別インスタンスに関しては問題としない) -
サイ さんからの引用 実装的には、Azuleanさんの意見と同じなのでしょうか?
フリーズさせて、書き換え不可のインスタンスに仕立てるという部分(の実装)が理解できませんでした。
そのインスタンスを未来永劫、setできないようにすることを「フリーズさせる」と言いました。
「コンストラクタで読み取り専用として作成する」でも同じ効果を得られます。
現状は、コンストラクタから指定してずっと読み取り専用のままにするか、途中から指定してずっと読み取り専用のままにして、解除できないようにするプランですね。
途中で読み取り専用から元に戻せるような仕組みは辞めておくべきです。
その読み取り専用のON/OFFができるプロパティが外からもアクセスできると意味がないのが理由です。
-
>途中で読み取り専用から元に戻せるような仕組みは辞めておくべきです。
対象のインスタンスに関してはインスタンス作成時に「フリーズ」させるつもりです。
理由に関しても理解しているつもりです。アドバイスありがとうございます。
現状、データクラスとアクセスクラスが異なるアセンブリとなっているので、
ON/OFFプロパティを制御するにはコンストラクタしかないかと考えております。
(同一アセンブリ内ならinternalで実装も可能そうですが...)
(private SetReadOnlyメソッドを実装し、アクセスクラスよりリフレクションでキックするか...)
なかなか、すんなりとはいきませんが実際に実装しながら策を練っていきたいと思います。
ご教授ありがとうございました。