3

感谢您的时间!

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


原始模型

工厂模式中的窗口模型

就像上面的图片一样,我们在这里得到了一个窗口工厂模型。

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

2.Implement:WindowsFactory、MacFatory(实现接口);</p>


新模式

新模式

现在,我们已经改变了旧模型。所以 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.MacWindow类必须依赖具体的IButton/ITextBox实现(虽然它们在同一个Factory)。感觉不好!

2.如果有一天,我们得到一个派生自 Class 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

4 回答 4

2

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

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

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

关于单一职责原则,TextBoxes 和 Buttons 的创建应该在 Factory 中进行,因为 Factory 的职责是创建具有适当业务规则的对象。

解决方案 2 还不够好,因为窗口不应该知道工厂。或者一个窗口与其创建者没有任何业务,它的业务是在屏幕上绘制一个窗口等。(松耦合

于 2013-10-30T09:47:02.330 回答
1

好吧,我花了一段时间才理解潦草剧本的意图。你添加了很多成分,所以我真的很难找到最好的方法来回答你的问题。

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

所以,主要问题是,你想达到什么目标?您不会首先摆脱依赖关系,而且依赖关系也不是一件坏事。此外,工厂应该让你的生活更轻松,而不是更难——在你需要的地方使用它们,而不是无处不在。

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

如果有一天,我们得到一个派生自 Class MacWindow 的新类,我们必须重写那些虚拟方法以保持它们的风格相同。

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

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

于 2013-10-31T21:18:58.623 回答
1

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

首先,我认为最好区分MsComponentand MacComponent,以防止错误的依赖注入。2接口IMsComponentIMacComponent应该足够了。两者都应该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 中,您只需要接受特定类型的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,而忘记所有关于泛型的事情。

于 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).

当您想基于某些逻辑(在您的情况下为操作系统)创建通用初始化时,需要工厂。
当您创建一个时,MacWindow您已经知道它的所有组件将始终是 Mac 的,所以您真的只需要使用工厂,因为您已经拥有它吗?!当然不是

于 2013-10-30T13:18:00.427 回答