【问题标题】:OO Design issue interfaces and classesOO 设计问题接口和类
【发布时间】:2015-08-05 19:24:01
【问题描述】:

我现在正在纸上设计一个应用程序,而且我的设计有点小题大做。

该应用程序的目标是我的汽车工厂可以为我提供他们汽车模型的蓝图。蓝图不是汽车,但它描述了可以用它制造的最终汽车,例如型号名称、车辆上的选项(如空调等)。

为此,我创建了一个接口ICarBlueprint。例如,此接口上的方法可以包括getModelName()getOptionList()。这些方法必须适用于汽车蓝图的每个实例,无论是欧宝还是大众汽车蓝图。

一家工厂,例如欧宝或大众,可以提供一份他们的汽车蓝图清单。因此,我为工厂创建了一个接口ICarFactory。此接口包含方法getAllBlueprints(),它返回ListICarBluePrint 对象。对于欧宝工厂,这将是OpelBlueprint 实例,它实现了接口ICarBlueprint

在我的主程序中,我想获取用户可以从中选择的所有可能的汽车蓝图。一个例子可能是:

for(ICarFactory f : factories)
    allblueprints.addAll(f.getAllBlueprints());

然后我想遍历该列表,如果其中一个蓝图是我想要的汽车,我想建造它。但是,汽车蓝图只能由生成该蓝图的工厂建造。所以欧宝汽车的蓝图只能由OpelFactory的实例构建,从而实现接口ICarFactory

建造汽车会产生一个实际的汽车对象。为此,我创建了接口ICar。同样,对于每个工厂,我都会有实现此接口的类。

我遇到的设计问题从这里开始。

要构建蓝图,我可以将名为 build() 的方法添加到 ICarFactory 接口。然后主程序可以简单地选择一个汽车实例,例如 goodcarBlueprint 并要求汽车工厂建造它。

因此,为了确保蓝图是由正确的工厂构建的,我正在考虑将方法 getFactory() 添加到接口 ICarBlueprintbuild()ICarFactory 接口。这将允许我在其各自的工厂中构建蓝图,如下所示:

ICarBlueprint goodcarBlueprint = // Assigned somewhere else
ICar x = goodcarBlueprint.getFactory().build(goodcarBluePrint);

但是,我的直觉告诉我这个设计真的很糟糕。任何有关正确设计方法的帮助表示赞赏。

【问题讨论】:

  • 您最初使用 Factory 时,听起来像是“工厂模式”,但后来听起来像是“汽车工厂”,这些是不同的概念。你打算在这里使用哪个概念?
  • 总结起来,ICarBlueprint 将因此需要对其 ICarFactory 的引用。你这样做的方式是好的。另一种方法是让所有 ICarBlueprints 成为它们来自的工厂的内部类。这样,您可以调用 ICarBlueprint.createCar() 并且 ICarBlueprint 实现将能够使用正确的工厂
  • xenoterracide 所说的,我同意你将现实工厂的概念和编程范式混为一谈
  • @xenoterracide 我确实在这里使用工厂/公司的意义。另外,为什么两票接近?这个问题有什么不好?
  • 任何人给你的答案都将主要基于意见,codereview.stackexchange 和programmers.stackexchange 可能是更好的论坛。

标签: java design-patterns interface


【解决方案1】:

这是非常主观的,我理解糟糕设计的感觉,但我不会称你的想法“非常糟糕”。

对我来说,这听起来像是经典的 Builder design pattern。在这种情况下,蓝图是 Builder。大多数 Builders 都有一个 .build() 方法。通过在蓝图上调用 .build() ,您将避免必须从工厂检索蓝图而只需使用它给您的蓝图再次调用工厂。蓝图仍然需要了解自己的工厂,才能让工厂制造汽车。

【讨论】:

    【解决方案2】:

    我要避免的是ICarBlueprintICarFactory 知道彼此。这种紧密的耦合会使未来的变化复杂化,你可能有多个工厂可以建造相同的汽车蓝图。松散耦合开辟了可能性。

    public interface ICarBuilder {
        ICarBlueprint getBlueprint();
        ICar build();
    }
    
    public class CarBuilder implements ICarBuilder {
        private final ICarBlueprint blueprint;
        private final Collection<ICarFactory> factories = new ArrayList<ICarFactory>();
    
        public CarBuilder(ICarBlueprint blueprint) {
            this.blueprint = blueprint;
        }
    
        public ICarBlueprint getBlueprint() {
            return blueprint;
        }
    
        public CarBuilder addFactory(ICarFactory factory) {
            factories.add(factory);
            return this;
        }
    
        public ICar build() {
            // Choose least busy or least cost factory, etc. from factories.
            ICarFactory factory = ...;
            return factory.build(blueprint);
        }
    }
    

    现在您可以创建ICarBuilder 的集合并向用户展示蓝图,然后让CarBuilder 决定使用哪个工厂来制造选定的汽车。您甚至可以添加逻辑以根据交付时间和成本向用户显示工厂选项,并让他们选择。

    【讨论】:

    • 这有几个问题:1) getBlueprint() 可能会违反 LoD。 2) addFactory 不是通用的,因此您可以将大众工厂添加到沃尔沃蓝图中。 3) 你还没有移除 ICarFactory 和 ICarBlueprint 的耦合。你还是调用 factory.build(blueprint)
    • @michaelsnowden 感谢您提供代码编辑建议。由于ICarBlueprint 不提供服务,getBlueprint 只是一个属性访问器,不会违反 LoD AFAIK。关于工厂/蓝图之间的阻抗问题的好点。我消除了知道工厂的蓝图,只有工厂知道蓝图。制造商特定的构建者可以在addFactory 中进行验证。
    【解决方案3】:

    我认为你的设计很好。行:

    ICar x = goodcarBlueprint.getFactory().build(goodcarBluePrint);
    

    有点冗长,您可能不喜欢 goodcarBluePrintFactory 出现两次的事实。有一个简单的解决方案,保留你的设计并将这个静态辅助函数添加到 ICarFactory :

    public static ICar build( ICarBlueprint blueprint) {
      return blueprint.getFactory().build(blueprint);
    }
    

    当您想根据蓝图制造汽车时,您只需:

    ICar x = ICarFactory.build(goodcarBluePrint);
    

    或者您可以改为向 ICarBlueprint 添加构建方法:

    public Icar build() {
      getFactory().build(this);
    }    
    

    您将用作:

    ICar x = goodcarBlueprint.build();
    

    我觉得我更喜欢最后一个。

    【讨论】:

      【解决方案4】:

      好的,这是我的 tuppence 价值,可能是你违反了得墨忒耳法则。

      这种设计的复杂性是由于两个要求。 1. 蓝图必须知道它属于哪个工厂。您的设计中有一个公共方法 getFactory。蓝图的客户需要了解整个工厂吗?我们可以在蓝图中添加一个 build 方法,在内部调用工厂的 build,这样可以改善信息隐藏和封装。

      ICar x = goodcarBlueprint.getFactory().build(goodcarBluePrint);
      

      减少到

      ICar x = goodcarBlueprint.build();
      

      因为我们在蓝图类中有这个额外的方法

          public void Build()
          {
              this.factory.Build(this.BlueprintId);
          }
      

      只提供所需的信息而不提供更多信息总是好的;你不会把钱包给收银员,你给他们的只是他们需要的钱。但是,如果您确实希望应用程序的其他部分使用整个工厂,那么您的原始设计很好,但暴露它可能会产生额外的依赖关系,并且以后很难更改工厂实现,因为客户将开始依赖它.

      提到的另一个约束是 - 2. 每个蓝图只能由一种类型的工厂使用。我不确定你想限制多少,但如果你这样做......你可以从每个工厂返回一个 IBlueprints 列表,但只接受工厂的特定类型的蓝图。

      class OpelFactory : IFactory
      {
          public IBluePrints GetBluePrints()
          {
              return this.bluePrints;
          }
      
          public void AddBluePrint(IOpelBluePrint bluePrint) //1. we can only add opel blueprints into the opel factory
          {
              this.bluePrints.Add(bluePrint);
          }
      

      【讨论】:

        猜你喜欢
        • 2011-01-16
        • 1970-01-01
        • 2011-11-04
        • 1970-01-01
        • 2014-03-21
        • 2010-11-04
        • 2010-09-10
        • 1970-01-01
        相关资源
        最近更新 更多