java - 这是 Java 中构建器模式的有效使用(甚至是良好的 OO 设计)吗?

标签 java design-patterns

作为面向对象的新手,我常常觉得自己理解了一个概念,直到我尝试从一个简化的示例转向给出的实际需求。如果您能帮助我理解如何思考这个特定问题,我将不胜感激。

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

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

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

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

我正在接近可用的面向对象设计吗?我不确定我是否只是将特殊外壳埋得更深一点。

又一个皱纹。其中一种元素类型位于所有三个容器中。现在,我的可观察对象分别跟踪容器和项目,创建图表的方法决定要求什么(当用户使用这些值时,图表会发生很大变化)。如果我有多个构建器模式,那会如何工作?

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

欢迎所有能帮助我了解 OO 设计或特别是这个问题的评论。实际需求有更多特殊情况,但都是这个基本主题的变体。

感谢您的回复。我认为我把两个问题混在一起是有罪的。这里尝试提供一个专注于构建器模式的最小代码示例。注意 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 会将项目放入正确的容器中并将其保存到我的可观察对象中(实际上还有很多其他内容已设置),它告诉我的观察者使用导向器来获取适当的值,然后观察者会更新GUI 的一些 View 组件。

最佳答案

My containers have a size, but sometimes it is entered explicitly and sometimes it is determined by what the container is holding. That is determined by the type of container. [...] if not entered explicitly, depends on whether one of the optional items is in the container.

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

... and I use a bunch of switch statements to handle the special cases.

听起来不太好。 Replace Conditional with Polymorphism如果适用。

I am thinking that I could use the builder pattern...

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

Right now, my observable keeps track of the container and items separately[...] observable updates the builder of the current container[...] How's that work if I have multiple builder patterns?

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

Am I getting close to a usable OO design?

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

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


你的方法看起来不错,但它需要 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 现在在某种程度上已经是一个构建器了。

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 );
    }
}

我认为这种方式应该可以很好地解决我想象的您遇到的问题。我可能会犯错。

关于java - 这是 Java 中构建器模式的有效使用(甚至是良好的 OO 设计)吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13463517/

相关文章:

java - 助手 : marven libraries can't be downloaded

java - JPA的Hibernate实现

java - 从源代码设置 Liferay Portal 6.0

java - 重写 Crenshaw 的 "let' 构建编译器的设计模式”?

java - 正确设计 Java 类层次结构以实现代码共享和封装

java - 从 msgraph api 下载 csv 报告

java - Apache Commons Logging 的编程配置

C# "using" block

c# - 灵活使用对象包装器类

c# - 静态类与类的实例