Вопрос о шаблонах дизайна - PullRequest
4 голосов
/ 27 мая 2011

У меня есть следующая проблема, которую я хочу решить элегантно:

public interface IMyclass
{
} 
public class A
{
   public void Init(IMyclass class){?}
   public IMyclass CreateMyClass(){?}
}

В начале системы я хочу определить динамический тип IMyClass с помощью Init (), а во время работы системы я хотел бы создать новые экземпляры типа, который я определил в init.

Заметки:
1. IMyclass должен быть интерфейсом
2. Динамический тип IMyclass известен только в init (у меня нет конструктора после :))
3. Я мог бы сделать это, используя метод клонирования отражения или определения в IMyclass. Есть ли лучшие решения?

Спасибо.

Ответы [ 4 ]

2 голосов
/ 27 мая 2011

Это своего рода внедрение зависимости , вы должны прочитать:

По сути, у вас есть класс A, который заполняется фабриками (или поставщиками) при инициализации. Тогда вы используете A вместо вызова new.

Быстрый пример:

interface Provider<V> {
   V instance(Object... args);
}
class Dispatch {
   // you can make a singleton out of this class
   Map<Class, Provider> map;
   <T> void register(Class<T> cl, Provider<? extends T> p) {
      // you can also bind to superclasses of cl
      map.put(cl, p);
   }
   <T, I extends T> void register(Class<T> cl, final Class<I> impl) {
      register(cl, new Provider<I>() {
         I instance(Object... args) {
            // this class should be refactored and put in a separate file
            // a constructor with arguments could be found based on types of args values
            // moreover, exceptions should be handled
            return impl.newInstace();
         }
      });
   }
   <T> T instance(Class<T> cl, Object... args) {
      return map.get(cl).instance(args);
   }
}

// usage
interface MyIf { ... }
class MyIfImpl implements MyIf { ... }

Dispatch d = new Dispatch();
d.register(MyIf.class, new Provider<MyIf>() {
   MyIf instance(Object... args) {
      return new MyIfImpl();
   }
});
// or just
d.register(MyIf.class, MyIfImpl.class);
MyIf i = d.instance(MyIf.class);

Edit: добавлено register(Class, Class)

2 голосов
/ 27 мая 2011

Вы можете передать провайдера в class A

public class A
{ 
   IMyClassProvider _provider;

   public void Init(IMyClassProvider provider) 
   {
        _provider = provider; 
   }

   public IMyclass CreateMyClass()
   {
        return _provider.Create();
   }
}

Или, может быть, с делегатом конструктора

public class A
{ 
   Func<IMyclass> _ctor;

   public void Init(Func<IMyclass> ctor) 
   {
        _ctor = ctor; 
   }

   public IMyclass CreateMyClass()
   {
        return _ctor();
   }
}

Обратите внимание, что оба эти примера будут работатьесли бы Init не вызывался раньше CreateMyClass, вам понадобится некоторая проверка или лучше выполнить инициализацию в конструкторе.

Правильно ли я понял вопрос?

1 голос
/ 27 мая 2011

Вам понадобится Reflection в какой-то момент из-за видимости.Если вы можете сразу принять Reflection и не использовать его снова, это, вероятно, было бы идеально, да?

Вы можете поместить метод getInstance() в скрытый интерфейс (расположенный в том же пакете, что иIMyClass, MyClassImpl и A, но не ClientOfA), а затем передайте прототип от MyClassImpl до A.init().

// -- You wish you would have thought of the word prototypeable! ...maybe?
interface IMyClassPrototypeable extends IMyClass
{
 public IMyClass getInstance();
}

class MyClassImpl implements IMyClassPrototypeable // -- and IMyClass by extension.
{
 // -- Still not visible outside this package.
 public IMyClass getInstance()
 {
  return new MyClassImpl();
 }
}

class A
{
 private IMyClassPrototypeable prototype;

 // -- This method is package-private.
 void init( IMyClassPrototypeable prototype )
 {
  this.prototype = prototype;
 }

 public IMyClass createMyClass()
 {
  return prototype.getInstance();
 }
}

Для этого решения потребуется Reflection для созданияэкземпляр прототипа MyClassImpl, который может быть выполнен с помощью Spring (или какой-либо другой формы внедрения зависимости).Он использует шаблон Prototype, шаблон Factory-метода и с готовностью поддерживает шаблон Singleton / Pool, но помните, что использование большего количества шаблонов проектирования не всегда лучше.Фактически, это может сделать дизайн (и код) более сложным и трудным для понимания новичком.

Для справки, единственная причина, по которой я бы даже подумал о защите этого решения, состоит в том, что оно требует отраженияударить один раз, вперед, а не каждый раз, когда вызывается createMyClass(), что на оригинальном плакате указывало, что он / она будет часто.

1 голос
/ 27 мая 2011

Если вы просто хотите создать экземпляр того же класса в CreateMyClass() без дальнейшей настройки, вы можете использовать рефлексию.

public class A
{
    private Class prototype;

    public void Init(IMyClass object) {
        this.prototype = object.getClass();
    }

    public IMyClass CreateMyClass() {
        return prototype.newInstance();
    }
}

Я подозреваю, что вы хотите большего, чем это, и если да, то вам нужно будет объяснитькак вы хотите использовать это.Возможно, вы ищете шаблоны Builder или Factory .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...