0

私はグラフの実装に取り​​組んでいます。私の Graph クラスは次のようになります。

public class Graph<VERTEX_TYPE, EDGE_TYPE, IDENTIFIER_TYPE> 
    where VERTEX_TYPE : Identifier<IDENTIFIER_TYPE>
    where EDGE_TYPE : Identifier<IDENTIFIER_TYPE>
    where IDENTIFIER_TYPE : IConvertible
{
...
}

(実装されたインターフェースは気にしないでください。質問には関係ありません)。

Vertex クラスと Edge クラスの両方に、次の方法でアクセスできる一般的なデータが含まれています。

public class Vertex<VERTEX_TYPE,EDGE_TYPE,IDENTIFIER_TYPE>  
    where VERTEX_TYPE : Identifier<IDENTIFIER_TYPE>
    where EDGE_TYPE : Identifier<IDENTIFIER_TYPE>
{
    public VERTEX_TYPE Data{get;private set;}
    ....
}

public class Edge<EDGE_TYPE,VERTEX_TYPE,IDENTIFIER_TYPE> 
    where EDGE_TYPE : Identifier<IDENTIFIER_TYPE>
    where VERTEX_TYPE : Identifier<IDENTIFIER_TYPE>
{

    public EDGE_TYPE Data{get;private set;}
    ....
}

これで、いくつかのアルゴリズムを実装する GraphVisitor クラスができました。グラフがトラバースされるとき、アルゴリズムによって現在トラバースされているエッジと頂点の両方でいくつかのデリゲートが呼び出されるようにします。

2 組のデリゲートを定義しました。1 組は、 などのグラフ要素で実行する必要がある操作に関連していますVertex<VERTEX_TYPE,EDGE_TYPE,IDENTIFIER_TYPE>。デリゲートの 2 番目のペアは、保持されているデータに対してのみ実行された操作に関連しています。

public class GraphVisitor<VERTEX_TYPE, EDGE_TYPE, IDENTIFIER_TYPE> 
    where VERTEX_TYPE : Identifier<IDENTIFIER_TYPE>
    where EDGE_TYPE : Identifier<IDENTIFIER_TYPE>
    where IDENTIFIER_TYPE : IConvertible

{
    public delegate void VertexDataOperation(VERTEX_TYPE vertex);
    public delegate void EdgeDataOperation(EDGE_TYPE vertex);
    public delegate void VertexOperation(Vertex<VERTEX_TYPE,EDGE_TYPE,IDENTIFIER_TYPE> vertex);
    public delegate void EdgeOperation(Edge<EDGE_TYPE,VERTEX_TYPE,IDENTIFIER_TYPE> vertex);
       .....
}

BFS アルゴリズムを実装しました (実装全体を投稿することは避けます)。パラメーターとして 2 つのデリゲートを使用します。

public void BFS(VertexOperation op, EdgeOperation edgeOp)
{
...      
Vertex<VERTEX_TYPE,EDGE_TYPE,IDENTIFIER_TYPE> currentVertex;
op(currentVertex);

foreach (Edge<EDGE_TYPE,VERTEX_TYPE,IDENTIFIER_TYPE> e in currentVertex.NeighBors())
{
    edgeOp(e);
    ...
}
... 

と の BFS バージョンを作成しようとVertexDataOperationEdgeDataOperationていましたが、2 つの異なるデリゲート パラメータ タイプを使用するには、最終的にすべての BFS コードをコピーする必要があることに気付きました。

public void BFS(VertexDataOperation op, EdgeDataOperation edgeOp)
...
Vertex<VERTEX_TYPE,EDGE_TYPE,IDENTIFIER_TYPE> currentVertex;
op(currentVertex.Data); //method differs only here

foreach (Edge<EDGE_TYPE,VERTEX_TYPE,IDENTIFIER_TYPE> e in currentVertex.NeighBors())
{
    edgeOp(e.Data);//method differs only here
    ...
}

同じメソッドの同一の実装を避けたいと思います。変更されるのは、メソッド シグネチャと、メソッドが呼び出されるオブジェクトだけです。

このコードの設計を改善するためのアイデアはありますか?

編集:

4 つのデリゲートすべてを関数に渡すこともできますが、トラバーサルごとに 1 組しか使用しないため、あまりきれいに見えません。

4

1 に答える 1

1

まず、タイプパラメータの名前を変更することを強くお勧めします。慣例では、Tプレフィックスを使用してからPascalCased名を使用します(例:TVertexおよび)TEdge

次に、関連する複数のデリゲートが必要な場合は、いくつかのメソッドを備えたインターフェイスが本当に必要なようです。いつでもインターフェースのno-op実装を作成して、1つのメソッドのみをオーバーライドできるようにすることができます。または、ラムダ式を使用してアクションを表現する場合は、デリゲートから具象実装のインスタンスを作成する静的メソッドを提供することもできます。

これは基本的にReactiveExtensionsがIObserver<T>インターフェースで行うことであり、非常にうまく機能します。

于 2012-11-12T17:20:38.213 に答える