Использование определенных шаблонов для реализации интерфейсов - PullRequest
6 голосов
/ 14 сентября 2010

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

Давайте рассмотрим пример.Когда класс реализует IDisposable, вы должны следовать определенному шаблону, чтобы убедиться, что ваши ресурсы очищены должным образом, путем создания частного метода Dispose (bool dispose), который различает, вызывается ли он финализатором или вызывается изметод публичной утилизации.Кроме того, в этом случае должен быть реализован финализатор, и вам также может понадобиться закрытая переменная bool isDisposed, которая устанавливается методом Dispose, так что любой метод, вызываемый после того, как объект th будет расположен, будет вызывать исключение, поясняющее, что этот объектуже удален, вместо кода внутри метода, вызывающего сбой, потому что некоторые необходимые ресурсы расположены и, следовательно, больше не доступны.

Существует также множество других интерфейсов, которые я обычно реализую, но это не всеЯ уверен, что способ их реализации - предпочтительный способ сделать это, и позже я могу выяснить, что это вызывает незначительную ошибку, которую трудно найти, которая, вероятно, не существовала бы, если бы я следовалправильный шаблон в первую очередь.

Некоторые примеры интерфейсов, которые я хотел бы знать, лучший способ реализации: ISerializable, IComparable, IComparable <>, ICloneable, IEnumerable <> и так далее.Здесь интересны все интерфейсы из Framework, поэтому он не должен ограничиваться теми, что я перечислил выше.

Мне нужны разные интерфейсы, предпочтительный способ и, надеюсь, также ссылка на ресурс наИнтернет, который объясняет, как и почему следует следовать определенному шаблону.

Я надеюсь получить хорошую коллекцию этих шаблонов, так как я знаю, что они могут значительно улучшить мой код, сделать его более правильным и следовать наилучшим образом.-practices

Было бы здорово, если бы для одного и того же интерфейса было несколько шаблонов, чтобы мы могли обсудить, какой из них предпочтительнее.Это также может привести к тому, что некоторые из вас перейдут к новому шаблону или внесут изменения в существующие шаблоны, чтобы еще больше улучшить свой код, и это было бы здорово!

Редактировать

После прочтения комментария Грзениоса я также призываю всех дать контекст, к которому должен применяться шаблон.Например, шаблон IDIsposable следует использовать только в том случае, если у вас есть неуправляемые ресурсы внутри класса, которые вам нужно утилизировать, а не если все объекты, которые вам нужны для утилизации, сами реализуют IDisposable.

Edit 2

Я, наверное, должен сам начать, так как я поставил вопрос здесь.Поэтому я опишу один шаблон, который я хорошо знаю, и это шаблон IDisposable.

Этот шаблон следует использовать только в том случае, если ваш класс содержит один или несколько неуправляемых ресурсов внутри вашего класса, и вы должны убедиться, чтоони избавляются.в этом случае в дополнение к методу Dispose нам понадобится финализатор на тот случай, если пользователь вашего класса забудет его утилизировать.

Итак, первым делом.Ваш класс должен реализовывать интерфейс IDisposable, и вам нужно будет определить публичный метод Dispose как goverend для интерфейса.Этот метод должен выглядеть следующим образом:

public void Dispose()
{
  Dispose(true);
  GC.SuppressFinalize(this);
}

Это вызовет защищенный метод Dispose (bool), который заботится о фактической очистке.

Кроме того, включите в свой класс переменную, чтобы указатьесли класс удален или нет:

private bool alreadyDisposed = false;

GC.SuppressFinalize сообщает сборщику мусора, что этот элемент не нужно завершать, даже если он имеет финализатор.

Тогда вам нужензащищенный метод утилизации.Сделайте его защищенным, а не закрытым, если какой-либо производный класс должен его переопределить:

protected virtual void Dispose(bool isDisposing)
{
  if (alreadyDisposed)
  {
    return;
  }
  if (isDisposing)
  {
    // free all managed resources here
  }
  // free all unmanaged resources here.
  alreadyDisposed = true;
}

Финализатор также должен вызвать Dispose (bool), если пользователь забывает очистить:

~SomeClass(){
  Dispose(false);
}

Если для работы какого-либо метода требуется удаленный ресурс, сделайте функцию такой:

public void SomeMethod()
{
  if (alreadyDisposed)
    throw new ObjectDisposedException("SomeClass",
                                      "Called SomeMethod on Disposed object");
  // Method body goes here
}

Вот и все. Это обеспечит очистку ресурсов. Предпочтительнее для пользователя вашего класса, вызывающего Dispose, но с добавлением Finalizer в качестве резервного метода.

Ответы [ 2 ]

3 голосов
/ 14 сентября 2010

Когда вы изучаете шаблоны проектирования, вам также следует взглянуть на некоторые распространенные анти-шаблоны, вы получите представление о том, откуда берется шаблон.IDisposable является своего рода анти-паттерном, второстепенной версией последовательной связи , в которой требуется, чтобы пользователь вызвал dispose, и если он забудет, вы в дерьме.Одна из основных причин «одноразового шаблона» заключается в том, чтобы решить эту проблему.

Предпочтительный метод (в любом случае мой), где это возможно, состоит в том, чтобы не предоставлять объект IDisposable пользователю, а предоставлять одинметод (например, назовите его Using(...)), который принимает делегата, который будет выполнять код, обычно содержащийся в вашем блоке using(...) { }.Этот метод может выполнить построение, выполнить делегат, затем распорядиться ресурсами, которые он использовал, и исключить по крайней мере 3 проблемы с IDisposable: пользователь, забывший вызвать его, пользователь, вызывающий его несколько раз, и пользователь, вызывающий его тожерано - вам не нужно беспокоиться об этом шаблонном «одноразовом шаблоне», когда нет идентифицируемого идентификатора.

Например, скажем, у меня есть требование к файлу ввода-вывода, когда мне нужно регулярно открывать одни и те же файлы (итаким образом, я не могу ждать, пока сборщик мусора вызовет Finalize, если пользователь забудет вызвать Dispose).

class MyFileStream {
    FileStream fs;
    private MyFileStream(string filename, FileMode mode) {
        fs = new FileStream(filename, FileMode.Open);
    }
    private void Dispose() {
        fs.Dispose();
    }
    public static void Using(string filename, FileMode mode, Action<MyFileStream> use) {
        MyFileStream mfs = new MyFileStream(filename, mode);
        use(mfs);
        mfs.Dispose();
    }
    public void Read(...) { ... }
}

Затем вызывающий абонент может сказать

var x = default(...);
MyFileStream.Using("filename.txt", FileMode.Open, (stream) => {
    x = stream.Read(...);
});
Console.WriteLine(x);

Обратите внимание, что в любом случае это довольно похоже на синтаксис языка using() { }, только на этот раз вы вынуждены его использовать, и это накладывает дополнительные ограничения.Невозможно забыть очистить ресурсы таким образом.

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

Я все равно полностью ушел от темы, так что вернемся к тому, что вы спрашивали.

  • Не используйте ICloneable - он ничего не говорит о том, как объектвыполняется клонирование (глубокое или поверхностное), если вам требуется такая вещь, вместо этого создайте собственные интерфейсы IDeepCloneable или IShallowCloneable.
  • Для IEnumerable <> это редкое событие, которое необходимо создать самостоятельно, поскольку существуетдовольно большая коллекция существующих классов в фреймворке, и вы обычно можете получить дополнительные функции гораздо проще, внедрив методы расширения (такие как в LINQ, или свои собственные, используя мощное ключевое слово yield, которое создастIEnumerable <> для вас в фоновом режиме).

Я бы не сказал, что есть какие-либо спец.Точная картина для остальных, они говорят сами за себя.

0 голосов
/ 14 сентября 2010

Например, шаблон IDIsposable следует использовать только в том случае, если в вашем классе есть какие-то неуправляемые ресурсы, которые нужно утилизировать, а не в том случае, если все объекты, которые вам необходимо утилизировать, сами реализуют IDisposable.

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

Детерминированное удаление объектов и сохранение минимального объема этих ресурсов сделают их наиболее простыми и эффективными, не говоря уже о том, что для разработчиков, читающих код, гораздо яснее распоряжаться ресурсами и читать лучше. Как начало и конец истории, которую можно увидеть в одном месте. Я создаю экземпляр объекта как можно позже и избавляюсь от него как можно скорее после использования, минимизируя объем. Либо явный вызов .Dispose для объекта, либо использование блока Using, который автоматически вызывает метод .Dispose, (2) являются хорошими способами очистки.

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

Вся цель интерфейса - создать руководство для методов, свойств, событий и т. Д., Которые должен реализовать класс, но не предоставлять подробные сведения о том, как это сделать. Это зависит от вас. По сути, нет «шаблона» реализации специальных интерфейсов. Не сбивайтесь с толку интерфейсом IDisposable. После создания интерфейса в VS.NET для вас будет создан отвесный код, который не типичен для интерфейса, и в действительности может быть полностью изменен на то, что вам нужно.

Я думаю, что вы можете запутаться в реализации интерфейсов в .NET Framework, но вам нужно взглянуть на концепцию интерфейса, а не только на те, которые доступны в Framework. Если вы действительно хотите увидеть примеры того, как реализованы интерфейсы в .NET Framework, посмотрите на MSDN или декомпилируйте другие объекты Framework, которые реализуют эти интерфейсы, чтобы увидеть, как их реализует Microsoft.

По мере роста OOD вы начнете видеть всю мощь интерфейсов и начнете создавать их для ваших классов. Например, допустим, у вас есть интерфейс с именем IMachine с методом .StartEngine (). Мы просто хотим, чтобы разработчик интерфейса сам определил all деталей; нет никакого шаблона, которому нужно следовать, и мы, дизайнер интерфейса, не должны знать или заботиться о том, как он реализован. Для автомобиля, может быть, реализация методов включает в себя: «Получить ключи, поставить их в замок зажигания, поставить в парк, нажать педаль тормоза, включить зажигание ...» Однако для газонокосилки такая же реализация методов - «Вставить газ в газонокосилку, заправка». Вытяните сцепление, вытяните шнур ... "

Итак, вы видите, что вы не можете просто взглянуть на интерфейсы в .NET Framework и на то, как применить определенный шаблон к их реализации. Детали реализации, шаблоны и т. Д. Зависят от класса реализации, и если достаточно подробностей об интерфейсе, то это то, что имеет значение.

Надеюсь, это немного поможет ...

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