Фабричный образец: ограничить строительство объекта фабрикой - PullRequest
4 голосов
/ 23 июня 2019

У меня есть класс T и фабрика TFactory, которая создает объекты типа T.Я хочу убедиться, что только фабрике разрешено создавать новые T объекты.

Половинчатым решением будет требование фабрики в качестве параметра в конструкторе T, с единственной целью, котораятолько тот, кто хотя бы принес фабричный объект, может создать T:

class T
{
    public T(TFactory Tf)
    {
        if (!(Tf is TFactory))
            throw new InvalidOperationException("No factory provided");
    }
}

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

Третий подход - поместить в сборку и 1016 *, и TFactory.объявление make T конструктор internal.Но новый проект и сборка как раз для этой цели?

Кто-нибудь может быть лучше?(Хотя мой код C #, это, вероятно, более общий вопрос)

Ответы [ 3 ]

3 голосов
/ 23 июня 2019

Вот что-то очень похожее на ваш третий подход: объявите фабрику как внутренний класс T и сделайте конструктор T private:

public class T {
    public class Factory {
        public T GetT() {
            return new T(); // simple implementation just for an example here
        }
    }

    private T() {}
}

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

Обратите внимание, что вы все еще можете поместить фабричный класс и T в два разных файла с partial классами:

public partial class T {
    private T() {}
    // other stuff about T here...
}

// in another file

public partial class T {
    public class Factory {
        public T GetT() {
            return new T();
        }
        // other stuff about Factory here...
    }   
}
2 голосов
/ 25 июня 2019

Второй подход - худший.Такое поведение абсолютно неочевидно и неясно для клиента.Трассировка стека также замедляет выполнение.1 и 2 имеют смысл.

Если вы хотите иметь полный контроль над созданием экземпляра, поместите его в тип.Используйте фабричный метод.Помните, что при создании ограничений нужно быть разумным.Например, экземпляр должен быть инициирован полиморфным (виртуальным) методом.Нельзя вызывать такой метод из конструктора (очень плохая практика), поэтому метод следует вызывать после конструирования.Чтобы не возлагать эту ответственность на клиента, спрячьте конструктор от одного и предоставьте фабричный метод.

abstract class Base
{
    protected abstract void Initialize();
}

class Derived : Base
{
    protected Derived() { /* ... */}

    protected override void Initialize() { /* ... */}

    public Derived CreateDerived()
    {
        var derived = new Derived();
        derived.Initialize();
        return derived;
    }
}
1 голос
/ 23 июня 2019
public abstract class T { }

public class TFactory
{
    public T CreateT() => new TImpl();

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