Реализация специфических методов в интерфейсе - как их избежать? - PullRequest
0 голосов
/ 22 декабря 2009
  • У нас есть интерфейс, который передается в конструктор с помощью контейнера IoC
  • У нас есть несколько классов, которые реализуют этот интерфейс.

Проблема в том, что некоторые (не все) реализации необходимо очистить, предпочтительно с использованием интерфейса IDisposable.

Поскольку не всем реализациям понадобится метод «Dispose», я все равно включаю его (я понимаю, что на самом деле я не реализую IDisposable, это только пример)? Мне кажется, что класс, который знает о реальных реализациях интерфейса, должен знать, как очистить эти объекты. Однако это контейнер IoC, и я не вижу элегантного способа сообщить ему о необходимости очистки для определенного класса, который он создал.

interface IBar
{
  void DoWork();
  void Dispose(); //Not all implementations need this
}

class Foo : IBar
{
  public void DoWork()
  {
    //implementation 1
  }

  public void Dispose()
  {
    //cleanup, which needs to be called
  }
}

class Foo2 : IBar
{
  public void DoWork()
  {
    //implementation 2
  }

  public void Dispose()
  {
    //No cleanup needed in this implementation
  }
}

Ответы [ 4 ]

3 голосов
/ 22 декабря 2009

Вы можете реализовать IDisposable на тех, кто в этом нуждается, а затем сделать это:

interface IBar
{
    void Bar();
}

class Foo : IBar
{
    public void Bar() { }
}

class Foo2 : IBar, IDisposable
{
    public void Bar() {}
    public void Dispose() {}
}

class Program
{
    static void Main(string[] args)
    {
        foreach (IBar bar in new IBar[] { new Foo(), new Foo2() })
        {
            bar.Bar();

            IDisposable disp = bar as IDisposable;

            if (disp != null)
            {
                disp.Dispose();
            }
        }
    }
}
1 голос
/ 22 декабря 2009

Странный вопрос. Если вас беспокоит именно IDisposable, большинство контейнеров будет проверять реализацию, и если она содержит реализацию IDisposable, контейнер автоматически вызовет Dispose (), когда освободит экземпляр.

Итак,

// pure clean business interface. no dispose member
public interface ISomeBusinessInterface { }

// an implementation that does not require any 'disposing'
// and implements *only* business interface
public class SomeBusinessImplementation : 
    ISomeBusinessInterface
{
}

// an implementation that contains heavy resources and 
// requires 'disposing'
public class SomeDisposableBusinessImplementation : 
    ISomeBusinessInterface,
    IDisposable
{
}

// a typical consumer. no knowledge of instance life-cycle
public class SomeConsumer 
{
    // injector constructor
    public SomeConsumer (ISomeBusinessInterface business) { }
}

Ваши потребители не должны знать о жизненном цикле впрыскиваемого компонента и не несут никакой ответственности за него. С этой точки зрения, имеет смысл, что ISomeBusinessInterface не наследует и не подвергает никакому такому Dispose методу - я имею в виду, подумайте, избавились ли вы от важного одноэлементного сервиса! Насколько это было бы неловко?

Так кто же ответственен за жизненный цикл впрыскиваемых компонентов? Ну контейнер это конечно! Как упоминалось ранее, большинство контейнеров будут проверять реализации компонентов на предмет реализаций IDisposable [также еще одна причина использовать стандартные интерфейсы, определенные CLR], и будут вызывать Dispose, когда он определит, что компонент достиг конца своего срока службы. Так что SomeDisposableBusinessImplementation будет правильно утилизирован в конце. :)


Замок Виндзор

Контейнер Castle Windsor IoC отслеживает и поддерживает ссылки на все экземпляры, которые он создает. Он использует отражение, чтобы определить, является ли экземпляр «одноразовым» или нет, и затем вызывает Dispose (), когда жизненный цикл экземпляра завершается. Для долгоживущих объектов это когда контейнер утилизируется. Для кратковременных кратковременных объектов это когда Release (object) явно вызывается потребителем *.

* = что поднимает важный вопрос, временные объекты должны быть получены с помощью явного вызова Resolve<T> () и освобождены с помощью явного вызова Release (object). Неявное внедрение временных классов может привести к утечкам памяти!


StructureMap

Контейнер IoC StructureMap не отслеживает и не поддерживает ссылки на временные экземпляры. Что в значительной степени означает, что управление жизненным циклом объекта немного сложнее. Это отлично подходит для кратковременных кратковременных объектов, так как не требуется дополнительного бухгалтерского учета , за исключением обычного . Однако для долгоживущих объектов вам придется расширить структуру, отразить и утилизировать экземпляры самостоятельно .

+ = сложный имо.

1 голос
/ 22 декабря 2009

Что не так со следующим?

interface IBar { void DoWork(); }
class Foo : IBar, IDisposable { // details, implements Dispose }
class Foo2 : IBar { // details, no Dispose }
0 голосов
/ 22 декабря 2009

Когда нужно вызвать Dispose? После выполнения работы или во время закрытия приложения? Если последнее, не поддерживает ли ваш контейнер IoC такую ​​очистку? В Java и с контейнером Spring IoC ваша реализация будет реализовывать одноразовый интерфейс Spring, и контейнер будет вызывать метод dispose во время завершения работы.

...