3

お時間をいただきありがとうございます!

これは、私を大いに混乱させた問題です!それは、Factory パターンのコンポーネント間の依存関係に関するものです。


オリジナルモデル

ファクトリーパターンのウィンドウモデル

上の写真のように、ここに Window Factory Model があります。

1.Interface: IFatory IWindow,IButton,ITextBox (モデル定義);

2.実装: WindowsFactory,MacFatory (インターフェイスを実装するため);</p>


新しいモデル

新しいモデル

今、古いモデルを変更しました。そのため、IWindow には「CloseButton」と「TitileBox」という名前の 2 つのプロパティがあります。問題が発生します。

1.これら2つのプロパティをいつ、どのように実装しますか?

2.これらの 2 つのプロパティは、コンテナー ウィンドウと同じスタイルを持つ必要があります。


これらの問題を解決するために、いくつかの試行錯誤を行いました。以下のコードを確認してください。

解決策 1

class MacWindow:IWindow
{
  public IButton CloseButton {get;private set;}
  public ITextBox TitleBox {get;private set;}

  public MacWindow()
  {       
     this.CreateCloseButton();
     this.CreateTitleBox();
  }

  protected virtual void CreateCloseButton()
  {
     CloseButton = new MacButton();
  }

  protected virtual void CreateTitleBox()
  {
     TitleBox = new MacTextBox(); 
  }
}

ご覧のとおり、このソリューションは良くありません。

1.クラス MacWindow は、特定の IButton/ITextBox 実装に依存する必要があります (ただし、それらは同じファクトリにあります)。

2.いつか、クラス MacWindow から派生した新しいクラスを取得した場合、それらの仮想メソッドをオーバーライドして、それらを同じスタイルに保つ必要があります。


解決策 2

class MacWindow:IWindow
{
  public IFactory ActiveFactory{get;private set;}      
  public IButton CloseButton {get;private set;}
  public ITextBox TitleBox {get;private set;}

  public MacWindow(IFactory factoy)
  {
     this.ActiveFactory=factory;
     this.CreateCloseButton();
     this.CreateTitleBox();
  }

  private void CreateCloseButton()
  {
     CloseButton = ActiveFactory.MakeButton();
  }

  private void CreateTitleBox()
  {
     TitleBox = ActiveFactory.MakeTextBox(); 
  }
}

これは見た目が良くなりましたが、今では完璧です。

1. MacOSXFactory という名前の MacFactory から派生した新しいファクトリを取得した場合、作業がはるかに簡単になりました。必要なことは、IFactory.MakeButton() および IFactory.MakeTextBox() メソッドをオーバーライドすることだけです;(er. .これらのメソッドは「パブリック仮想」として定義する必要があります)

2.特定の実装への依存関係はなくなりましたが、ウィンドウ クラスは IFactory への依存関係を注入する必要があります。工場の詳細を製品に知られてしまうのは気分が悪い。


というわけでパズルです!どちらのソリューションが優れていますか? この問題を解決する他の方法はありますか? (知りたい!本当にありがとう!) アドバイスをお願いします。

了承

「ありがとうございます!」と言わせてください。質問を読んでください!

4

4 に答える 4

2

ファクトリ内でそれらをインスタンス化する必要があります。

インスタンス化ビジネスは 1 か所で実装する必要があり、この場所は Factory 内にあるため、解決策 1 は十分ではありません。各ファクトリには既に MakeButton と MakeTextBox の実装があり、次のように MakeWindow 実装内でそれらを呼び出します。

IWindow MakeWindow() //MacFactory's MakeWindow
{
    IWindow window = new MacWindow();
    window.TitleBox = this.MakeTextBox();
    window.CloseButton = this.MakeButton();
}

単一責任の原則に関して、Factory の責任は適切なビジネス ルールによるオブジェクトの作成であるため、TextBox と Button の作成は Factory で保持する必要があります。

Window は Factory について認識してはならないため、解決策 2 も十分ではありません。または、ウィンドウはその作成者とは何の関係もありません。その仕事は、画面上にウィンドウを描画することです。(疎結合)

于 2013-10-30T09:47:02.330 に答える
1

うーん、落書きされたシナリオの意図を理解するのに時間がかかった。あなたは多くの成分を追加したので、あなたの質問に答えるのに最適なフックを見つけるのに本当に苦労しました.

まず第一に、Windows コンポーネントを Mac ウィンドウに簡単に追加できます (これについては既に説明しました)。複製されたクラスの完全なセットを作成したため、少しイライラしました。ウィンドウの実装はかなり具体的な例のようです。

では、主な質問は、何を達成しようとしているのかということです。そもそも依存関係を取り除くことはできませんし、依存関係は悪いことではありません。また、工場はあなたの生活を困難にするのではなく、楽にするべきです。どこでもではなく、必要な場所で使用してください。

したがって、具体的なウィンドウの実装は、具体的なボタンとタイトルボックスの実装に依存しています。これは、あなたの場合、ウィンドウの実装がその子について最もよく知っている必要があることを意味します。ファクトリはウィンドウのみを作成しており、ウィンドウは作成するボタンを認識しています。したがって、解決策 1 はその観点からは良さそうです。解決策 2 は、ウィンドウがファクトリに依存しているため、実際にはちょっと悪い方法です。これは奇妙なことです。

いつか、クラス MacWindow から派生した新しいクラスを取得した場合、それらの仮想メソッドをオーバーライドして、それらを同じスタイルに保つ必要があります。

実際、これは私の最初の質問が導くところでした。特定のルック (アンド フィール) を持つためだけに、同じ動作で重複したクラスを作成しています。複雑さを簡単に軽減し、問題を単純化するときにより良いアイデアを得ることができ、「遠い将来」についての質問を避けることができます。ルック アンド フィールを動作から切り離すと、依存関係に関する多くの疑問が解消されます。ファクトリはウィンドウ インスタンスを作成し、おそらくスタイルを設定して完了です。

これは拡張可能なアプローチであり、簡単であり、最新のフレームワーク (スキニング + スタイリング | 動作 | コントローラー) でビューを作成する方法に非常に近いものになります。

于 2013-10-31T21:18:58.623 に答える
1

Jehof のコメントに同意します。あなたの解決策は抽象的すぎるようです。各コンポーネントが異なるタイプのウィンドウ (MS から Mac またはその逆) に到達しないようにするために、さらに制約が必要です。MSWindowここでは区別するために用語を使用します。

まず、間違った依存性注入を防ぐために、MsComponentとを区別した方がよいと思います。MacComponent2 インターフェイスIMsComponentでありIMacComponent、これで十分なはずです。IComponentBase両方とも、後でジェネリック用に実装する必要があります。multiple-implementation{IMsComponent, ITextBox} などの型を定義する方法がないため、 3 つ目のインターフェイスであるIMsTextBox : IMsComponent, ITextBoxandが必要IMacTextBox : IMacComponent, ITextBoxです。

したがって、グラフは次のようになります。

ITextBox  IComponentBase
    \           |  
     \     IMsComponent
      \        /    
      IMsTextBox

次に、コンポーネントを処理するためのコンテキストが必要です。

IWindowContext<TtextBox, Tbutton> where TtextBox : IComponentBase, ITextBox 
                                  where Tbutton : IComponentBase, IButton
{
    TtextBox TitleBox {get;set;}
    Tbutton CloseButton {get;set;}
}

パブリック セットをプライベート セットに置き換えて、コンストラクター インジェクションを実行できることに注意してください。次に、IWindow で、特定のタイプの を受け入れるだけですIWindowContextMsWindowクラスのように:

public class MsWindow{
   public MsWindow(IWindowContext<IMsTextBox, IMsButton> context){
       this.CloseButton = context.CloseButton;
       this.TitleTextBox = context.TitleTextBox;
   }

   public ITextBox TitleTextBox {get; private set;}
   public IButton CloseButton {get; private set;}
}

ファクトリの場合、特定の windowContext または特定のウィンドウを返すように柔軟に対応できます。ただし、より柔軟であるため、特定の windowContext を返すファクトリを好みます。ファクトリもジェネリックにすることを忘れないでください。

public interface IContextFactory<TTextBox, TButton>
    where TTextBox : IComponent, ITextBox
    where TButton : IComponent, IButton
{
    IWindowContext<TTextBox, TButton> Create();
}

少し複雑ですよね?ただし、 をMsWindowに割り当てることはできませんMacComponent。ただし、可能であれば、これほど多くの抽象化は必要ないと思います。漏れやすい抽象化が必要な場合は、 Context と ContextFactory のみを使用して、ジェネリックをすべて忘れることができます。

于 2013-10-31T02:41:18.580 に答える
1

解決策 1 を使用する必要があります。

Class MacWindow has to rely on the specific IButton/ITextBox implements (although they are in the same Factory).

Factory は、何らかのロジック (この場合は OS) に基づいて一般的な初期化を作成する場合に必要です。
を作成するMacWindowと、そのすべてのコンポーネントが常に Mac のものになることがすでにわかっているので、既にあるという理由だけで本当に factory を使用する必要があるのでしょうか?! もちろん違います

于 2013-10-30T13:18:00.427 に答える