2014-12-04 2 views
1

У меня есть PluginClassLoader, который является абстрактным классом, который обеспечивает 99% функциональности для загрузчиков классов моего проекта. У меня есть 2 подкласса (ServiceClassLoader и ChannelClassLoader), которые простираются на PluginClassLoader и представляют собой немного больше, чем обертки и некоторые настраиваемые протоколирования. 99% логики одинаковы между двумя реализациями.Отделить абстрактные данные класса от расширенных реализаций

Я тогда PluginManager, который также является абстрактным классом, который имеет 2 реализаций, которые расширяют его (ServiceManager и ChannelManager), которые являются оболочками плюс индивидуальные каротаж и более удобный конструктор.

Проблема, с которой я столкнулся, в моем PluginManager должна быть способна создать экземпляр нового типа загрузчика класса либо ServiceClassLoader, либо ChannelClassLoader. Я пытаюсь избежать привязки своего PluginManager к его текущим реализациям (т. Е. Я хочу, чтобы гибкость в возможности добавлять будущие реализации, но не изменяла логику PluginManager), поэтому старайтесь избегать прохождения в каком-то Enum и используя некоторые из них:

if (classLoaderType instanceof ClassLoaderType.SERVICE) { 
    // do logic for instantiating ServiceClassLoader 
} 

Пример иерархии классов:

public abstract class PluginManager { 
    // logic for managing plugins and when to load them 
    // ... 
    // somewhere deep in a loadPlugin(final File directory) method 
    pluginLoader = new PluginClassLoader(); // <-- not valid, can't instantiate 
              //   an abstract class, 
              //   and it's of the wrong type! 
} 

public abstract class PluginClassLoader extends URLClassLoader { 
    // class loader logic 
} 

public class ServiceManager extends PluginManager { 
    // wrapper for PluginManager with some customized logging 
} 

public class ServiceClassLoader extends PluginClassLoader { 
    // wrapper for PluginClassLoader with some customized logging 
} 

Попытка избежать делать что-то вроде:

public abstract class PluginManager { 

    private final PluginType pluginType; 

    public PluginManager(final PluginType pluginType) { 
     this.pluginType = pluginType; 
    } 

    // logic ... 

    // somewhere deep in the loadPlugin(final File directory) method 
    if (pluginType instanceof PluginType.SERVICE) { 
     pluginLoader = new ServiceClassLoader(); 
     // more logic 
    } else if (plugintype instanceof PluginType.CHANNEL) { 
     pluginLoader = new ChannelClassLoader(); 
     // more logic 
    } 
} 
+1

Мне непонятно, что нужно для определения загрузчика классов.Если ServiceManager всегда создает ServiceClassLoader, а в каком-то другом подклассе PluginManager всегда используется PluginClassLoader? –

+0

@JonSkeet да именно это. Я пытаюсь избежать переопределения метода loadPlugin() для каждого менеджера, поскольку 99% деталей идентичны, это действительно только эта строка, которая будет отличаться. – SnakeDoc

ответ

1

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

public abstract class PluginManager { 
    public PluginManager() { 
     pluginLoader = MakeClassLoader(); 
    } 
    ... 
    protected abstract PluginClassLoader MakeClassLoader(); 
} 
public class ServiceManager extends PluginManager { 
    ... 
    protected abstract PluginClassLoader MakeClassLoader() { 
     return new ServiceClassLoader(); 
    } 
} 
public class ChannelManager extends PluginManager { 
    ... 
    protected abstract PluginClassLoader MakeClassLoader() { 
     return new ChannelClassLoader(); 
    } 
} 

Это реализует паттерн Factory Method дизайна.

2

Вам не хватает удивительной гибкости enum, они полностью выдувают объекты сами по себе и поэтому могут реализовывать интерфейсы. Если вы сделаете enum само способным функционировать по мере того как фабрика все станет просто.

Вот очень упрощенная демонстрация использования enum в качестве фабрики.

interface Loader { 

    public Plugin load(); 
} 

enum PluginType implements Loader { 

    Service { 

       @Override 
       public Plugin load() { 
        return new ServiceClassLoader(); 
       } 
      }, 
    Channel { 

       @Override 
       public Plugin load() { 
        return new ChannelClassLoader(); 
       } 
      }; 
} 

public void loadPlugin(PluginType type) { 
    Plugin plugin = type.load(); 
} 

public void test() { 
    loadPlugin(PluginType.Channel); 
} 
2

Три варианта:

  • Объявите абстрактный newClassLoader() метод PluginManager, который переписан в ServiceManager вернуть новый ServiceClassLoader и т.д.
  • Изменить тип параметра из PluginType в Class<? extends ClassLoader>, храните его в поле (например, classLoaderClass), а затем просто позвоните classLoader.newInstance(), когда вам нужно
  • Ma ke PluginType перечисление (если оно еще не), у которого есть свой собственный метод для создания нового ClassLoader.

(Это не ясно, нужно ли вам PluginType по другим причинам - если вы этого не сделаете, то его нет.)

Смежные вопросы