Java对象创建模式和设计

时间:2015-06-18 16:04:57

标签: java object factory

我最近开始研究一个Java项目,该项目已经拥有一个由3个月以上的团队开发的相当大的代码库。我注意到在很多地方,一些对象直接在客户端对象的构造函数中实例化,而不是使用依赖注入。我想将对象构造重构为工厂并使用一些注入框架。

我创建了一个工厂,基本上是一个工作new <type(some params here)>的工作室。这里没什么好看的 - 没有单身,没有静态的工厂模式。只是一个newInstance()方法,它返回一个新的依赖实例。

在代码中显示内容:

class A {   A() {  
       B bobj = new B();  // A and B are coupled directly
    } 
}

我想将此重构为:

BFactory {
    newInstance() {  return new B(); // return B implementation  }
}

 class A {
   A(BFactory factory){  
     B bobj = factory.newInstance(); // A does not know about B impl
  }
}

我的论点是不应该在代码中的任何地方创建对象,除非在为此目的的工厂中。这促进松耦合,否则你紧紧地耦合这两种类型。一位高级成员(我试图重构的代码的作者)认为,一个班轮工厂是一个过于复杂的设计。

是否有关于此问题的模式的权威建议/参考?可以用什么来决定哪种方法更好,为什么更好呢?

2 个答案:

答案 0 :(得分:1)

  

一位高级会员(我试图重构的代码的作者)认为,一家班轮工厂是一个过于复杂的设计。

这看起来像你问题的关键,而不是你是否应该重构代码,所以让我们回答它而不是偏离实际的问题。如果我们考虑您在代码中提供的示例,我同意您的同事。您不应该为要注入的每个依赖项创建工厂类。你试图实现的目标没有任何问题,但是你试图实现它的方式是一种过度杀伤。

您要么依赖于Factory类的层次结构,这些类知道如何创建每个依赖项,要么依赖于实际的类本身,并且有一个Container可以将对象连接在一起。

选项1:取决于常见工厂

class A {
   B bobj;
   C cobj;
   A(Factory factory){  
     bobj = factory.createB(); 
     cobj = factory.createC();
   }
}

选项2:直接依赖于依赖

class A {
   B bobj;
   C cobj;
   A(A a,B b) {  
      this.bobj = b;
      this.cobj = c
   }
}

然后,您可以创建一个知道如何将对象连接在一起的Container类:

class Container {
    public static B createB() {
        return new BImpl();
    }

    public static C createC() {
         return new CImpl();
    }

    public static A createA() {
        return newAImpl(createB(),createC());
    }
}

上面提到的例子太基础了。在现实世界中,您将主要拥有更复杂的依赖关系图。这就是DI框架派上用场而不是重新发明轮子的地方。如果您的最终目标是开始使用DI框架,那么您可以使用选项2,因为DI框架通过向客户端提供依赖关系来实现控制的反转,而不是客户端代码要求它们。

答案 1 :(得分:0)

你的基础点是完全有效的,直接在构造函数中实例化对象通常不是一个好主意(它们可能是该规则的有效例外)。

如果你这样做:

class Car {

   private Engine engine;

   public Car() {
       engine = new Engine();
   }

}

如果没有发动机,您将很难测试汽车。您必须使用反射才能通过模拟交换实例。

如果您执行以下操作

class Car {

   private Engine engine;

   @Inject
   public Car(Engine engine) {
       this.engine = engine;
   }

}

使用假引擎测试汽车非常容易,替换引擎的实现或更改引擎的构造方式(例如,向构造函数添加更多参数)。

但是你绝对应该使用已建立的依赖注入框架而不是编写自己的工厂。如果你传入一个“普通工厂”,Chetan建议你最终隐藏你的依赖。

可以在此处找到使用依赖注入获得更多动力的良好资源: https://github.com/google/guice/wiki/Motivationhttps://youtu.be/acjvKJiOvXw(非常好的谈话,应该值得你的时间)。