2

作为 OO 的新手,我经常觉得我理解了一个概念,直到我尝试从一个简化的示例转移到给我的实际需求。对于理解如何思考这个特定问题的任何帮助,我将不胜感激。

我有一个 GUI,它有一个面板,它定义了一个容器和其中的项目。目前,有三种类型的容器。容器具有一些属性(如大小),可以包含一到三种不同类型的项目(两种是可选的)。一旦输入了足够的信息,我就会使用这些信息来制作图表。

我实现了一个观察者模式。当用户输入信息时,它会更新一个 observable,它会通知图形它已更改。

到目前为止我很高兴。现在皱纹。我的容器有一个大小,但有时它是明确输入的,有时它是由容器所容纳的东西决定的。这取决于容器的类型。如果没有明确输入,如何确定大小取决于可选项目之一是否在容器中。我不确定需求编写者只是讨厌我还是我缺乏足够的 OO 经验,但是这些皱纹让我很不舒服。现在,我的 observable 只有变量来保存所有分类信息,我使用一堆 switch 语句来处理特殊情况。

我在想我可以使用构建器模式。导演将产生被绘制的数据。我将为每种类型的容器都有一个具体的构建器,并且我将使用容器属性和其中的项目来实例化该类。我将使用抽象构建器类的方法将图形所需的值返回给主管,例如 getContainerSize() 并将它们组合起来以生成实际的数据点。此外,如果用户还没有输入足够的数据来完成图表,director 可能会返回 null。

我是否正在接近可用的 OO 设计?我不确定我是否只是将特殊外壳埋得更深一些。

另一种皱纹。其中一种物品类型放在所有三个容器中。现在,我的 observable 分别跟踪容器和项目,创建图表的方法决定了要求的内容(随着用户使用这些值,图表会发生很大变化)。如果我有多个构建器模式,那将如何工作?

也许我错过了一步?observable 更新当前容器的构建器,然后让图知道它应该调用 director 来获取它的坐标?那么哪个还需要询问当前容器是什么?

欢迎所有评论帮助我了解 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 );
    }
}

我有两种类型的容器,它们使用不同的方法来计算体积。因此,假设我有单选按钮来选择容器类型,并且在每个单选按钮下都有一个可以进入所选容器的项目组合框。组合框上的 ActionListener 会将项目放在正确的容器中并将其保存到我的 observable 中(实际上还有很多其他的东西被设置),它告诉我的观察者使用 director 来获取适当的值,然后观察者更新GUI的一些视图组件。

4

1 回答 1

2

我的容器有一个大小,但有时它是明确输入的,有时它是由容器所容纳的东西决定的。那是由容器的类型决定的。[...] 如果没有明确输入,则取决于其中一项可选项目是否在容器中。

听起来您可以拥有抽象容器的不同子类,每个子类都getContainerSize()以不同的方式实现。一种用于明确输入,一种用于有可选项目的情况,另一种用于没有它的情况。

...我使用一堆 switch 语句来处理特殊情况。

听起来不太好。如果适用,将条件替换为多态。

我在想我可以使用构建器模式......

我假设您需要null根据一组输入变量来确定对象(或)的具体类型。该模式提供了一种构建复杂对象的方法,如果它知道这是什么类型,但实际的问题是决定哪种类型。所以你在某个地方需要条件代码。那个地方可以是建造者,但也可以是简单的工厂。

现在,我的 observable 分别跟踪容器和项目 [...] observable 更新当前容器的构建器 [...] 如果我有多个构建器模式,那将如何工作?

并不真正了解 Observable 正在观察什么以及在哪种情况下会触发什么变化,但是 Observable 更新一个构建器(或多个)听起来很奇怪。不过,这更像是一种直觉:)

我是否正在接近可用的 OO 设计?

如果它有效,是的。但实际上我无法告诉您您是否创建了一个好的或可用的设计,因为我仍然不知道您的问题或您的设计的细节 - 在阅读了您的文本几次之后。

现在不要在您的问题中添加另一页信息,而是尝试将您的问题分解成更小的部分,并使用代码片段/图像/图表或任何类型的可视化来帮助人们理解您的问题以及这些部分之间的所有联系。只是大量的文本是相当可怕的,像这样的巨大的 OO 设计对于 SO 来说整体来说太大而且太本地化。


您的方法看起来不错,但它需要 IMO 相当复杂的对象来证明这种使用的合理性。

您通过观察者在您的 GUI 中创建一个 MyContents 实例。然后将该对象包装在 MyContainerBuilder 中,然后将其提供给 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 );
    }
}

这是我认为应该很好地解决我想象中的问题的一种方式。我可能错了。

于 2012-11-20T01:21:00.450 回答