Есть ли какие-либо преимущества (семантические или другие) в использовании статического метода, который вызывает конструктор?

#c# #constructor #static-methods

#c# #конструктор #статические методы

Вопрос:

Я только что обновил Visual Studio 2013 и заметил, что в шаблоне проекта для приложения MVC класс ApplicationDbContext теперь имеет статический метод, который просто вызывает конструктор:

 public static ApplicationDbContext Create()
{
    return new ApplicationDbContext();
}
 

Мне это кажется беспорядком, но я полагаю, что есть какая-то семантическая причина, по которой я должен теперь начать использовать ApplicationDbContext.Create() вместо new ApplicationDbContext() . Есть ли какие-либо преимущества в этом?

Комментарии:

1. Это похоже на плохую реализацию фабрики.

2. Является ли конструктор частным? … Это шаблон, который используется, чтобы помешать потребителю просто создать объект, не указав, что от него требуется, — заставляя вас использовать статический метод. Конечно, этот метод кажется бесполезным, но если вы хотите его расширить, у вас есть для этого настройка.

3. Конструктор не является частным.

4. Если вы используете API Autodesks, все они реализуют этот шаблон, но они делают конструкторы закрытыми. Например; static Pipe Document::CreateNewPipe(...); и static Curve Document::CreateNewCurve(...); , честно говоря, я нахожу это невероятно раздражающим и бесполезным. Однако это, вероятно, полезно для разработчиков API для обеспечения целостности объекта.

5. Найдите в Google книгу «Банда четырех» и прочитайте обзор шаблонов проектирования, описанных в ней. Это простая реализация шаблона factory, который, среди 22 других шаблонов проектирования, описан в книге. Полезно быть знакомым с ними, по крайней мере, до такой степени, чтобы распознавать их и знать, когда их использовать.

Ответ №1:

На самом деле. ДА.

В вашем конкретном случае, обернув его таким образом, вы сможете быстро начать привязку к логике, например, создать ApplicationDbContext и singleton или обработать исключение общим способом для всего приложения. Поскольку конструктор не может возвращать значение null, это может быть очень важно, чтобы иметь возможность перехватывать исключение и возвращать значение null.

Кортеж.Create — это яркий пример общего вывода, который не работает с конструкторами. Это позволяет вам сказать

 Tuple.Create(Item1, Item2.. ItemN);
 

И пусть компилятор выводит типы, а не

 new Tuple<T1, T2...Tn>(Item1, Item2...ItemN);
 

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

Существует также случай анонимных типов, которые не могут быть указаны явно и, следовательно, не могут быть использованы в новых операторах. У меня, в частности, был случай, когда при поиске сборок для определенного атрибута, для которого можно связать структуру команд, я хотел сделать перечисляемый (в данном случае очередь) из анонимного типа во время поиска, чтобы связать ссылки на классы с их конструктором и строковыми аргументами, вместо того, чтобы искать их каждый раз время, когда они понадобятся. Поскольку я снова могу использовать общий вывод в методе, я смог обернуть конструктор в метод расширения и выполнить задание.

Существуют также случаи для одноэлементных шаблонов, когда вы хотите, чтобы метод «getInstance» обычно создавал значение или получал его, если оно существует. Может не подходить, так как он делает немного больше, чем просто перенос конструктора.

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

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

Комментарии:

1. Просто примечание: вывод параметров типа конструктора (разрешающий new Tuple(1, 2) ) является одной из ожидаемых новых функций в C # 6.

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

Ответ №2:

Этот шаблон может быть очень полезен, особенно если вы используете частный конструктор и возвращаете тип интерфейса из Create, а не конкретный тип.

 private ApplicationDbContext()
{

}

public static IApplicationDbContext Create()
{
    return new ApplicationDbContext();
}
 

Теперь потребители вашего класса не могут зависеть от конкретной реализации — они могут полагаться только на абстракцию.

Комментарии:

1. Обычно это называется шаблоном заводского метода. oodesign.com/factory-method-pattern.html

Ответ №3:

Обертывание конструктора статическими методами (методами создания) позволяет вам выбрать конкретное имя, которое передает информацию. Вы также можете создать несколько методов с одинаковой сигнатурой параметра, таких как CreateX(float f) и CreateY(float f) , что вы не можете сделать с конструкторами.

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

Пример:

 public struct Length
{
  private const double MetersPerYard = 0.9144;
  private double _meters;

  private Length(double meters)
  {
    _meters = meters;
  }

  public static Length FromMeters(double meters)
  {
    return new Length(meters);
  }

  public static Length FromYards(double yards)
  {
    return new Length(yards*MetersPerYard);
  }

  public double Meters 
  {
    get { return _meters; } 
  }

  public double Yards
  {
    get { return _meters / MetersPerYard; }
  }
}
 

Или взгляните на TimeSpan и методы, такие как FromMinutes , и т.д. FromSeconds