感谢您的时间!
这是一个让我很困惑的问题!它是关于工厂模式中组件之间的依赖关系。
就像上面的图片一样,我们在这里得到了一个Window Factory模型。
1.Interface:IFatory IWindow,IButton,ITextBox(模型定义);
2.Implement:WindowsFactory,MacFatory(实现接口);
现在,我们更改了旧模型。因此,IWindow有两个名为“CloseButton”和“TitileBox”的属性。问题出现了:
1.何时以及如何实现这两个属性?
2.这两个属性应该与它们的容器窗口具有相同的样式,这意味着我们只能有WindowsStyle(Window / Button / TextBox)或MacStyle(Window / Button / TextBox)。不能混合!
为了解决这些问题,我进行了一些试验。请检查下面的代码。
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的新类,我们必须覆盖这些虚拟方法以保持它们的风格相同。
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的依赖。让产品了解工厂的细节并不好。
所以这就是谜题! 哪种解决方案更好? 有没有其他方法可以解决这个问题?(渴望知道!真的非常感谢!) 你能给我一些建议吗?
请允许我说“非常感谢你!”你有时间阅读这个问题!
答案 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
来区分。
首先,我认为最好区分MsComponent
和MacComponent
,以防止错误的依赖注入。 2接口IMsComponent
和IMacComponent
应该足够了。两者都应该在以后为泛型实现IComponentBase
。由于无法定义multiple-implementation
类型,例如{IMsComponent,ITextBox},因此您需要第3个界面,即IMsTextBox : IMsComponent, ITextBox
和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,而忘记关于泛型的所有内容。
答案 3 :(得分:1)
嗯,我花了一段时间才明白潦草的场景的意图。你添加了许多成分,所以我真的很难找到最好的钩子来回答你的问题。
首先,您可以轻松地将Windows组件添加到Mac窗口中(已经说明了),您创建了一组完整的重复类,这让我感到恼火,窗口实现似乎是一个非常具体的例子。
所以,主要的问题是,你想要实现什么?你不会首先摆脱依赖关系,而且依赖关系也不是坏事。此外,工厂应该让你的生活更轻松,而不是更难 - 在你需要的地方使用它们,而不是在任何地方。
因此,您的具体窗口实现依赖于具体按钮和标题框实现。这意味着在您的情况下,窗口实现应该最了解其子项。工厂只创建窗口,窗口知道要创建哪些按钮。因此,从这个角度看,解决方案1看起来很好。解决方案二其实有点不好,因为窗口依赖于工厂,这有点奇怪。
如果有一天,我们得到一个派生自类MacWindow的新类,我们必须覆盖这些虚拟方法以使它们保持相同的样式。
实际上,这是我最初提出问题的地方。您创建具有相同行为的重复类只是为了具有一定的外观(和感觉)。在简化问题时,您可以轻松降低复杂性并获得更好的想法,并避免在“远距离未来”时出现问题。在将外观与行为区分开来时,您将摆脱有关依赖关系的许多问题。工厂会创建窗口实例,也许设置样式,你就完成了。
这将是一种可扩展的方法,也很容易,并且也非常接近于在现代框架中创建视图的方式(蒙皮+样式|行为|控制器)。