Как сохранить фабрику, используемую в производном классе для инициализации его базового класса? - PullRequest
1 голос
/ 25 августа 2009

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

public class Resource : IDisposable {
  public void Dispose() { /* ... */ }
}

public class ResourceManager : IDisposable {
  public Resource Load() { /* create resource and add to list */ }
  public void Dispose() { /* dispose all loaded resources in list */ }
}

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

public class Fooificator {
  public Fooificator(Resource resource) {
    this.resource = resource;
  }
  private Resource resource;
}

Как класс, производный от такого базового класса, может создать Resource без потери ссылки на фабрику?

Наивный подход, использующий статический метод, не позволяет запоминать фабрику (кроме уродливых хаков, таких как хранение ее в статическом поле, которое конструктор может выбрать после завершения построения базового класса, или использования двухэтапной инициализации где Resource предоставляется базовому классу через конструктор, называемый методом Initialize())

public class DefaultFooificator : Fooificator {
  public DefaultFooificator() : base(loadDefaultResource()) {}

  private static Resource loadDefaultResource() {
    ResourceManager resourceManager = new ResourceManager();
    return resourceManager.Load();
    // Bad: losing reference to ResourceManager here!
  }
}

Одним из возможных обходных путей будет именованный конструктор:

public class DefaultFooificator : Fooificator, IDisposable {

  public static DefaultFooificator Create() {
    ResourceManager resourceManager = new ResourceManager();
    DefaultFooificator fooificator = new DefaultFooificator(
      resourceManager.Load()
    );
    fooificator.resourceManager = resourceManager;

    return fooificator;
  }

  private DefaultFooificator(Resource resource) : base(resource) {}
  private ResourceManager resourceManager;
}

Однако, при решении проблемы сохранения ссылки ResourceManager, это не позволит дальнейшим классам наследовать от DefaultFooificator.

Существуют ли какие-либо элегантные решения, которые позволили бы мне в дальнейшем извлечь из DefaultFooificator или сохранить ссылку ResourceManager другим способом?

Ответы [ 2 ]

1 голос
/ 25 августа 2009

Я думаю, вы должны подумать, не слишком ли тесно связаны ваши занятия. Если у Resource есть открытый конструктор, каждый может создать экземпляр и передать его как ссылку на конструктор Fooificator. Это на самом деле хорошая вещь.

Что кажется менее привлекательным, так это то, что у вас есть требование, чтобы только Ресурсы, созданные ResourceManager.Load (кстати, это нарушение разделения команд / запросов), могли использоваться на законных основаниях. Однако API не применяет это ограничение.

Либо вы должны применить это ограничение, сделав конструктор Resource внутренним, либо сняв ограничение ResourceManager.

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

В качестве альтернативы, измените конструктор Fooificator, чтобы взять ссылку на ResourceManager, или, возможно, Func<ResourceManager, Resource>.

Без большего контекста трудно дать лучший ответ, я думаю.

1 голос
/ 25 августа 2009

Почему класс, производный от Fooificator, должен отвечать за создание Resource? Почему вы не можете просто вставить эту зависимость в ее конструктор?

public class DefaultFooificator : Fooificator
{
    public DefaultFooificator(Resource resource) : base(resource) {}
}

Вы можете отложить создание ресурса до FooificatorFactory.

...