工厂模式中组件之间的依赖关系

时间:2013-10-30 09:21:33

标签: design-patterns

感谢您的时间!

这是一个让我很困惑的问题!它是关于工厂模式中组件之间的依赖关系。


原始模型

Window Model in Factory Pattern

就像上面的图片一样,我们在这里得到了一个Window Factory模型。

1.Interface:IFatory IWindow,IButton,ITextBox(模型定义);

2.Implement:WindowsFactory,MacFatory(实现接口);


新模型

New Model

现在,我们更改了旧模型。因此,IWindow有两个名为“CloseButton”和“TitileBox”的属性。问题出现了:

1.何时以及如何实现这两个属性?

2.这两个属性应该与它们的容器窗口具有相同的样式,这意味着我们只能有WindowsStyle(Window / Button / TextBox)或MacStyle(Window / Button / TextBox)。不能混合!


为了解决这些问题,我进行了一些试验。请检查下面的代码。

解决方案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.Class MacWindow必须依赖于特定的IButton / ITextBox工具(虽然它们在同一个Factory中)。感觉很糟糕!

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.现在的工作要容易得多,如果我们从MacFactory派生出一个名为MacOSXFactory的新工厂,我们唯一需要做的就是覆盖IFactory.MakeButton()和IFactory.MakeTextBox()方法; (呃......这些方法应该被定义为'公共虚拟')

2.不再存在对特定工具的依赖,但是窗口类必须注入对IFactory的依赖。让产品了解工厂的细节并不好。


所以这就是谜题! 哪种解决方案更好? 有没有其他方法可以解决这个问题?(渴望知道!真的非常感谢!) 你能给我一些建议吗?

确认

请允许我说“非常感谢你!”你有时间阅读这个问题!

4 个答案:

答案 0 :(得分:2)

您应该在工厂内实例化它们。

解决方案1不够好,因为实例化业务应该在一个地方实施,而这个地方在工厂内部。您已经在每个工厂中都有MakeButton和MakeTextBox实现,在MakeWindow实现中调用它们,如下所示;

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

关于Single Responsibility principle,TextBox和Buttons的创建应该在Factory中保存,因为Factory的职责是使用适当的业务规则创建对象。

解决方案2也不够好,因为Window不应该知道Factory。或者Window与其创建者没有任何业务,其业务是在屏幕上绘制窗口等。(Loose Coupling

答案 1 :(得分:1)

你应该使用解决方案1!

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

当您希望基于某些逻辑(在您的情况下是OS)创建通用初始化时,需要工厂 当你创建一个MacWindow时,你已经知道它的所有组件都将是Mac的,所以你真的必须使用工厂,因为你已经拥有它了吗?! 当然不是

答案 2 :(得分:1)

我同意Jehof的评论,你的解决方案似乎太抽象了。它需要一些更多的约束来确保每个组件没有到达不同类型的窗口(MS到Mac,反之亦然)。我在这里使用术语MSWindow来区分。

首先,我认为最好区分MsComponentMacComponent,以防止错误的依赖注入。 2接口IMsComponentIMacComponent应该足够了。两者都应该在以后为泛型实现IComponentBase。由于无法定义multiple-implementation类型,例如{IMsComponent,ITextBox},因此您需要第3个界面,即IMsTextBox : IMsComponent, ITextBoxIMacTextBox : 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中,您只需要接受特定类型的IWindowContext。例如在MsWindow类中:

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,而忘记关于泛型的所有内容。

答案 3 :(得分:1)

嗯,我花了一段时间才明白潦草的场景的意图。你添加了许多成分,所以我真的很难找到最好的钩子来回答你的问题。

首先,您可以轻松地将Windows组件添加到Mac窗口中(已经说明了),您创建了一组完整的重复类,这让我感到恼火,窗口实现似乎是一个非常具体的例子。

所以,主要的问题是,你想要实现什么?你不会首先摆脱依赖关系,而且依赖关系也不是坏事。此外,工厂应该让你的生活更轻松,而不是更难 - 在你需要的地方使用它们,而不是在任何地方。

因此,您的具体窗口实现依赖于具体按钮和标题框实现。这意味着在您的情况下,窗口实现应该最了解其子项。工厂只创建窗口,窗口知道要创建哪些按钮。因此,从这个角度看,解决方案1看起来很好。解决方案二其实有点不好,因为窗口依赖于工厂,这有点奇怪。

  

如果有一天,我们得到一个派生自类MacWindow的新类,我们必须覆盖这些虚拟方法以使它们保持相同的样式。

实际上,这是我最初提出问题的地方。您创建具有相同行为的重复类只是为了具有一定的外观(和感觉)。在简化问题时,您可以轻松降低复杂性并获得更好的想法,并避免在“远距离未来”时出现问题。在将外观与行为区分开来时,您将摆脱有关依赖关系的许多问题。工厂会创建窗口实例,也许设置样式,你就完成了。

这将是一种可扩展的方法,也很容易,并且也非常接近于在现代框架中创建视图的方式(蒙皮+样式|行为|控制器)。