工厂类

Nei*_*etz 51 java oop factory object

就个人而言,我从未理解工厂类的想法,因为直接实例化一个Object似乎更有用.我的问题很简单,在什么情况下使用工厂类模式是最佳选择,出于什么原因,以及良好的工厂类是什么样的?

SWe*_*eko 53

这是我的代码库中真正的实时工厂.它用于生成一个采样器类,它知道如何从一些数据集中采样数据(它最初是在C#中,所以请原谅任何java faux-pas)

class SamplerFactory
{
  private static Hashtable<SamplingType, ISampler> samplers;

  static
  {
    samplers = new Hashtable<SamplingType, ISampler>();
    samplers.put(SamplingType.Scalar, new ScalarSampler());
    samplers.put(SamplingType.Vector, new VectorSampler());
    samplers.put(SamplingType.Array, new ArraySampler());
  }

  public static ISampler GetSampler(SamplingType samplingType)
  {
    if (!samplers.containsKey(samplingType))
      throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
    return samplers.get(samplingType);
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个示例用法:

ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);
Run Code Online (Sandbox Code Playgroud)

如你所见,代码不多,甚至可能更短,更快

ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);
Run Code Online (Sandbox Code Playgroud)

而不是使用工厂.那为什么我甚至打扰?嗯,有两个基本原因,相互依赖:

  1. 首先,它是代码的简单性和可维护性.假设在调用代码中,它enum是作为参数提供的.即如果我有一个需要处理数据的方法,包括采样,我可以写:

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler = SamplerFactory.GetSampler(sampling);
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    
    Run Code Online (Sandbox Code Playgroud)

    而不是一个更麻烦的结构,像这样:

    void ProcessData(Object dataSet, SamplingType sampling)
    {
      //do something with data
      ISampler sampler;
      switch (sampling) {
        case SamplingType.Scalar:  
          sampler= new ScalarSampler();
          break;
        case SamplingType.Vector:  
          sampler= new VectorSampler();
          break;
        case SamplingType.Array:
          sampler= new ArraySampler();
          break;
        default:
          throw new IllegalArgumentException("Invalid sampling type");
      }
      dataSet= sampler.Sample(dataSet);
      //do something other with data
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,每次我需要一些采样时,应该写出这种怪异.你可以想象,如果让我更新一个参数给ScalarSampler构造函数,或者添加一个新参数,那将是多么有趣SamplingType.而这个工厂现在只有三个选项,想象一个有20个实现的工厂.

  2. 其次,它是代码的分离.当我使用工厂时,调用代码不知道或不需要知道一个名为ArraySamplereven 的类.甚至可以在运行时解决该类,并且呼叫站点将更加明智.所以,因此,我可以随意更改ArraySampler类,包括但不限于直接删除类,例如,我决定ScalarSampler应该也用于数组数据.我只需要换行

    samplers.put(SamplingType.Array, new ArraySampler());
    
    Run Code Online (Sandbox Code Playgroud)

    samplers.put(SamplingType.Array, new ScalarSampler());
    
    Run Code Online (Sandbox Code Playgroud)

    它会神奇地工作.我不必在调用类中更改一行代码,这些代码可能有数百个.实际上,工厂使我能够控制采样的内容和方式,并且任何采样更改都可以有效地封装在与系统其余部分连接的单个工厂类中.

  • +1感谢真实的例子.从"非书"例子中理解它将非常有用. (3认同)

Thi*_*ilo 26

这里的想法是关注点的分离:如果使用该对象的代码也有足够的信息来实例化它,那么您不需要工厂.但是,如果涉及某些逻辑或配置,您不希望让API用户考虑(或搞乱),您可以在工厂中隐藏所有这些(并将其封装以便重复使用).

以下是一个示例:您想要访问Google App Engine提供的其中一项服务.相同的代码应该在开发环境(其中有两个版本,主从和高可用性)和完全不同的本地开发环境中工作.谷歌不想告诉你他们内部基础设施的内部运作情况,你真的不想知道.所以他们所做的是提供接口和工厂(以及工厂可以选择的那些接口的几个实现,你甚至不需要知道).


Nik*_*sov 5

来自 Joshua Bloch 的 Effective Java 一书,由我部分重写:

1)与构造函数不同,静态工厂方法 ( SFM ) 有名称。

public static ComplexNumber one () {
    return new ComplexNumber(1, 0);
}

public static ComplexNumber imgOne () {
    return new ComplexNumber(0, 1);
}

public static ComplexNumber zero () {
    return new ComplexNumber(0, 0);
}
Run Code Online (Sandbox Code Playgroud)

2)不需要每次SFM被调用时都创建一个新对象

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE; 
}
Run Code Online (Sandbox Code Playgroud)

3)SFM可以返回其返回类型的任何子类型的对象。

4)SFM减少创建参数化类型实例的冗长。

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}

Map<String, List<String>> m = HashMap.newInstance();
Run Code Online (Sandbox Code Playgroud)