Универсальные фабрики невозможны при необходимости внедрения зависимостей?

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

Обычно мои фабрики сервисов и дао выглядят так:

public static class PersonServiceFactory
{
    private static PersonService personService;

    public static PersonService GetInstance()
    {
        if (personService == null)
        {
            PersonDao personDao = PersonDaoFactory.GetInstance();
            personService = new PersonService(personDao);

        }

        return personService;
    }
}

public static class PersonDaoFactory
{
    private static PersonDao personDao;

    internal static PersonDao GetInstance()
    {
        if (personDao == null)
        {
            personDao = new PersonDao();

        }

        return personDao;
    }
}

Затем я попытался создать общие фабрики:

 public abstract class EntityDaoFactory<daoClass>
        where daoClass : class, new()
    {
        private static daoClass factorySupportClass;

        internal static daoClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                factorySupportClass = new daoClass();

            }

            return factorySupportClass;
        }
    }

  public abstract class EntityServiceFactory<serviceClass, daoClass>
        where serviceClass : class, new()
        where daoClass : class
    {
        private static serviceClass factorySupportClass;

        internal static serviceClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                //daoClass daoSupportClass = *how to get daoSupportClassfactory.GetInstance(); here?*
                factorySupportClass = new serviceClass(daoSupportClass);

            }

            return factorySupportClass;
        }
    }

Таким образом, их можно было использовать так:

public static class PersonDaoFactory : Entities.EntityDaoFactory<PersonDao>
{
}

public static class PersonServiceFactory : Entities.EntityServiceFactory<PersonService, PersonDaoFactory>
{
}

Вот проблемы, с которыми я сталкиваюсь:

  1. Невозможно использовать статический класс в качестве ограничения типа для дженериков, которые я пытался использовать для EntityServiceFactory, потому что без него я не знаю, как ввести соответствующий dao.

  2. Фабрики не могут быть производными от общих фабрик, потому что я получаю такую ​​ошибку:

Статический класс Persons.PersonDaoFactory не может быть производным от типа Entities.EntityDaoFactory. Статические классы должны быть производными от объекта.

  1. Пытался сделать их все нестатические классы с частными конструкторами, чтобы обойти это, но потом я получил:

'Persons.PersonService' должен быть неабстрактным типом с общедоступным конструктором без параметров, чтобы использовать его в качестве параметра 'serviceClass' в универсальном типе или методе 'Entities.EntityServiceFactory

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

Есть ли способ заставить эти универсальные фабрики работать с использованием другого подхода, но при этом иметь возможность использовать DI?

ИЗМЕНИТЬ ----

Мне удалось добиться такой работы, но в ней есть некоторые странности. Сначала я создал интерфейс IEntityFactory:

 public interface IEntityFactory<T>
    where T : class
{
    T GetInstance();
}

Затем изменил EntityDaoFactory на:

public abstract class EntityDaoFactory<daoClass> : IEntityFactory<daoClass>
        where daoClass : class, new()
    {
        private static daoClass factorySupportClass;

        public daoClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                factorySupportClass = new daoClass();

            }

            return factorySupportClass;
        }
    }

Поэтому я мог передать соответствующие параметры типа и изменить EntityServiceFactory на:

 public abstract class EntityServiceFactory<serviceClass, daoClass, daoFactoryClass>
        where serviceClass : class, new()
        where daoClass : class, new()
        where daoFactoryClass : IEntityFactory<daoClass>, new()
    {
        private static serviceClass factorySupportClass;

        public static serviceClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                daoFactoryClass daoSupportFactory = new daoFactoryClass();
                daoClass daoSupportClass = daoSupportFactory.GetInstance();
                factorySupportClass = new serviceClass();

            }

            return factorySupportClass;
        }
    }

Итак, для конкретной реализации, такой как объект Person, вызовы выглядят так:

public class PersonDaoFactory : Entities.EntityDaoFactory<PersonDao>
{
}

 public class PersonServiceFactory : Entities.EntityServiceFactory<PersonService, PersonDao, PersonDaoFactory>
{
}

Итак, сейчас это работает, но вот странности:

  1. Вы можете создать экземпляр фабрики, которая требовалась (насколько я знаю, единственный способ сделать это?) Для EntityServiceFactory, но для тех, кто использует мой API, у них не было бы причин для этого, но они все равно могли.

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

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

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


person SventoryMang    schedule 02.12.2011    source источник
comment
Похоже, все ваши проблемы связаны с тем, что вы отметили фабрики как статические, что не имеет смысла, кроме того, учитывая, что вы собираетесь использовать DI, который становится для уменьшения таких статических зависимостей   -  person sll    schedule 02.12.2011
comment
Они поразительно похожи на синглтонов.   -  person Austin Salonen    schedule 02.12.2011
comment
Вам не нужно строить фабрику, если вам нужно что-то построить.   -  person Dan Bryant    schedule 02.12.2011
comment
en.wikipedia.org/wiki/Factory_pattern Например, используя это определение, синглтоны, реализованные singleton pattern - это формальные фабрики.   -  person SventoryMang    schedule 02.12.2011
comment
@DOTang: Конечно. Но вы не стали бы использовать синглтоны там, где вам нужно иметь возможность создать что-то (требование new()).   -  person Austin Salonen    schedule 02.12.2011
comment
Почему нет? В синглтоне вы создаете экземпляр класса только один раз, и это то, что я делаю. А если нет, то что вы используете?   -  person SventoryMang    schedule 03.12.2011


Ответы (2)


Прежде всего, вы НЕ используете внедрение зависимостей. Внедрение зависимостей не имеет ничего общего с предоставлением параметров типа универсальному классу / методу.

Ошибки возникают из-за нарушения правил C #. Вы должны изменить свой код, чтобы он соответствовал им. Итак, сделайте ваши классы нестатическими и не используйте частные конструкторы. Вы можете заменить статический класс экземпляром синглтона и использовать защищенные конструкторы, чтобы избежать создания экземпляра неконтроллера.

person Ondrej Tucny    schedule 02.12.2011
comment
Да, я использую внедрение зависимостей, ServiceFactory вводит соответствующий класс Dao в новый экземпляр класса Service, и это имеет значение, потому что я не могу использовать класс Service в качестве параметра универсального типа, если у него нет общедоступного пустого конструктора, который я уже попробовали, и это помогло исправить некоторые из моих проблем, но теперь человек, использующий мой API, может напрямую создать экземпляр службы (чего им даже не следовало бы делать) без конструктора dao, что могло бы нарушить работу. Что вы, возможно, коснулись этого в своем последнем предложении, но я не уверен, что вы имеете в виду, можете ли вы привести пример? - person SventoryMang; 03.12.2011
comment
Я отредактировал свой пост, мне удалось заставить его работать, удалив все статики и частные конструкторы, но есть некоторые странности, которые я заметил в своем редактировании. - person SventoryMang; 03.12.2011
comment
Пометить это как ответ, потому что он устранил мои конкретные проблемы с первого раза, но он отказался от слишком большого количества imo, чтобы его стоило использовать. - person SventoryMang; 03.12.2011

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

Следующее компилируется и делает то, что вы хотите:

public abstract class Entity<serviceFactory, serviceClass, daoFactory, daoClass>
    where daoFactory : Entity<serviceFactory, serviceClass, daoFactory, daoClass>.DaoFactory, new()
    where daoClass : class, new()
    where serviceFactory : Entity<serviceFactory, serviceClass, daoFactory, daoClass>.ServiceFactory, new()
    where serviceClass : class, new()
{
    public abstract class DaoFactory
    {
        private static daoClass factorySupportClass;

        internal static daoClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                factorySupportClass = new daoFactory().createDao();

            }

            return factorySupportClass;
        }

        protected abstract daoClass createDao();
    }

    public abstract class ServiceFactory
    {
        private static serviceClass factorySupportClass;

        internal static serviceClass GetInstance()
        {
            if (factorySupportClass == null)
            {
                daoClass daoSupportClass = DaoFactory.GetInstance();
                factorySupportClass = new serviceFactory().createService(daoSupportClass);

            }

            return factorySupportClass;
        }
        protected abstract serviceClass createService(daoClass dao);
    }
}

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

person Tyree Jackson    schedule 10.08.2015