对于OO而言,我常常觉得我理解一个概念,直到我尝试从简化的例子转到我给出的实际要求。我很感激任何帮助,了解如何考虑这个特殊问题。
我有一个GUI,它有一个面板,用于定义容器和其中的项目。现在,有三种类型的容器。容器具有一些属性(如大小),并且可以包含一到三种不同类型的项目(两个是可选的)。输入足够的信息后,我会使用这些信息制作图表。
我实现了一个Observer模式。当用户输入信息时,它会更新一个observable,通知图表它已更改。
到目前为止,我很高兴。现在皱纹。我的容器有一个大小,但有时它是明确输入的,有时它取决于容器的容量。这取决于容器的类型。如果未明确输入,确定大小的方式取决于其中一个可选项是否在容器中。我不确定作家的要求是否只是讨厌我,或者我缺乏足够的OO经验,但那些皱纹让我很适合。现在,我的observable只有变量来保存所有各种信息,我使用一堆switch语句来处理特殊情况。
我在想我可以使用构建器模式。导演会产生绘制的数据。我会为每种类型的容器创建一个具体的构建器,然后我将使用容器属性及其中的项来实例化该类。我将使用抽象构建器类的方法将图形所需的值返回到控制器,例如getContainerSize()并将它们组合以产生实际的数据点。此外,如果用户尚未输入足够的数据来完成图表,则导演可以返回null。
我是否接近可用的OO设计?我不确定我是不是只是将特殊的外壳埋得更深一些。
另一个皱纹。其中一个项目类型包含在所有三个容器中。现在,我的observable分别跟踪容器和项目,创建图形的方法决定要求什么(当用户玩这些值时,图形会发生很大变化)。如果我有多个构建器模式,那该怎么办?也许我错过了一步? observable更新当前容器的构建器,然后让图形知道它应该调用导向器来获取其坐标?那还需要询问当前的容器是什么?
欢迎所有评论,帮助我了解OO设计或特别是这个问题。实际要求有更多特殊情况,但这是基本主题的变化。
感谢您的回复。我想我将两个问题混在一起是有罪的。这是尝试提供一个专注于Builder模式的最小代码示例。注意IE8我没有看到任何身份,FireFox 8,我很抱歉在IE8中阅读代码的人。
interface MyContainerBuilder
{
void setContents( MyContents contents );
Double myVolume();
Double myDensity();
}
class SmallContainerBuilder implements MyContainerBuilder
{
Double volume = null;
Double density = null;
MyContents contents = null;
public void setVolume()
{
if (contents != null)
{
volume = contents.myDensity() / 3.0;
}
}
public void setContents( MyContents contents )
{
this.contents = contents;
}
public Double myVolume()
{
if (volume == null)
setVolume();
return volume;
}
public Double myDensity()
{
return contents.myDensity();
}
}
class BigContainerBuilder implements MyContainerBuilder
{
Double volume = null;
Double density = null;
MyContents contents = null;
public void setVolume( Double volume )
{
this.volume = volume;
}
public void setContents( MyContents contents )
{
this.contents = contents;
}
public Double myVolume()
{
return volume;
}
public Double myDensity()
{
return contents.myDensity();
}
}
class ContainerDirector
{
Double myResult( MyContainerBuilder container )
{
return container.myVolume() * container.myDensity();
}
}
class MyContents
{
Double density;
MyContents( Double density )
{
this.density = density;
}
public Double myDensity()
{
return density;
}
}
class Test
{
public static void main(String[] args)
{
SmallContainerBuilder smallContainer = new SmallContainerBuilder();
BigContainerBuilder bigContainer = new BigContainerBuilder();
ContainerDirector director = new ContainerDirector();
//
// Assume this comes from the GUI, where an ActionListener knows which Builder
// to use based on the user's action. I'd be having my observable store this.
Double density = 15.0;
MyContents contents = new MyContents( density );
smallContainer.setContents( contents );
//
// Then I would need to tell my observer to do this.
Double results = director.myResult( smallContainer );
System.out.println( "Use this result: " + results );
}
}
我有两种类型的容器,它们使用不同的方法来计算音量。因此,假设我有radiobuttons来选择容器类型,并在每个radiobutton下面放置一个可以进入所选容器的项目组合框。组合框上的ActionListener会将项目放在正确的容器中并将其保存到我的observable(还有许多其他实际设置的东西),它告诉我的观察者使用导向器获取适当的值,然后观察者更新GUI的一些视图组件。
答案 0 :(得分:2)
我的容器有一个大小,但有时会明确输入,有时它取决于容器的容量。那是由容器类型决定的。 [...]如果没有明确输入,则取决于其中一个可选项是否在容器中。
听起来你可能有一个抽象容器的不同子类,每个子类以不同的方式实现getContainerSize()
。一个用于显式输入,一个用于带有可选项的情况,一个用于没有它。
...我使用一堆switch语句来处理特殊情况。
听起来不太好。 Replace Conditional with Polymorphism如果适用的话。
我在想我可以使用构建器模式...
我假设您需要根据一组输入变量确定具体类型的对象(或null
)。如果模式知道什么类型,模式提供了构建复杂对象的方法,但实际问题是决定哪种类型。所以你需要在某个地方使用条件代码。那个地方可以是建筑工人,但也可以是简单的工厂。
现在,我的observable分别跟踪容器和项目[...] observable更新当前容器的构建器[...]如果我有多个构建器模式,那该怎么工作?
并不真正理解Observable正在观察什么以及在哪种情况下触发什么变化,但Observable更新构建器(或多个)听起来很奇怪。尽管如此,这更像是一种直觉:)
我是否接近可用的OO设计?
如果有效,是的。但实际上我无法告诉你,如果你创造了一个好的或可用的设计,因为我仍然不知道你的问题或你的设计的细节 - 现在几次阅读你的文字。
现在,不要在问题中添加另一页信息,而是尝试将问题分解为更小的部分,并使用代码段/图像/图形或任何类型的可视化来帮助人们了解您的问题以及这些部分之间的所有联系。只是很多文本都是相当可怕的,像这样的巨大的OO设计整体来说太大而且过于本地化了。
你的方法似乎很好,但它需要IMO非常复杂的对象来证明这种使用的合理性。
您可以通过观察者在GUI中创建MyContents实例。然后将该对象包装在MyContainerBuilder中,然后将其提供给ContainerDirector,然后ContainerDirector生成结果。在我看来,如果MyContents或结果很简单,那就太过分了。
此外,将MyContents设置为MyContainerBuilder的方式意味着您无法盲目地重用相同的具体MyContainerBuilder实例。您要么必须确保按顺序使用它,要么每次必须构建一个新的。
即这不起作用
MyContents content1 = new MyContents( 5 );
MyContents content2 = new MyContents( 6 );
smallContainer.setContents( content1 );
smallContainer.setContents( content2 ); // overwriting old state
Double results1 = director.myResult( smallContainer ); // wrong result
Double results2 = director.myResult( smallContainer );
我假设MyContents是一个通用数据保持对象,用户在几个步骤中填充数据。一旦用户对它感到满意,就会将其提交到结果中。据我所知,你知道结果必须是什么。
下面是一个使用策略模式的方法(? - 我很糟糕,所有这些名字和差异很小),我选择直接插入MyContents,所以MyContents对象一旦完成,它的所有细节都必须如何转换结果。这样保护了一步,您不需要创建/维护额外的构建器对象。 MyContents现在已经成为了一个Builder。
interface VolumeStrategy {
Double calculateVolume(Double density);
}
class SmallVolumeStrategy implements VolumeStrategy {
public Double calculateVolume(Double density) {
return density / 3.0;
}
}
class BigVolumeStrategy implements VolumeStrategy {
public Double calculateVolume(Double density) {
return density;
}
}
class ContainerDirector {
Double myResult( MyContents container ) {
Double density = container.myDensity();
VolumeStrategy strategy = container.myStrategy();
return density * strategy.calculateVolume(density);
}
}
class MyContents {
// built via observer
Double density;
MyContents( Double density ) {
this.density = density;
}
public Double myDensity() {
return density;
}
// plugged in at the end.
VolumeStrategy strategy;
public void setStrategy(VolumeStrategy strategy) {
this.strategy = strategy;
}
public VolumeStrategy myStrategy() {
return strategy;
}
}
public class Test {
public static void main(String[] args) {
// all those can be static
VolumeStrategy smallStrategy = new SmallVolumeStrategy();
VolumeStrategy bigStratetgy = new BigVolumeStrategy();
ContainerDirector director = new ContainerDirector();
// from the GUI
Double density = 15.0;
MyContents contents = new MyContents( density );
// building this contents ...
// ... time to submit, we know what strategy to use
contents.setStrategy(smallStrategy);
// can turn contents into result without needing to know anything about it.
Double results = director.myResult( contents );
System.out.println( "Use this result: " + results );
}
}
这是我认为应该适用于我想象的问题的方式。我可能是错的。