C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: 親子関係とメメントの汎化において、より厳密に型指定したい


(過去ログ 55 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■30579 / inTopicNo.1)  親子関係とメメントの汎化において、より厳密に型指定したい
  
□投稿者/ うたひこ (3回)-(2008/12/26(Fri) 22:03:12)

分類:[VB.NET/VB2005 以降] 

こんにちは。

質問するのは初めてです。
是非皆様にご一緒にお考え頂きたく思いますので、
宜しくお願いいたします。



親子関係にあるクラスと、その親子関係をメメントするクラスが
多くあるプロジェクトについて
リファクタリングをしています。

この親子関係についてのコードの重複を避けようとすると、
どうしても「親から見た子」「子から見た親」について、
型の指定が抽象的になってきます。

質問は、
「今のコードよりももう少し厳密な型の指定ができそうな気がする
(そう思う根拠は曖昧です)し、そうしたいのだけれど、
どこまで厳密にできるかがわからないので、方法や妥協点をお教えください」
ということです。



その把握が困難なのは、
継承と包含の関係性がとても複雑な構造があるためです。

現在行っているのは、
http://www.adobe.com/devnet/illustrator/scripting/
ここにあるAdobe Illustrator のスクリプティングリファレンスを参考にした、
Adobe Illustratorの可能な限りの模倣です。
(最近知った言葉では、リバースエンジニアリングと言うらしいです。
 法的にどうかという問題は、個人の趣味なので気にしていません)

複雑な構造は、この中の
「Document」
「Layer」
「PageItem」
「PathItem」
「CompoundPathItem」
「GroupItem」
についてです。



まず、この包含関係ですが、
Document         => Layer
Layer            => Layer,PageItem,GroupItem,PathItem,CompoundPathItem
GroupItem        => PageItem,PathItem,CompoundPathItem
CompoundPathItem => PathItem

といった感じに、左のものは右のものを包含しています。

PageItemは抽象クラスで、
GroupItem,PathItem,CompoundPathItemはその派生クラスだろうと
考えています。



このアプリケーションがC++で作られていることから、
VB使いの僕にはこの構造がどのように実現されているか
多重継承などのVBにない機能がわからないために考慮しきれませんが、
自分なりにVBでの実現するための継承関係として、以下のように考えました。

(ApplicationItem)★
    Document
    (LayerItem)★
        Layer
        PageItem★
            GroupItem
            CompoundPathItem
            PathItem

(インデントがスーパーとサブの関係を表します)
(括弧で括られたものはリファレンスにはない)
(★は抽象クラス)



次は自分のやりたいことです。

まず、以下のようにコレクションを持たせたいです。
Document        =>Layer
Layer           =>LayerItem
GroupItem       =>PageItem
CompoundPathItem=>PathItem
(左のオブジェクトは右のオブジェクトのコレクションをメンバに持つ)



親子関係は、できれば以下のような感じに
ApplicationItem内に一本化させたいです。
理由は、親子関係とメメントを汎化させたい構造が
今挙げた以外にもたくさんあり、使いまわしたいからです。
(コードはイメージです。実際はもっと長ったらしいです)

MustInherit Class ApplicationItem

    Private myParent As 親コレクションの型

    Public ReadOnly Property Parent As myParentの親の型
        'myParentの親を返したい
    End Property

    Protected Sub New(ByVal Parent As 親コレクションの型)
         Me.myParent = Parent
    End Sub

    Public Class ApplicationItemCollection(Of C As ApplicationItem)
        Inherits CollectionBase

        Private myParent As 親の型

        Public Sub New(Parent As 親の型)
             Me.myParent = Parent
        End Sub 

        Public Class MementOfParent
             Private myParent As 親コレクションの型
             Private myIndex As integer
             Private myTarget As ApplicationItem
        End Class
End Class    


ApplicationItemのコンストラクタは、こんなイメージで呼び出されます。

Class GroupItem
    Inherits PageItem
    
    Public Class GroupItemWrappedCollection

        Private myCollection As ApplicationItemCollection(Of PageItem)
        Public Sub New(Collection As ApplicationItemCollection(Of PageItem)
            Me.myCollection = Collection
        End Sub
        Public Function Add()As GroupItem
            Dim Result As New GroupItem(myCollection)
            myCollection.Add(Result)
            Return Result
        End Function
    End Class
End Class
            


しかし、現状は以下のようなザマです。

MustInherit Class ApplicationItem

    Private myParent As ApplicationItemCollection

    Public ReadOnly Property Parent As Object
        Get
            Return myParent.Parent
        End Get
    End Property

    Protected Sub New(ByVal Parent As ApplicationItemCollection)
         Me.myParent = Parent
    End Sub

    Public Class ApplicationItemCollection
        Inherits CollectionBase

        Private myParent As Object

        Public ReadOnly Property Parent As Object
            Get
                Return myParent
            End Get
        End Property

        Public Sub New(Parent As Object)
             If Parent Is Nothing Then Throw New Exception("ダメ")
             Me.myParent = Parent
        End Sub 

        Public Class MementOfParent
             Private myParent As ApplicationItemCollection
             Private myIndex As integer
             Private myTarget As ApplicationItem
        End Class
End Class  

Class GroupItem
    Inherits PageItem
    
    Public Class GroupItemWrappedCollection

        Private myCollection As ApplicationItemCollection
        Public Sub New(Collection As ApplicationItemCollection)
            Me.myCollection = Collection
        End Sub
        Public Function Add()As GroupItem
            Dim Result As New GroupItem(myCollection)
            myCollection.Add(Result)
            Return Result
        End Function
    End Class
End Class

そして、以下のようにコレクションを持っています

Document        =>ApplicationItemCollection
Layer           =>ApplicationItemCollection
GroupItem       =>ApplicationItemCollection
CompoundPathItem=>ApplicationItemCollection

これらのコレクションをメンバに持つオブジェクトは、
コレクションを外部に直接公開するのでなく
GroupItemWrappedCollectionのように、
ApplicationItemCollectionをラップして公開するので、
コレクションにオブジェクトモデルに即さない型のオブジェクトが
入ることはないとは思うのですが、
現状の抽象的な型指定が腑に落ちません。



なんとかできそうでしょうか?
宜しくお願いいたします。
(返事は遅いので、あらかじめご容赦ください。)

引用返信 編集キー/
■30583 / inTopicNo.2)  Re[1]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ たくボン (128回)-(2008/12/27(Sat) 01:13:08)
2008/12/27(Sat) 01:16:05 編集(投稿者)

No30579 (うたひこ さん) に返信

まず、何点か確認させてください。

> 親子関係にあるクラスと、その親子関係をメメントするクラスが

ここで言われているメメントとは、デザインパターンのmementoで良いでしょうか?

イラレのScriptは請った使い方をしたことがないので、ざっくりとしか読んでないですが、
http://www.adobe.com/devnet/illustrator/pdfs/illustrator_scripting_guide.pdf
の16ページにあるObject Modelを、memenntoを前提とした構造にリファクタリングしたいということで合っていますでしょうか?

mementoの実装を汎用化させたいために汎化するなら、内部的な抽象表現もある程度仕方ないかもしれません。

各オブジェクトに実装するコレクションクラスの公開方法(wrappedでも継承したコレクションクラスでも)で制限をかけるか、mementoに受け渡す型を緩くするかどちらかなのかも。

#上記は、メメント=mementoと解釈して書いたので、的外れだったらすいません。
引用返信 編集キー/
■30753 / inTopicNo.3)  Re[2]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ うたひこ (4回)-(2009/01/05(Mon) 20:21:51)
あけましておめでとうございます。
お返事が大幅に遅れてしまいましてすみません。


> ここで言われているメメントとは、デザインパターンのmementoで良いでしょうか?

> http://www.adobe.com/devnet/illustrator/pdfs/illustrator_scripting_guide.pdf
> の16ページにあるObject Modelを、memenntoを前提とした構造にリファクタリングしたいということで合っていますでしょうか?

合っています。おっしゃる通りです。
「メメント」で共通認識を得られるかのように錯覚していたため、
説明をしていませんでした。

「メメント=memento=子から見た親の状態を記憶、復元するためのオブジェクト」です。



> mementoの実装を汎用化させたいために汎化するなら、内部的な抽象表現もある程度仕方ないかもしれません。
> 各オブジェクトに実装するコレクションクラスの公開方法(wrappedでも継承したコレクションクラスでも)で制限をかけるか、mementoに受け渡す型を緩くするかどちらかなのかも。

そうですか。
でしたら仕方ありませんね。
お教え下さった路線に的を絞りたいと思います。

ところで、たくボンさんのおっしゃい方から、
このパターンに関して、こういった問題が起こりうる可能性を
経験則や知識など、何らかの形で
ご存知であったかのような印象を僕は受けたのですが、
もしこういったデザインパターン実装上の問題の解決方法についての
webページや書籍などをご存知であればご紹介いただきたいです。

スタンダードな手法が確立されているとすれば、
なるべくその手法に即したコードが書きたいです。

(このスレの質問自体は解決していただいたつもりなので解決済みにしておきますが、
 もし上記をご存知でしたらその後でもよろしくお願いします。)



> #上記は、メメント=mementoと解釈して書いたので、的外れだったらすいません。

いえ、的を射てらっしゃいます。



> イラレのScriptは請った使い方をしたことがないので、

蛇足ですが、こった使い方をしようとすると、
こりたい部分がスクリプトでいじれないことに気付きます。
(それが現在の開発の一つの動機でもあります。)

おそらくは、実現に外部プラグインが関わっている機能は
スクリプトでいじれない仕様になってるんじゃないかと思います。
どれだけバージョンアップしてもこれは変わらないんじゃないかとも思います。
解決済み
引用返信 編集キー/
■30793 / inTopicNo.4)  Re[3]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ たくボン (133回)-(2009/01/06(Tue) 22:55:05)
No30753 (うたひこ さん) に返信
> お返事が大幅に遅れてしまいましてすみません。

こちらも返事が遅くなってしまい、申し訳ありません。
昨日の深夜に気がついたのですが、年末からタイトなプロジェクトがスタートしてしまったので、なかなかわんくまに足を運べないでいます。。。

> 「メメント=memento=子から見た親の状態を記憶、復元するためのオブジェクト」です。

> ところで、たくボンさんのおっしゃい方から、
> このパターンに関して、こういった問題が起こりうる可能性を
> 経験則や知識など、何らかの形で
> ご存知であったかのような印象を僕は受けたのですが、
> もしこういったデザインパターン実装上の問題の解決方法についての
> webページや書籍などをご存知であればご紹介いただきたいです。

残念ながら、mementoを使った本格的な開発はまだ行っておりません。
しかし、mementoもあるオブジェクトに対する制約を設けるという視点から見れば、Interfaceを使用するのがいいかもしれません。

VB.NETは多重継承ができないため、クラスに継承関係を持たせると制約の部分についてはどうしても最大公約数的なクラスを基底にする必要があり、イラレのような多くのオブジェクトを扱う場合はどうしてもオブジェクトが肥大しがちになると思います。

そこで、振る舞い(制約)の部分をInterfaceで定義することにより、データと振る舞いを柔軟に分離することが可能になると思います。

例えばですが、

> 「メメント=memento=子から見た親の状態を記憶、復元するためのオブジェクト」です。

このような制約を、
IStateRestorable(状態復元可能)
というようなInterfaceで定義しておき、実装するクラスのメンバで状態として保存しておきたいメンバにカスタム属性を用意して制御側で保存、復元を行うのはどうかな?

参考資料のPDFを見てもわかるように、イラレのDOMも基底にはCompositeの制約があると思いますし、まずはComposite制約を実装する各クラスを作成し、各クラスにはIStateRestorableを実装。
mementoの機能を提供するOriginatorとMementoは、IStateRestorableを介してオブジェクトを受け取り、カスタム属性を辿って状態の保存・復元を行えばいいのかな。



カスタム属性を使わないもう一つの方法としては、複数のOriginatorとMemento(これは各クラスに厳密に対応したmemento)を用意して、CareTaker内部で振り分けを行う方法かな。

CareTaker内部でComposite制約に従って、再帰処理。再帰関数の中で振り分け。
これも受け渡すオブジェクトは、ルートオブジェクトになると思うのでIStateRestorableみたいなインターフェースと基底になるComposite制約を実装するクラスは必要になると思うけど、見た目にはすっきりなると思います。


> スタンダードな手法が確立されているとすれば、
> なるべくその手法に即したコードが書きたいです。

これは俺に聞かないでください(笑)
20歳からずっと一人でやってきていたので、ほとんどが独学なんです(今はしがないサラリーマンですけど)
デザインパターンだけでも実装できると思うけど、最近の流行りならDIやAOPと言う手法もあるかな。


> おそらくは、実現に外部プラグインが関わっている機能は
> スクリプトでいじれない仕様になってるんじゃないかと思います。
> どれだけバージョンアップしてもこれは変わらないんじゃないかとも思います。

うーん、プラグインも細かい部分までInterfaceを定義・公開して実装すれば制御はできると思うけど、イラレのような不特定多数のプラグインを前提としたInterfaceならユルユルの制約しかかけれないと思うし難しいところですね。

とりあえず、この件については俺も解決済みにチェックしておきます。
急いで書いてたので分かりにくかったらすいません。

解決済み
引用返信 編集キー/
■30916 / inTopicNo.5)  Re[4]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ うたひこ (5回)-(2009/01/08(Thu) 21:50:02)
2009/01/08(Thu) 21:53:43 編集(投稿者)

No30793 (たくボン さん) に返信

お忙しい中、お返事ありがとうございます。

> こちらも返事が遅くなってしまい、申し訳ありません。
教えを乞う身としてはお返事いただけるだけでありがたいですし、
僕自身がノロマなので、早いレスをいただくと逆にプレッシャーです(^^;



まず、お教え下さった案について、わからない部分があるので、
質問させて下さい。

> IStateRestorable(状態復元可能)
> というようなInterfaceで定義しておき、実装するクラスのメンバで状態として保存しておきたいメンバにカスタム属性を用意して制御側で保存、復元を行うのはどうかな?
>
> 参考資料のPDFを見てもわかるように、イラレのDOMも基底にはCompositeの制約があると思いますし、まずはComposite制約を実装する各クラスを作成し、各クラスにはIStateRestorableを実装。
> mementoの機能を提供するOriginatorとMementoは、IStateRestorableを介してオブジェクトを受け取り、カスタム属性を辿って状態の保存・復元を行えばいいのかな。

このスレを立てる前に色々と(ジェネリックなインターフェイスやらデリゲートやら)実験してみたのですが、
カスタム属性というのは全く考えていませんでした。

この案についてわからないのは

>実装するクラスのメンバで状態として保存しておきたいメンバにカスタム属性を用意
「カスタム属性を用意する」というのはどういうことでしょう?
「保存しておきたいメンバごとに対応するカスタム属性を定義して、
メンバの宣言箇所にてそのカスタム属性を適用する」みたいなことでしょうか??



> カスタム属性を使わないもう一つの方法としては、複数のOriginatorとMemento(これは各クラスに厳密に対応したmemento)を用意して、CareTaker内部で振り分けを行う方法かな。
>
> CareTaker内部でComposite制約に従って、再帰処理。再帰関数の中で振り分け。
> これも受け渡すオブジェクトは、ルートオブジェクトになると思うのでIStateRestorableみたいなインターフェースと基底になるComposite制約を実装するクラスは必要になると思うけど、見た目にはすっきりなると思います。

この案については、かなり困惑してるのですが、
 ○「振り分け」というのがどういった意味合いで用いられているのかがわからないのと、
  それに伴って「何を振り分けるのか」が読み取れませんでした。
 ○>これも受け渡すオブジェクトは
   何が何を受け渡すのでしょうか?
 ○「再帰」という言葉はCompositeに関連した以下のような状態のことだと思って大丈夫ですか?

Interface IDrawable
Sub Draw(ByVal g As Graphics)
End Interface

Class Layer
Implements IDrawable

Private myDrawItems As List(Of IDrawable)

'再帰関数
Sub Draw(ByVal g As Graphics) Implements (省略)
For Each Item As IDrawable In Me.myDrawItems
Item.Draw(g) '再帰処理
Next
End Sub

End Class

> 20歳からずっと一人でやってきていたので、ほとんどが独学なんです(今はしがないサラリーマンですけど)
ここらへんも個人的にかなり興味深いですね。野心がチラチラしてますね。
差し障りなければお伺いしたいところです。

> DIやAOPと言う手法もあるかな。
これは全く存じませんでした。
このワザも後ほど習得しようと思います。



というか、こんなにまでお話にお付き合い下さる方がいらっしゃるとは思っていませんでした。
どうもありがとうございます。

実は、レスがつかない、解決できないという自身にとっての最悪な状況を避けるために、
スレを立てる時点であえて割愛した、かなりややこしい「こうしたい」という仕様がありまして、
今後どこまでお話にお付き合い下さるかはわかりませんが、
この先お話を続けていただけるのであれば、その情報は不可欠になってくると思いますので、
関係ありそうな部分は先にお話しておきます。

○オブジェクトモデルのみならず、参考資料のPDFを仕様書として考え、可能な限り模倣したい。
○模倣に際し、以下のようなややこしさがある
  ○親子関係を持つオブジェクトの、子から見た親は、外部にはReadOnlyとして公開される
  ○コレクションクラスのAddメソッドが、
   その子となるクラスのFactoryMethodとしての機能を有する。
   子となるクラスのコンストラクタは公開されていない。
  ★親子関係を変更する手段は公開されていない
   (例えば、PathItemインスタンスのPがあり、その親をAからBに変更したい場合は、
    AからPを削除し、
    BのPathItemコレクションのAddを呼び出して作成した新たなPathItemインスタンスを
    Pと同じプロパティを持つように変更しなければならない。)

  ○上記★の仕様を真似するのは馬鹿げていると感じるので、
   コレクションクラスにAdd(オーバーロード)を新たに追加して、親を変更できるようにしている。
  ○VBでスクリプトを書くためにCOM参照設定して、参考資料にあるオブジェクトを
   オブジェクトブラウザやインテリセンスで見た時、全部がインターフェイス扱いになっていたので、
   それも真似しようと全てのクラスをFriend扱いにしようとしたけど、
   これはVBとC++のクッションとしてそうなってるだけだと思ったのでやっぱりやめた。

といった感じです。
これらを含めてスレを立てていたら、
何が質問かわからなくなりそうな上に収拾がつかなくなりそうだったので伏せていました。
解決済み
引用返信 編集キー/
■31071 / inTopicNo.6)  Re[5]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ たくボン (137回)-(2009/01/15(Thu) 00:52:18)
No30916 (うたひこ さん) に返信
> 2009/01/08(Thu) 21:53:43 編集(投稿者)

返信が遅れてしまい申し訳ありません。
最近はプロジェクト管理やら仕様書作成でなかなかまとまった時間が取れないので遅くなりました。


>>IStateRestorable(状態復元可能)
>>というようなInterfaceで定義しておき、実装するクラスのメンバで状態として保存しておきたいメンバにカスタム属性を用意して制御側で保存、復元を行うのはどうかな?
>>
>>参考資料のPDFを見てもわかるように、イラレのDOMも基底にはCompositeの制約があると思いますし、まずはComposite制約を実装する各クラスを作成し、各クラスにはIStateRestorableを実装。
>>mementoの機能を提供するOriginatorとMementoは、IStateRestorableを介してオブジェクトを受け取り、カスタム属性を辿って状態の保存・復元を行えばいいのかな。
>
> このスレを立てる前に色々と(ジェネリックなインターフェイスやらデリゲートやら)実験してみたのですが、
> カスタム属性というのは全く考えていませんでした。
>
> この案についてわからないのは
>
> >実装するクラスのメンバで状態として保存しておきたいメンバにカスタム属性を用意
> 「カスタム属性を用意する」というのはどういうことでしょう?
> 「保存しておきたいメンバごとに対応するカスタム属性を定義して、
> メンバの宣言箇所にてそのカスタム属性を適用する」みたいなことでしょうか??

そうです。保存を行いたいメンバに独自で作成したSerializableAttributeみたい属性を属性を与え、memento側でリフレクションを使って保存する/しないを判断すればクラスに依存しない柔軟な制約を与えることが可能です。

>
>>カスタム属性を使わないもう一つの方法としては、複数のOriginatorとMemento(これは各クラスに厳密に対応したmemento)を用意して、CareTaker内部で振り分けを行う方法かな。
>>
>>CareTaker内部でComposite制約に従って、再帰処理。再帰関数の中で振り分け。
>>これも受け渡すオブジェクトは、ルートオブジェクトになると思うのでIStateRestorableみたいなインターフェースと基底になるComposite制約を実装するクラスは必要になると思うけど、見た目にはすっきりなると思います。
>
> この案については、かなり困惑してるのですが、
>  ○「振り分け」というのがどういった意味合いで用いられているのかがわからないのと、
>   それに伴って「何を振り分けるのか」が読み取れませんでした。
>  ○>これも受け渡すオブジェクトは
>    何が何を受け渡すのでしょうか?
>  ○「再帰」という言葉はCompositeに関連した以下のような状態のことだと思って大丈夫ですか?

説明不足ですいません。「振り分け」という意味は、上記で記述したカスタム属性や保存対象かどうかを判定し、保存対象の場合は型で対応するmementoクラスにキャストして投げていくと言う意味です。

「受け渡すオブジェクト」とは、CareTakerに渡すオブジェクトのことです。CarTaker内部で再帰処理を書いていくことになると思うので、再帰処理の開始位置として必要なルートオブジェクトです。



>>20歳からずっと一人でやってきていたので、ほとんどが独学なんです(今はしがないサラリーマンですけど)
> ここらへんも個人的にかなり興味深いですね。野心がチラチラしてますね。
> 差し障りなければお伺いしたいところです。

すいません。今はあまり時間がないのでまた今度と言うことでお願いします(笑)


> 実は、レスがつかない、解決できないという自身にとっての最悪な状況を避けるために、
> スレを立てる時点であえて割愛した、かなりややこしい「こうしたい」という仕様がありまして、
> 今後どこまでお話にお付き合い下さるかはわかりませんが、
> この先お話を続けていただけるのであれば、その情報は不可欠になってくると思いますので、
> 関係ありそうな部分は先にお話しておきます。

ここも一通り目を通していますが、じっくり考えないといけないと思うので少し時間をください。(とりあえず今は睡眠時間が欲しいので・・・orz)

解決済みのチェックを外しておけば、俺以外の色々な人の意見も聞けると思いますので、チェックを外しておきます。
殴り書きのようなレスですいませんm(._.)m
引用返信 編集キー/
■31275 / inTopicNo.7)  Re[6]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ うたひこ (7回)-(2009/01/19(Mon) 20:03:42)
No31071 (たくボン さん) に返信

すみません、仕事の都合でお返事差し上げるのにまとまった時間がなかなかとれません。

後ほどいただいたレスを元にテストコードをアップしたいと思いますが、少しお時間下さい。
引用返信 編集キー/
■31369 / inTopicNo.8)  Re[7]: 親子関係とメメントの汎化において、より厳密に型指定したい
□投稿者/ たくボン (138回)-(2009/01/20(Tue) 20:09:30)
No31275 (うたひこ さん) に返信
> ■No31071 (たくボン さん) に返信
>
> すみません、仕事の都合でお返事差し上げるのにまとまった時間がなかなかとれません。

いえ、こちらも来週納期と来月納期のプロジェクトを平行してるので、なかなか見れないです。

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -