Убедитесь, что объект создан только фабрикой (C#)
Как сделать так, чтобы конкретный класс создавался только фабрикой, а не вызывать new напрямую?
РЕДАКТИРОВАТЬ: мне нужно, чтобы фабрика была отдельным классом (для целей внедрения зависимостей), поэтому я не могу сделать его статическим методом экземпляра класса, и поэтому я не могу сделать новый private.
8 ответов
Если по какой-то причине вам нужно, чтобы фабрика и созданный класс находились в отдельных сборках (что означает просто internal
не будет работать), и вы можете убедиться, что ваша фабрика получит шанс запустить в первую очередь, вы можете сделать это:
// In factory assembly:
public class Factory
{
public Factory()
{
token = new object();
MyClass.StoreCreateToken(token);
}
public MyClass Create()
{
return new MyClass(token);
}
private object token;
}
// In other assembly:
public class MyClass
{
public static void StoreCreateToken(object token)
{
if (token != null) throw new InvalidOperationException(
"Only one factory can create MyClass.");
this.token = token;
}
public MyClass(object token)
{
if (this.token != token) throw new InvalidOperationException(
"Need an appropriate token to create MyClass.");
}
private static object token;
}
Да, это громоздко и неловко. Но могут быть странные ситуации, когда это действительно хорошее решение.
Если фабрика находится в той же сборке и вам нужна защита только от внешних сборок, создающих экземпляр класса, вы можете сделать конструктор внутренним. Единственный известный мне способ предотвратить это для всех других классов (в том числе в той же сборке) - сделать экземпляр класса вложенным закрытым классом фабрики и выставить его только как интерфейс. Если класс является собственной фабрикой (метод статической фабрики), вы можете сделать конструктор частным, как уже упоминали другие.
Сделайте его конструкторы частными и предоставьте фабричный метод как статический метод для самого класса.
В большинстве случаев вы можете просто сделать конструкторы внутренними, что позволит вам разделить фабрику на ее собственный класс - я обнаружил, что часто не стоит пытаться помешать моей собственной команде использовать new
создавать экземпляры внутри сборки класса.
Сделайте конструктор внутренним и разместите фабрику в той же сборке.
public MyClass
{
internal MyClass()
{
}
}
в той же сборке
public MyClassGenerator
{
public static CreateMyClass()
{
return new MyClass();
}
}
Если фабрика не может быть в одной сборке или этот метод не работает для вас, посмотрите ответ Дэна
Вы можете создавать ваши конкретные классы как вложенные частные классы с открытыми конструкторами внутри вашего фабричного класса - таким образом ваша фабрика может создавать их, другие даже не видят их. В любом случае вы возвращаете с фабрики некоторый интерфейсный / абстрактный класс, а не конкретный тип. Конечно, вы не сможете преобразовать свой тип возвращаемого значения в конкретный тип где-нибудь в клиентском коде, но сначала это признак плохого дизайна, во-вторых, вы всегда можете обойти это с более конкретным интерфейсом / абстрактным классом, который наследует ваш вложенный закрытый класс.
Вы можете обратиться к ответу Эрика Липперта здесь (для аналогичной проблемы): зачем мне когда-либо нужно использовать вложенные классы C#
Он всегда будет создаваться путем вызова new где-нибудь, но если вы хотите, чтобы это происходило только в вашем классе фабрики, вы можете установить для всех конструкторов значение Internal (или Private) и использовать метод фабрики Public Static для того же класса.
Многие люди упоминали об использовании внутренних, но вы также можете защитить свои конструкторы и получить класс, в котором есть только статический метод фабрики. Это не мешает другим делать то же самое, но довольно неплохо справляется с ограничением прямого доступа к вашим конструкторам.
Мне не нравится иметь фабрику на самом типе, особенно если это объект домена. Имейте это внутреннее, если у вас есть отдельный класс как фабрика (который я думаю, что вы должны). Используйте атрибут InternalVisible, если фабрика находится в другой сборке.