Java 工厂模式动态方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18235877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:36:37  来源:igfitidea点击:

factory pattern dynamic approach

javadesign-patterns

提问by constantlearner

I am trying to understand factory pattern.If there are many implementation then my factory pattern will have lot of if else or switch cases. And also every time I introduce a new implementation i should change my factory code

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

Like in below examples if lets assume dog duck are implementing Pet interface like tomorrow if many animals implement pet interface my factory frows long with lot of if else else if code or switch case. Is there any way to solve this with bringing more dynamic approach?

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

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 the animals grows

如果动物长大

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

采纳答案by Eugene Ryzhikov

I think there is a dynamic approach:

我认为有一种动态方法:

  1. In your factory you need a Map<String, Class<? extends Pet>>
  2. In static constructor of every class, which extends Pet, register it with such map.
  3. Than creating a class will be just map.get(pet).newInstance( you'd have to check for nulls, of course)
  1. 在您的工厂,您需要一个 Map<String, Class<? extends Pet>>
  2. 在扩展 Pet 的每个类的静态构造函数中,将它注册到这样的映射中。
  3. 比创建一个类只是map.get(pet).newInstance(当然,您必须检查空值)

回答by Jason C

The idea behind the factory pattern is to let you dynamically instantiate objects whose types you don't necessarily know about at design time.

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

Having a big ifblock defeats that purpose.

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

The effective way to implement this pattern is to also have a factory for each type, which implements a base factory interface and has the ability to instantiate a new object of that type (by the way, in Java, the built-in Classis an example of such a factory).

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

Then you register a map of names/ids/etc. to instances of these individual factories at runtime. When it's time to instantiate one of the types, you look up the factory in the map by name and use that to instantiate a new object of that type.

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

How you register individual factories in the map is totally up in the air. You could register some explicitly, you could scan a configuration file, etc.

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

Essentially you want to replace your ifblock with a map that is dynamically created at runtime.

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

You don't even need to solely use a preregistered "map" - sometimes it may be appropriate to figure out how to create an object with a given name on the fly, or a combination of the two (e.g. Class.forName()searches the class path if it can't find the class already loaded). The point is the translation of the name to the class type can take place without the base factory actually knowing what the class type is.

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

It's worth noting that Java reflection provides a very workable factory implementation already via Class.forName()and/or Class.newInstance(), so consider using that instead of reinventing the wheel if it makes sense.

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

回答by wonde

use reflection

使用反射

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

you need to change your arguments from 'bark','quack' to 'Dog' and 'Duck' etc

您需要将参数从“bark”、“quack”更改为“Dog”和“Duck”等

回答by Luigi Martin Petrella

I've been banging my head a little on this since I had a similar problem, and finally I came with a solution based on Reflections Library(pay attention to the final S in Reflections!)

自从我遇到类似的问题以来,我一直对此感到头疼,最后我提出了一个基于Reflections Library的解决方案(注意Reflections中的最后一个 S!)

It can be applied to your problem IF all your pet subclasses have an attribute that can be used to discriminate them, for example

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

   public String petType;

The method of your factory could be the following:

您工厂的方法可能如下:

        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;
}

This method would remain unaffected if new Pets were defined.

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

Pros:

优点:

  • It doesn't need further modification to Pet subclasses, neither any sort of initialization/registration on a Map structure maintained by the Factory

  • It's more general than the solution based on Java Reflection API because you can discriminate Pet subclasses upon some attributes instead of the class name

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

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

Cons:

缺点:

  • It creates local instances of all Pet sub-classes to find the appropriate one. On this side, I'm confident that this approach can be improved.
  • 它创建所有 Pet 子类的本地实例以找到合适的子类。在这方面,我相信这种方法可以改进。

回答by Ankit Bhatnagar

In Java8, there's a Supplier interface that supports this quite well. Reflections and manual invocations can be avoided and a cleaner approach can be used.

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

Something like this for Factory:

工厂是这样的:

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;
    }
}

Suppliers will be like:

供应商会像:

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

This can be seen here in more detail: https://medium.com/@mhd.durrah/factory-pattern-the-dynamic-way-with-java-8-3ca5ab48a9cf

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