2013-04-14 15 views
7

私は、ユーザーから提供されたマウスデータに基づいて幾何学的図形を描く簡単なプログラムを持っています。 私はマウスのトラッキング(マウスの移動履歴を持つリストを取得する)とシェイプと呼ばれる の1つのクラスを処理するクラスを持っています。このクラスからCircle、Rectangleなどの余分なShapesを導き出します。それらのすべてが抽象Draw()関数をオーバーライドします。過去のオープンクローズド原理の取得

すべてうまく動作しますが、ユーザーが希望の形状 を手動で切り替えることができるようにしたいときに問題が発生します。私はマウスのデータを取得し、私はどのような形を描くべきかを知っています。問題は、どのオブジェクトを作成するかを「知る」ようにコードを作成し、適切なパラメータをコンストラクタに渡す方法です。現時点では、形状の派生物を新たに追加することも不可能です。これは間違っています。私はobiouslyのようなコードで出てくるしたくない

List<Shape> Shapes = new List<Shape>(); 
// somwhere later 

if(CurrentShape == "polyline"){ 
    Shapes.Add(new Polyline(Points)); 
} 
else if (CurrentShape == "rectangle"){ 
    Shapes.Add(new Rectangle(BeginPoint, EndPoint)); 
} 
// and so on. 

明確に上記のコードはオープン・クローズ原理をvilates。問題は、私がそれを乗り越えるには良いアイデアがないということです。主な問題は、さまざまな図形 には異なるパラメータを持つコンストラクタがあることです。そのため、はるかに面倒です。

私はこれが一般的な問題だと確信していますが、それをどのように回避するのか分かりません。アイデアはありますか?

+2

これは「オープンクローズド原理」ではありません。それはちょうど多形性です。 –

+1

Shapeのクラスコードを編集用に閉じて拡張機能用に開いてもらいたいので、OCPの問題と一致すると思います。 –

+0

あなたがそれが真実であることを望む限り、それはそうではありません! –

答えて

3

これは工場だけでなく、注射可能な労働者の工場を求めています。

public class Context { 
    public Point BeginPoint; 
    public Point EndPoint; 
    public List Points; 

    whatever else 
} 

public class ShapeFactory { 

    List<FactoryWorker> workers; 

    public Shape CreateShape(string ShapeName, Context context) 
    { 
     foreach (FactoryWorker worker in workers) 
     if (worker.Accepts(ShapeName)) 
      return worker.CreateShape(context); 
    } 

    public void AddWorker(FactoryWorker worker) { 
     workers.Add(worker); 
    } 
} 

public abstract class FactortWorker { 
    public abstract bool Accepts(string ShapeName); 
    puboic Shape CreateShape(Context context); 
} 

public class PolyLineFactoryWorker : FactoryWorker { 

    public override bool Accepts(string ShapeName) { 
     return ShapeName == "polyline"; 
    } 

    public Shape CreateShape(Context context) { ... } 

} 

このようにしてコードを拡張することができます。新しい工場労働者は自由に作成して工場に追加できます。

+0

こんにちは。他の答えが '文字列ShapeName'パラメータも使用していれば、これは他の答えが' ShapeFactory'を拡張するよりもどのように優れていますか? – Blueriver

+0

@Blueriver:これにより、両方の答えが同等になります。しかし、もう一つの答えには工場がありません。そこのメーカーはここから悪い労働者です。ファクトリ自体は、OCPのクローズド部分を破るため、変更しないでください。 –

+0

私は参照してください。ありがとうございました! – Blueriver

6

すべてのオブジェクトを単一のクラスから派生させるか、同じインターフェイスを実装する必要がある場合は、一般的な方法の1つはfactoryです。しかし、あなたのケースでは、工場自体が拡張可能でなければならないため、単純な工場では十分ではないかもしれません。

interface IShapeMaker { 
    IShape Make(IList<Point> points); 
} 
class RectMaker : IShapeMaker { 
    public Make(IList<Point> points) { 
     // Check if the points are good to make a rectangle 
     ... 
     if (pointsAreGoodForRectangle) { 
      return new Rectangle(...); 
     } 
     return null; // Cannot make a rectangle 
    } 
} 
class PolylineMaker : IShapeMaker { 
    public Make(IList<Point> points) { 
     // Check if the points are good to make a polyline 
     ... 
     if (pointsAreGoodForPolyline) { 
      return new Polyline(...); 
     } 
     return null; // Cannot make a polyline 
    } 
} 

を手にこれらのMakerのクラスを使用すると、メーカーのレジストリ(シンプルList<IShapeMaker>は)彼らに点を通るメーカーを通過させると、停止することができます:それを実装するために

一つの方法は、以下の通りです。あなたがヌル以外の形を返すとき。

あなたはNewShapeNewShapeMakerのペアを追加し、既存のフレームワークに「でそれらをプラグイン」することができますので、このシステムは、拡張可能なまま:NewShapeMakerがレジストリに入った後は、システムの残りの部分は瞬時に認識して使用することが可能となりますあなたのNewShape

+2

この実装の欠点があります - 複数のメーカーが同じポイントを受け取っている可能性がありますので、突然メーカーの注文が重要です - 注文を変更するファクトリを別の形状に戻すことができます。ファクトリクライアントは、どのような形にするかを明示的に指定するため、元の実装にはこの問題はありません。これは、この追加のパラメータをファクトリメソッドに渡すことで簡単に修正できます(私の答えを見てください)。 –

+0

@WiktorZychla余分なパラメータは、呼び出し元に明示的な制御を与えますが、新しいシェイプを追加することによってシステムを拡張するたびに呼び出し元を変更する必要があります(呼び出し側は、新しい形状が利用可能になるたびに新しい形状)。問題の私の読書は、OPは余分なパラメータを避けたいということです。 – dasblinkenlight

+0

彼は「私はマウスのデータと描くべき形状を知っている」と書いています。これから、私は呼び出し元が正確な形状を知っていると言います。私は彼が利用可能なすべての形状や類似のツールボックスを持っていると信じています。 –

関連する問題