14

我正在尝试了解工厂模式。如果有很多实现,那么我的工厂模式将有很多 if else 或 switch case。而且每次我介绍一个新的实现时,我都应该改变我的工厂代码

就像下面的例子一样,如果让我们假设狗鸭正在实现 Pet 接口,就像明天如果许多动物实现宠物接口,我的工厂会因为很多 if else if 代码或 switch 案例而皱眉。有没有办法通过引入更多动态方法来解决这个问题?

package com.javapapers.sample.designpattern.factorymethod;

//Factory method pattern implementation that instantiates objects based on logic
public class PetFactory {

    public Pet getPet(String petType) {
        Pet pet = null;

        // based on logic factory instantiates an object
        if ("bark".equals(petType))
            pet = new Dog();
        else if ("quack".equals(petType))
            pet = new Duck();
        return pet;
    }

如果动物长大

if ("bark".equals(petType))
    pet = new Dog();
else if ("quack".equals(petType))
    pet = new Duck();
else if ("mno".equals(petType))
    pet = new MNO();
else if ("jkl".equals(petType))
    pet = new JKL();
else if ("ghi".equals(petType))
    pet = new GHI();
else if ("def".equals(petType))
    pet = new DEF();
......
else if ("abc".equals(petType))
    pet = new ABC();
return pet
4

5 回答 5

23

我认为有一个动态的方法:

  1. 在您的工厂中,您需要一个Map<String, Class<? extends Pet>>
  2. 在扩展 Pet 的每个类的静态构造函数中,将其注册到这样的映射中。
  3. 比创建一个类更简单map.get(pet).newInstance(当然,你必须检查空值)
于 2013-08-14T15:46:56.210 回答
11

工厂模式背后的想法是让您动态地实例化您在设计时不一定知道其类型的对象。

拥有一个大块if会破坏这个目的。

实现这种模式的有效方法是为每种类型也有一个工厂,它实现了一个基本的工厂接口并能够实例化该类型的新对象(顺便说一下,在 Java 中,内置Class就是一个例子这样的工厂)。

然后你注册一个名称/ID/等的地图。在运行时这些单个工厂的实例。当需要实例化其中一种类型时,您可以按名称在映射中查找工厂并使用它来实例化该类型的新对象。

你如何在地图上注册各个工厂完全是未知数。您可以显式注册一些,您可以扫描配置文件等。

本质上,您想用if在运行时动态创建的地图替换您的块。

您甚至不需要单独使用预先注册的“地图” - 有时可能适合找出如何动态创建具有给定名称的对象,或者两者的组合(例如Class.forName(),如果它搜索类路径找不到已加载的类)。关键是名称到类类型的转换可以在基本工厂实际上不知道类类型是什么的情况下进行。

值得注意的是,Java 反射已经通过Class.forName()and/or提供了一个非常可行的工厂实现Class.newInstance(),因此如果有意义的话,请考虑使用它而不是重新发明轮子。

于 2013-08-14T15:44:00.640 回答
4

使用反射

public Pet getPet(String petType)
{
     Pet _pet = (Pet)Class.forName(petType).newInstance();
     return _pet;
}

你需要将你的论点从“bark”、“quack”更改为“Dog”和“Duck”等

于 2013-08-14T15:55:12.933 回答
1

自从我遇到类似的问题以来,我一直在努力解决这个问题,最后我提出了一个基于反射库的解决方案(注意反射中的最后一个 S!)

如果您的所有宠物子类都具有可用于区分它们的属性,则它可以应用于您的问题,例如

   public String petType;

您工厂的方法可能如下:

        public static Pet getPet(String _petType) {
    String packageName = "your.package.with.pet.classes";

    Reflections reflections = new Reflections(packageName);

    Set<Class<? extends Pet>> allPets = reflections
            .getSubTypesOf(Pet.class);

    Iterator<Class<? extends Pet>> it = allPets.iterator();

    while (it.hasNext()) {
        try {
            Pet pet = it.next().newInstance();
            if (pet.petType.equals(_petType))
                return pet;
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    System.out.println("Pet " + _petType
            + " not yet implemented in package " + packageName);
    return null;
}

如果定义了新的 Pets,此方法将不受影响。

优点:

  • 它不需要对 Pet 子类进行进一步修改,也不需要在 Factory 维护的 Map 结构上进行任何类型的初始化/注册

  • 它比基于 Java Reflection API 的解决方案更通用,因为您可以根据某些属性而不是类名来区分 Pet 子类

缺点:

  • 它创建所有 Pet 子类的本地实例以找到合适的子类。在这方面,我相信这种方法可以改进。
于 2014-09-03T16:12:10.927 回答
1

在 Java8 中,有一个 Supplier 接口可以很好地支持这一点。可以避免反射和手动调用,并且可以使用更简洁的方法。

像这样的工厂:

public class DynamicSupplierTierFactory {

    public static final Map<String, Supplier<? extends Tier>> registeredSuppliers = new HashMap<>();

    static {
        registeredSuppliers.put("blue", new BlueSupplier());
        registeredSuppliers.put("silver", new SilverSupplier());
        registeredSuppliers.put("golden", new GoldenSupplier());
    }

    public static void registerSupplier(String type, Supplier<? extends Tier> supplier){
        registeredSuppliers.put(type, supplier);
    }

    public static Tier getTier(String type){
        Supplier<? extends Tier> supplier = registeredSuppliers.get(type);
        return supplier != null ? supplier.get():null;
    }
}

供应商将是:

public class BlueSupplier implements Supplier<Tier> {
    @Override
    public Tier get() {
        return new Blue();
    }
}

这可以在这里更详细地看到:https ://medium.com/@mhd.durrah/factory-pattern-the-dynamic-way-with-java-8-3ca5ab48a9cf

于 2019-05-02T11:57:52.510 回答