繁体   English   中英

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

[英]Is this a valid use of the builder pattern in Java (or even good OO design)?

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

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

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

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

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

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

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

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

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

感谢您的回复。 我认为我将两个问题混在一起是有罪的。 这里试图提供一个关注 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(实际上还有很多其他东西可以设置),它告诉我的观察者使用导演来获取适当的值,然后观察者更新GUI 的一些视图组件。

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

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

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

听起来不太好。 如果适用, 将 Conditional 替换为 Polymorphism

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

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

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

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

我是否接近可用的面向对象设计?

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

现在不要在您的问题中添加另一页信息,而是尝试将您的问题分解为更小的部分,并使用代码片段/图像/图表或任何类型的可视化来帮助人们了解您的问题以及这些部分之间的所有联系。 只是很多文本相当可怕,像这样的巨大 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 );
    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM