【问题标题】:How to Reverse Dependency for Class Factory moved to a Library如何将类工厂的依赖关系反向移动到库
【发布时间】:2024-05-01 06:50:03
【问题描述】:

当所有涉及的类都在同一个项目中(determineSubClassBaseClass 的成员)时,以下代码运行良好:

protected static BaseClass determineSubClass(String p1, int p2, Boolean p3) {

    BaseClass baseObj = null;
    if ( (baseObj = SubClassOne.ofType(p1, p2, p3)) != null )
      return baseObj;
    else if ( (baseObj = SubClassTwo.ofType(p1, p2, p3)) != null )
      return baseObj;
    else if ( (baseObj = SubClassThree.ofType(p1, p2, p3)) != null )
      return baseObj;
    else if ( (baseObj = SubClassFour.ofType(p1, p2, p3)) != null )
      return baseObj;
    else
      return new SubClassDefault(p1, p2, p3);
}

但是现在,我想将BaseClass 移动到共享库项目中,其中SubClassOneSubClassTwoSubClassThreeSubClassFour 在库中没有定义而是在使用这个库的应用程序中。

我当然可以将BaseClass 移回使用该库的每个应用程序,但我想知道:

  • 有更好的解决方案吗?
  • 有没有一种解决方案可以让我 将BaseClass 保存在库中 项目并消除对它的需要 了解所有超类 来源于它?

编辑(回答下面的@ahmet alp balkan 问题):

每个子类的ofType() 做了两件事:

  1. 根据内容确定 字符串 p1 和其他参数 p2 和 p3,是否为子类 被实例化是 its 类型。
  2. 如果答案是肯定的,它 实例化一个 self 的对象 子类。否则,返回 null。

至于你的第二个问题,BaseClass 在这一点上拥有所有子类的公共数据成员和方法,并且只有这个单一的静态方法,旨在委派确定要实例化的子类的责任。

顺便说一句,多亏了你的问题,我注意到我原来的帖子中有一个可怕的错字:“SuperClassOne”应该是“SubClassOne”等等。

【问题讨论】:

  • ofType 方法是做什么的,你能给我们BaseClass 的定义吗?我无法在此处了解您想要实现的目标。
  • @ahmet alp balkan 请参阅上面的编辑。感谢并 +1 指出我的问题中不清楚的部分。

标签: java dependency-injection dependency-management library-project class-factory


【解决方案1】:

您的静态determineSubClass 方法是工厂方法。它显然不应该位于BaseClass 上,因为不仅基类不应该对子类一无所知,而且在您的情况下,它也不知道它的任何信息,因为您想在另一个项目中找到基类.不,这个方法应该位于负责创建BaseClass 实例的工厂类中。你应该做的是在BaseType 旁边定义一个用于创建BaseClass 实例的接口(或基本类型),并在你的应用程序的composition root 中定义一个实现。当您有多个应用程序时,它们可能每个都有一组不同的BaseClass 子类型,因此每个应用程序都有不同的工厂。当你有了这个构造后,你可以将工厂注入到需要BaseClass 实例的类中。

它可能看起来像这样:

// Shared library
public interface IBaseClassFactory
{
    BaseClass CreateNew(String p1, int p2, Boolean p3);
}

public abstract class BaseClass
{
}

// Application code
public class SubClassOne : BaseClass
{
}

public class SubClassTwo : BaseClass
{
}

// Note that this consumer depends on IBaseClassFactory.
public class SomeConsumer
{
    private IBaseClassFactory baseClassFactory;

    public SomeConsumer(IBaseClassFactory factory)
    {
        this.baseClassFactory = factory;
    }

    public void Consume()
    {
        BaseClass instance = this.baseClassFactory
            .CreateNew("foo", 0, false);

        // use instance
    }
}  

// Composition root
class BaseClassFactory : IBaseClassFactory
{
    public BaseClass CreateNew(String p1, int p2, Boolean p3)
    {
        BaseClass baseObj = null;

        if ((baseObj = SubClassOne.ofType(p1, p2, p3)) != null)
           return baseObj;
        // etc
        else
            return new SubClassDefault(p1, p2, p3);
    }
}

【讨论】:

  • 感谢您提供清晰详细的回答。除了SomeConsumer 必须驻留在库中之外,这几乎是完美的。另外,谁用factory 对象调用SomeConsumer 的构造函数?即 factory 对象是在哪里以及如何创建的?对上述内容有何建议修改?
  • IOW,这是先有鸡还是先有蛋的情况:SomeConsumer 依赖于 实例 BaseClassFactory,但在我的情况下,SomeConsumer 必须驻留在库中。我该如何解决这个问题?
  • @ef2011: No chicken & egg here: SomeConsumer 取决于将在共享库中定义的 IBaseClassFactory 抽象,而不是在组合根中定义的 BaseClassFactory 实现.仔细看看我的例子,看看BaseClassFactory是如何继承/实现IBaseClassFactory的。
【解决方案2】:

基类知道它的超类不是一个好习惯。它违反了大约一半的 OO 原则;).....

我会将该方法移到一个名为 HierarchyManager 或类似的新类中,并在那里拥有该方法。你甚至可以在那里建立一些层次结构——>你可以有效地使这个方法“可扩展”......

例如在图书馆你可以有:

BaseClass -> A, B (A, B 子类化 BaseClass) 还有一些 LibraryHierachyManager 处理这三个类...

然后在应用程序中使用它:

C、D(继承 BaseClass 或 A 或 B)

还有一些 ApplicationHeararchyManager 在做:

public static BaseClass determineSubClass(String p1, int p2, Boolean p3) {
    if (baseObj = C.ofType(.....) { 
    ....


    } else {
      return LibraryHierarchyManager.determineSubClass(p1,p2, p3);
    }
}

【讨论】:

  • 感谢您的回答。我不确定我是否理解它。如果必须至少有一个类知道所有子类,那么它的名称实际上是次要的。我真正需要知道的是这个管理器类应该放在哪里:如果在库中,那么它违反了相同的 OO 原则,因为库不需要“知道”所有将使用它的应用程序。恕我直言,需要一种“注册”新子类的方法。这样做的正确方法是什么?
  • 我的建议是将它放在单独的类中,甚至可能有 2 个 - 一个在库中,另一个在主应用程序中(库一个会知道库中的所有子类,而应用程序会知道所有子类在应用程序中)。这正是与众不同的地方——拥有单独的类而不是将其保留在基类中——因为仅仅拥有单独的类就可以将其分离出来并放入应用程序中。这就像有一个单独的工厂类(我相信这实际上是你的情况 - 该类应该命名为 SomethingFactory 因为它是工厂模式。
  • Potiuk 感谢 + 1 澄清这一点。
最近更新 更多