Виндзор: как избавляться от объектов, созданных с использованием фабрики на основе интерфейса. - PullRequest
0 голосов
/ 21 января 2012

Я начал использовать Виндзор и обнаружил, что это очень, очень крутая функция: печатные фабрики.Я узнал, что всякий раз, когда я использую фабрику для получения экземпляра, я также должен просить его уничтожить экземпляр, когда он мне не нужен.Мне было интересно, как это относится к чрезвычайно распространенной операции, такой как открытие файла на диске.Модель:

interface IDisk
  IFile OpenFile(string path)
interface IFileFactory
  IFile Create(string path)
  void Destroy(IFile openFile)
interface IFile // empty, it's not important what it does

class Disk : IDisk
  ctor(IFileFactory fileFactory)
  IFile OpenFile(string path)
     return fileFactory.Create(path)

Код пользователя:

disk = container.Resolve<IDisk>() // disk is root so this is not SL
file = disk.OpenFile("foo.bar");
// do something with file

Теперь настало время избавиться от файла.Средний пользователь .NET будет звонить file.Dispose().Но на этот раз все по-другому:

  • файл является интерфейсом, который не наследует IDisposable (если это так, то это будет утечка абстракции);однако класс File реализует IDisposable
  • файл, созданный фабрикой, которой он принадлежит

Так что вот другой способ: вместо Dispose, IFileИнтерфейс имеет дополнительный метод Close, который должен освободить экземпляр файла с фабрики, который, в свою очередь, вызывает Dispose в экземпляре File.Чтобы это работало, класс File должен либо знать о файловой фабрике (которая мне не нравится), либо о ее методе Destroy:

class File : IFile
  ctor(Action<IFile> destroyCallback)

Мне это тоже не нравится... Я мог бы избежать всех этих проблем, если бы я изменил модель так, чтобы файл не закрывался сам, но диск закрывал файл:

//file.Close() // not supported, by design
disk.Close(file)

Это легче для меня, кто написал Disk иFile класс, но я волнуюсь, что мои пользователи не оценят этот «новый шаблон».

Мысли?

1 Ответ

2 голосов
/ 22 января 2012

Хорошо, позвольте мне обратиться к этому по порядку:

Обычный пользователь .NET вызвал бы file.Dispose ().

Вызов file.Dispose() неверен,независимо от того, используете ли вы контейнер или нет.В .NET действует правило, согласно которому вы не должны уничтожать то, что не создали.

Это относится как к .Dispose(), так и к ctor(Action<IFile> destroyCallback)

То есть, кому принадлежит файл ?что касается вашего кода, то фабрика это делает.Поэтому ответственность за очистку после файла лежит на фабрике.

Если вы сохраняете фабрику в качестве подробности реализации Disk (как вам, скорее всего, следует), то Disk является вашим API области поверхностидля открытия и закрытия файла , следовательно, необходим метод Close(IFile file), который в качестве детали реализации передает его на фабрику.

Поэтому использованиефайл будет выглядеть следующим образом:

var myfile = disk.OpenFile(@"c:\myfile.txt);
try
{
   DoSomethingWithTheFile(myFile);
}
finally
{
   disk.Close(myFile);
}

[РЕДАКТИРОВАТЬ: на основе комментария]

Хорошо, если вы ожидаете и хотите, чтобы у ваших пользователей был какой-то объект IDisposable, тогда дайте имодин (я бы скорее посоветовал против этого, но это не мой вызов)

Затем метод OpenFile вернет объект, который выглядит следующим образом:

public class FileScope:IDisposable
{
   private Action<IFile> close;

   public FileScope(IFile file, Action<IFile> close)
   {
      File = file;
      this.close = close;
   }


   public IFile File {get; private set;}

   public void Dispose()
   {
      close(file);
   }
}

Закрытый делегат будет обратным вызовом дляdisk.Close, как в

public FileScope OpenFile(string path)
{
   return new FileScope(fileFactory.Create(path),Close);
}
...