Использование отражения в шаблоне factory

Хорошо ли использовать Reflection в шаблоне Factory?

public class MyObjectFactory{
private Party party;

public Party getObject(String fullyqualifiedPath)
{
  Class c = Class.forName(fullyqualifiedPath);
  party = (PersonalParty)c.newInstance();
  return party;
}
}

PersonalParty реализует Party

Ответ 1

Цель шаблона factory состоит в том, чтобы отключить некоторый код из типа времени выполнения объекта, который он потребляет:

// This code doesn't need to know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`
AbstractParty myParty = new PartyFactory().create(...);

Используя такой код, PartyFactory несет исключительную ответственность за определение или точное знание того, какой тип времени выполнения должен использоваться.

Вы отказываетесь от этого преимущества, передавая полное имя класса, в котором вы нуждаетесь. Как это...

// This code obviously DOES know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`.
// Now only the compiler doesn't know or enforce that relationship.
AbstractParty myParty = new PartyFactory().create("com.example.parties.SurpriseParty");

... любое отличие от простого объявления myParty как типа com.example.parties.SurpriseParty? В конце ваш код так же связан, но вы отказались от проверки статического типа. Это означает, что вы получаете меньше, чем никакой выгоды, в то же время отдавая некоторые преимущества Java, которые строго типизированы. Если вы удалите com.example.parties.SurpriseParty, ваш код все равно будет компилироваться, ваша среда IDE не даст вам никаких сообщений об ошибках, и вы не поймете, что между этим кодом и com.example.parties.SurpriseParty была связь между ними и до тех пор, пока время выполнения - это плохо.

По крайней мере, я бы посоветовал вам хотя бы изменить этот код, поэтому аргумент метода - это простое имя класса, а не полное имя:

// I took the liberty of renaming this class and it only method
public class MyPartyFactory{

    public Party create(String name)
    {
      //TODO: sanitize `name` - check it contains no `.` characters
      Class c = Class.forName("com.example.parties."+name);
      // I'm going to take for granted that I don't have to explain how or why `party` shouldn't be an instance variable.
      Party party = (PersonalParty)c.newInstance();
      return party;
    }
}

Далее: неудобно ли использовать Class.forName(...)? Это зависит от того, что является альтернативой, и отношения между этими аргументами String (name) и классами, которые этот factory предоставит. Если альтернатива является большой условной:

if("SurpriseParty".equals(name) {
    return new com.example.parties.SurpriseParty();
}
else if("GoodbyeParty".equals(name)) {
    return new com.example.parties.GoodbyeParty();
}
else if("PartyOfFive".equals(name)) {
    return new com.example.parties.PartyOfFive();
}
else if(/* ... */) {
    // ...
}
// etc, etc etc

... не масштабируемый. Поскольку существует очевидная наблюдаемая связь между именами типов времени выполнения, создаваемых этим factory и значением аргумента name, вы должны использовать вместо этого Class.forName. Таким образом, ваш объект Factory защищен от необходимости изменения кода каждый раз, когда вы добавляете в систему новый тип Party.


Что-то еще, что вы могли бы подумать, вместо этого использовать AbstractFactory. Если ваш код потребления выглядит следующим образом:

AbstractParty sParty = new PartyFactory().create("SurpriseParty");
AbstractParty gbParty = new PartyFactory().create("GoodByeParty");

... где существует ограниченное количество часто встречающихся типов партий, которые запрашиваются, вы должны рассмотреть возможность использования разных методов для разных сторон:

public class PartyFactory {

    public Party getSurpriseParty() { ... }
    public Party getGoodByeParty() { ... }

}

..., который позволит вам использовать статическую типизацию Java.

Это решение, однако, означает, что каждый раз, когда вы добавляете новый тип Party, вы должны изменить объект factory, поэтому, если рефлексивное решение или AbstractFactory является лучшим решением, это действительно зависит от того, как часто и как быстро вы будете добавлять типы Party. Новый тип каждый день? Используйте отражение. Новый тип партии каждые десять лет? Используйте AbstractFactory.

Ответ 2

Использование отражения таким образом (Class.forName) почти всегда является признаком плохого дизайна приложения. Есть несколько видов, где его использование в порядке, например, если вы выполняете динамическую нагрузку внешних библиотек или плагинов.

Ответ 3

Вы можете использовать его для API, где вы предоставляете API и конфигурационный файл XML, где пользователи могут добавлять имена классов своего плагина. Тогда, да, вы можете использовать этот