7

私は最近、非常に特定のニーズに応える画像編集ツールの作成を開始しました。これは、私自身の娯楽のためだけでなく、それを使用しようとしている人々にとっても同じです。しかし、私は早い段階で少し建築上の問題にぶつかりました。

他の画像エディタと同様に、ユーザーは「ツール」を使用して画像を描画および操作します。これに対する私の最初の試みは、単純なインターフェースで構成されていました。

public interface IDrawingTool
{
    void DrawEffect( Graphics g );
    // other stuff
}

これ(私は思った)は素晴らしくてきれいで、簡単なメンテナンスと拡張を可能にするでしょう。インターフェイスオブジェクトをに追加し、実行時に選択したオブジェクトのDrawEffectメソッドを呼び出すだけです。

このアプローチの問題は、さまざまな描画ツールが単一のインターフェイスに完全に準拠していないことです。たとえば、ペンツールは、機能するために描画するポイントを知っているだけで済みます。ただし、長方形には、現在の位置だけでなく、最初にクリックした点も必要です。ポリゴンツールは、複数のマウスクリックを追跡する必要があります。

これを実装するための良い方法を考えるのに苦労しています。私が今考えることができる最良の方法は、switchステートメントと各ツールのケースを含みます。これは、描画ロジックがToolタイプのオブジェクトによってカプセル化されるのではなく、Canvasクラスにあることを意味します。これは練習なので、正しい方法でやりたいと思います。よろしくお願いします。

4

4 に答える 4

1

インターフェイスを少し複雑に設計するのはどうですか?いくつかのコードから始めましょう。その後、それがどのように機能するかを説明します。

public class AbstractDrawingTool {

    private Graphics g;

    void AbstractDrawingTool( Graphics g ) {
        this.g = g;
    }

    void keyDown(KeyEvent e);
    void keyUp(KeyEvent e);
    void mouseMove(MouseEvent e);
    void mouseClick(MouseEvent e);
    void drop();
    // other stuff
}

ユーザーが特定の実装で作業を開始したら、ユーザー入力をツールに渡すという考え方です。このようにして、すべて同じインターフェイスを使用して、さまざまな描画ツールを作成できます。たとえば、単純なPointDrawingToolは、マウスクリックイベントを実装して、キャンバス上にポイントを配置するだけです。PolygonDrawingToolはkeyUpイベントも実装するため、特定のキー(つまりエスケープキー)が押されたときに線の描画を停止できます。

特殊なケースはドロップ方式です。現在選択されているツールを「ドロップ」するために呼び出されます。これは、ツールバーなどから別の実装が選択された場合に発生します。

この定義をコマンドパターンと組み合わせることもできます。この場合、AbstractDrawingToolの実装は、コマンドインターフェイスのインスタンスを作成し、操作が終了したら(つまり、キャンバス上にポイントを配置すると)スタックに配置する役割を果たします。

于 2009-04-06T22:47:59.000 に答える
1

経験則ですswitch。コードスケッチにステートメントが表示されている場合は、代わりにポリモーフィズムを使用する必要があることを示しています。したがって、この場合、さまざまな操作を実行できるようにしたいのですが、自分が欲しいと思っているswitchので、「ポリモーフィズムを使用してこれを作成するにはどうすればよいですか?」と考える必要があります。

ここで、オブジェクトが名詞ではなく動詞であるコマンドパターンを見てください。各コマンドはメソッドを実装します。オブジェクトを作成するときは、コマンドが実行する内容を確立します。 doThis()

public interface Command {
   public void doThis(Graphics g);  // I don't promise returning 
                                    // void is the best choice
   // Would it be better to return a Graphics object?
}

public class DrawRectangle implements Command {
   public DrawRectagle( Point topLeft, Point btmRight) { // ...
   }
   public void doThis(Graphics g){ // ...
   }
}

では、 undoを実装したい場合はどうするか考えてみてください。

アップデート

さて、これをもう少し拡張しましょう。このパターンを使用するポイントは、元の構築を行っている場合を除いて、クライアントがそれほど多くを知る必要がないことを確認することです。したがって、この例では、長方形の描画について考えてみましょう。長方形ツールを選択すると、ボタンクリックイベントハンドラーにコードが表示されます(これはすべて擬似コードです)

 cmdlist = [] // empty list
 bool firstClick = true
 Point tl = br = new Point(0,0)
 onClick:
   if firstClick:
     get mouse position into tl
     firstClick = false
   else:
     get mouse position into br
     cmdlist.append(new DrawRectangle(tl, br))
     firstClick = true

したがって、長方形を選択したら、コマンドリスト構造にDrawRectangleオブジェクトを追加します。しばらくして、リストを実行します

for cmd in cmdlist:
   cmd.doThis(Graphics g)

そして、これらのことは成し遂げられます。Commandに「undoThis」メソッドを追加してundoを実装することは明らかです。コマンドを作成するときは、オブジェクトがそれ自体を元に戻す方法を認識できるように、コードを構築する必要があります。次に、元に戻すとは、最後のCommandオブジェクトをリストから削除し、そのundoThisメソッドを実行することを意味します。

于 2009-04-05T04:41:38.527 に答える
0

GDI+ と Cairo グラフィック ライブラリの両方をサポートするようにマッピング ソフトウェアを再設計しようとしたときに、同様の問題に直面しました。描画インターフェイスをいくつかの一般的な操作/プリミティブに減らすことで解決しました。以下のコードを参照してください。

この後、描画したい「効果」はコマンドです(チャーリーが言うように)。インターフェイスを使用しIPainterて描画します。このアプローチの良いところは、効果が GDI+ のような具体的な描画エンジンから完全に切り離されていることです。Cairo エンジンに切り替えることで図面を SVG にエクスポートできるので、これは便利です。

もちろん、追加のグラフィック操作が必要な場合は、IPainterそれを使用してインターフェイスを拡張する必要がありますが、基本的な考え方は変わりません。詳細については、http: //igorbrejc.net/development/c/welcome-to-cairoを参照してください。

public interface IPainter : IDisposable
{
    void BeginPainting ();
    void Clear ();
    void DrawLines (int[] coords);
    void DrawPoint (int x, int y);
    void EndPainting ();
    void PaintCurve (PaintOperation operation, int[] coords);
    void PaintPolygon (PaintOperation operation, int[] coords);
    void PaintRectangle (PaintOperation operation, int x, int y, int width, int height);
    void SetHighQualityLevel (bool highQuality);
    void SetStyle (PaintingStyle style);
}

public class PaintingStyle
{
    public PaintingStyle()
    {
    }

    public PaintingStyle(int penColor)
    {
        this.penColor = penColor;
    }

    public int PenColor
    {
        get { return penColor; }
        set { penColor = value; }
    }

    public float PenWidth
    {
        get { return penWidth; }
        set { penWidth = value; }
    }

    private int penColor;
    private float penWidth;
}

public enum PaintOperation
{
    Outline,
    Fill,
    FillAndOutline,
}
于 2009-04-05T19:29:36.130 に答える
0

Kent Beck と Ralph Johnson による次の論文「Patterns Generate Architectures」で、エディター パターンを使用して正確な問題を説明し、解決します。

https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.46.8603&rep=rep1&type=pdf

于 2021-09-05T16:52:15.777 に答える