1

以下のコードを読んでください。質問は最後にあります。

using System;
using System.Collections.Generic;

namespace Graphics
{
    public interface IGraphicsFactory
    {
        ICanvas CreateCanvas();
        Square CreateSquare();
        ComposedShape CreateComposedShape();
    }

    public class SimpleGraphicsFactory : IGraphicsFactory
    {
        public Square CreateSquare()
        {
            return new SimpleImpl.SimpleSquare();
        }

        public ComposedShape CreateComposedShape()
        {
            return new SimpleImpl.SimpleComposedShape();
        }

        public ICanvas CreateCanvas()
        {
            return new SimpleImpl.SimpleCanvas();
        }
    }

    public interface ICanvas
    {
        void AddShape(ShapeBase shape);
        void Render();
    }

    public abstract class ShapeBase
    {
        public abstract void Paint(ICanvas canvas);
    }

    public abstract class Square : ShapeBase
    {
        public int size;
    }

    public abstract class ComposedShape : ShapeBase
    {
        public int size;
        public ShapeBase InternalShape1 { get; set; }
        public ShapeBase InternalShape2 { get; set; }
    }
}


namespace Graphics.SimpleImpl
{
    internal class SimpleSquare : Graphics.Square
    {
        public void Init()
        {
            // do something really important
        }

        public override void Paint(ICanvas canvas)
        {
            Init();

            //?? how to avoid the type cast? (and I want to keep the DrawLine out of the ICanvas interface)
            SimpleCanvas scanvas = (canvas as SimpleCanvas);
            scanvas.DrawLine();
            scanvas.DrawLine();
            scanvas.DrawLine();
            scanvas.DrawLine();
        }
    }

    internal class SimpleComposedShape : Graphics.ComposedShape
    {
        public void Init()
        {
            //?? how can I call `InternalShape1.Init', preferably without type casts? (and I want to keep `Init` out of the `ShapeBase` class)
            // this.InternalShape1.Init();
            // this.InternalShape2.Init();
        }

        public override void Paint(ICanvas canvas)
        {
            Init();
            // TODO: draw the thing
        }
    }

    internal class SimpleCanvas : Graphics.ICanvas
    {
        List<ShapeBase> shapes = new List<ShapeBase>();

        public void AddShape(ShapeBase shape)
        {
            shapes.Add(shape);
        }

        public void Render()
        {
            foreach (ShapeBase s in shapes)
            {
                s.Paint(this);
            }
        }


        public void DrawLine()
        {
        }
    }
}


namespace Test
{
    using Graphics;
    class TestSimpleGraphics
    {
        static void Test1()
        {
            IGraphicsFactory fact = new SimpleGraphicsFactory();
            ICanvas canvas = fact.CreateCanvas();

            Square sq1 = fact.CreateSquare();
            Square sq2 = fact.CreateSquare();
            ComposedShape cs = fact.CreateComposedShape();
            cs.InternalShape1 = sq1;
            cs.InternalShape2 = sq2;

            canvas.AddShape(cs);
            canvas.Paint();
        }
    }
}
  1. 抽象ファクトリ パターンの実装は正しいですか?
  2. 内部SimpleSquare.Paint: 型キャストを回避することは可能ですか? (そして、インターフェースDrawLineから外したい)ICanvas
  3. Inside :できれば型キャストなしでSimpleComposedShape.Initを呼び出すにはどうすればよいですか? (そして私はクラスから離れInternalShape.Initたい)InitShapeBase
4

2 に答える 2

1

1 - あなたSimpleGraphicsFactoryは確かに抽象ファクトリの良い例だと思います.

2 -同じコンクリート工場によって作成された同じ「ファミリ」の一部であるため、にSimpleSquareキャストすることは完全に適切です。SimpleCanvasAbstract Factory の定義を思い出してください (強調は私のものです)。

具象クラスを指定せずに、関連オブジェクトまたは依存オブジェクトのファミリを作成するためのインターフェイスを提供します。

この設計パターンの意味は、それによって作成されたクラスが、同じファミリのクラスで使用されていることを想定/要求できるということです。

.NET の世界の別の例を使用すると、System.Data名前空間は同様に機能します。名前空間のオブジェクトはSystem.Data.Sql、System.Data.Oracle のオブジェクトでは機能しません。SqlParameteranOracleParameterが期待される場所にa を渡すことはできません。ファミリを選択し、ファミリ内にとどまります。

3 - あなたが何をしようとしているのかわかりません。詳細についてコメントする必要があります。対処するための回答を見直します。には、呼び出し元が複数の形状を複合 (コンテナー) に追加できるようにComposedShapeするメソッドが必要です。Add(Shape s)しかし、おそらく私は誤解しています。

于 2013-08-02T13:11:42.610 に答える
0

あなたの意図が正しければ、System.Drawing.Graphicsクラス (または HTML<canvas>など) の機能を模倣しようとしています。

それが正しければ、次の提案をします。

  1. 名前を変更IGraphicsFactoryしてICanvasFactory、具体的なICanvas実装のみを作成します。およびその他のメソッドを削除しCreateSquareます。これは、ファクトリを介して形状を作成する必要がないためです (重要なのは、形状が具体的なICanvas実装に渡されることだけです)。

  2. インターフェイスは、ICanvas基本的な形状 (線、円、塗りつぶし領域など) を描画できるキャンバスを表します。これは、呼び出し元がこれらのプリミティブを作成できるようにするパブリック メソッドを公開する必要があることを意味します。Graphicsさまざまな変換機能も提供しますが、現時点ではやり過ぎかもしれません。

    interface ICanvas
    {
         void Clear();
         void DrawLine(Point from, Point to, Pen pen);
         void DrawCircle(Point center, Double radius, Pen pen);
         void Paint();
         /* and stuff like that */
    }
    
  3. Canvasには のリストを含めるのではなくShapes、プリミティブのリストを含める必要があります。たとえば、ICanvas.DrawLine(...)メソッドを呼び出すと、プリミティブのインスタンスが作成Lineされ、内部リストに格納されます。

    これらのプリミティブ クラスの機能は、実際の実装によって異なりますICanvas(ビットマップに描画するか、プリンターに描画するかなど)。また、実装に依存する非表示のデータもいくつかあります (byte[]何らかの 8 ビット ビットマップを格納するふりをして、 を使用します)。

    class BitmapCanvas : ICanvas
    {
        private readonly byte[] _bitmapData;
        private readonly List<IBitmapShape> _primitives;
    
        public BitmapCanvas(int width, int height)
        {
            _bitmapData = new byte[width * height];
            _primitives = new List<IPrimitiveShape>();
        }
    
        public void DrawLine(...) 
        {
            // different implementations will handle this part differently.
            _primitives.Add(new BitmapLine(_bitmapData, from, to, pen));
        }
    
        public void Paint()
        {
            Clear(_bitmapData);
            foreach (var shape in _primitives)
                shape.Draw();
        }
    }
    
  4. 具体的なプリミティブ クラスは、この内部ロジックを処理します。

    class BitmapLine : IBitmapShape
    {
        public void Draw()
        {
            // write to the underlying byte array
        }
    }
    
  5. 実装では実際の形状は描画されないため、クラスICanvasは必要ありません。ShapeBaseただし、グラフィックプリミティブを描画するためのアナログクラスが必要になります (IBitmapShape上記で呼び出されます)。

于 2013-08-02T13:35:24.380 に答える