Инъекция зависимостей с обязательным параметром [duplicate]

Я думаю, что ответ на этот вопрос заключается в том, как python передает данные в параметр (передать по значению или по ссылке), а не изменчивость или как питон обрабатывает инструкцию «def».

Краткое введение. Во-первых, в питоне есть два типа типов данных: один простой элементарный тип данных, например числа, а другой тип данных - объекты. Во-вторых, при передаче данных в параметры python передает элементарный тип данных по значению, т. Е. Делает локальную копию значения локальной переменной, но передает объект по ссылке, т. Е. Указывает на объект.

Признавая вышеуказанные два момента, давайте объясним, что произошло с кодом python. Это происходит только из-за передачи по ссылке для объектов, но не имеет ничего общего с mutable / immutable или, возможно, факта, что оператор «def» выполняется только один раз, когда он определен.

[] является объектом , поэтому python передает ссылку [] на a, т. е. a является только указателем на [], который лежит в памяти как объект. Существует только одна копия [] с, однако, многими ссылками на нее. Для первого foo () список [] изменен на 1 методом добавления. Но учтите, что существует только одна копия объекта списка, и этот объект теперь становится 1 . При запуске второго foo (), какая веб-страница effbot говорит (элементы больше не оцениваются) неверна. a оценивается как объект списка, хотя теперь содержимое объекта 1 . Это эффект прохождения по ссылке! Результат foo (3) может быть легко получен таким же образом.

Чтобы еще раз подтвердить мой ответ, давайте рассмотрим два дополнительных кода.

===== = № 2 ========

def foo(x, items=None):
    if items is None:
        items = []
    items.append(x)
    return items

foo(1)  #return [1]
foo(2)  #return [2]
foo(3)  #return [3]

[] - это объект, поэтому None (первый является изменяемым, в то время как последний является неизменным, но изменчивость не имеет ничего что касается вопроса). Никто не находится где-то в пространстве, но мы знаем, что он есть, и там есть только одна копия «Нет». Таким образом, каждый раз, когда вызывается foo, элементы оцениваются (в отличие от некоторого ответа, который оценивается только один раз) равны None, чтобы быть ясным, ссылка (или адрес) None. Затем в foo элемент изменяется на [], т. Е. Указывает на другой объект, который имеет другой адрес.

====== № 3 =======

def foo(x, items=[]):
    items.append(x)
    return items

foo(1)    # returns [1]
foo(2,[]) # returns [2]
foo(3)    # returns [1,3]

Вызов элемента foo (1) указывает на объект списка [] с адрес, скажем, 11111111. содержимое списка будет изменено на 1 в функции foo в дальнейшем, но адрес не изменится, еще 11111111. Тогда foo (2, []) является приходит. Хотя [] в foo (2, []) имеет тот же контент, что и параметр по умолчанию [] при вызове foo (1), их адрес отличается! Поскольку мы предоставляем параметр явно, items должен принять адрес этого нового [], скажем 2222222, и вернуть его после внесения некоторых изменений. Выполняется foo (3). поскольку предоставляется только x, элементы должны снова принимать значение по умолчанию. Что такое значение по умолчанию? Он задается при определении функции foo: объект списка, расположенный в 11111111. Таким образом, элементы оцениваются как адрес 11111111, имеющий элемент 1. Список, расположенный в 2222222, также содержит один элемент 2, но он не указывается элементами any Больше. Следовательно, добавление 3 сделает items [1,3].

Из приведенных выше объяснений видно, что веб-страница effbot , рекомендованная в принятом ответе, не дала соответствующего ответа на этот вопрос. Более того, я думаю, что точка на веб-странице effbot неверна. Я думаю, что код UI.Button верен:

for i in range(10):
    def callback():
        print "clicked button", i
    UI.Button("button %s" % i, callback)

Каждая кнопка может содержать определенную функцию обратного вызова, которая будет отображать различное значение i. Я могу привести пример, чтобы показать это:

x=[]
for i in range(10):
    def callback():
        print(i)
    x.append(callback) 

Если мы выполним x[7](), мы получим 7, как ожидалось, и x[9]() даст 9, другое значение i.

22
задан George R 4 August 2011 в 04:29
поделиться

6 ответов

Такие конструкции следует избегать, когда это возможно. Поэтому спросите себя: действительно ли этот параметр необходим как аргумент конструктора? Или может SomeObject быть заменен на безгосударственный, который повторно используется всеми, кто зависит от него, передавая параметр методу, который вы выполняете на объекте?

, например. Вместо

public class SomeObject
{
    private float someValue
    public SomeObject(IService service, float someValue)
    {
        this.someValue = someValue
    }

    public float Do(float x)
    {
        return this.Service.Get(this.someValue) * x;
    }
}

используйте

public class SomeObject
{
    public SomeObject(IService service)
    {
    }

    public float Do(float x, float someValue)
    {
        return this.Service.Get(someValue) * x;
    }
}

Если требуется, пошли за фабрикой:

public interface ISomeObjectFactory
{
    ISomeObject CreateSomeObject(float someValue);
}

public class SomeObjectFactory : ISomeObjectFactory
{
    private IKernel kernel;
    public SomeObjectFactory(IKernel kernel) 
    {
        this.Kernel = kernel;
    }

    public ISomeObject Create(float someValue)
    {
        return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
    }
}

Preview: Ninject 2,4 won 't требуется реализация, но разрешить

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();
17
ответ дан Ulysses Alves 21 August 2018 в 14:05
поделиться
  • 1
    +1 Определенно избегая правильного ответа, отредактировал мой ответ, чтобы согласиться. Я знаю, что это скроет точку немного, но, как описано выше, в другом месте рассмотрим выражение вашего SomeObjectFactory с помощью Func & lt; T & gt; ctor arg, а не напрямую использовать Kernel – Ruben Bartelink 4 August 2011 в 16:55
  • 2
    @Ruben Bartelink: Я просто превзошел эту версию, потому что я думаю, что она будет предпочтительной для Ninject 2.4. Завод будет автогенерироваться ядром. Вариант Func также будет поддерживаться с недостатком, что Func не дает информации о параметрах и затрудняет сопоставление параметров. Поэтому я больше не буду придерживаться варианта Func, чтобы иметь возможность обновлять, просто удалив заводскую реализацию и изменив привязку. Но также я бы поставил фабрику в загрузчик, а интерфейс - в стороне от потребителя. – Remo Gloor 4 August 2011 в 22:00
  • 3
    Спасибо за объяснение тонкостей инъекции Func - не думал, что через. Я просто делал вывод, что в твоей манере фабрики [что 2.4 будет автоген], вы берете ctor dep на ядре, которое можно удалить, взяв вместо него Func. Думая об этом сейчас, сделайте так, чтобы ответ был менее ясным и не отображался в отношении того, что ToFactory собирается делать. Поэтому забудьте о том, что я сказал, и спасибо за объяснение! И это su_gg_est: P Кстати, вы ошибочно указали имя метода как Create (not CreateSomeObject). Я лично буду использовать Func в предпочтении, поскольку я живу в запутанном мире ... – Ruben Bartelink 5 August 2011 в 00:30
  • 4
    Полюбите идеи, хотя - должны позволять меньше и меньше контейнероустойчивой выкидывания за пределами загрузочных машин. Я все еще не уверен, что мне нравится поведение по умолчанию, позволяющее IKernel быть разрешимым без явной привязки и / или WithKernelOption () в выражении Bind, но тогда я не делаю много классов Factory, поэтому я мог ошибаться – Ruben Bartelink 5 August 2011 в 00:34

Другой подход - инициализация в два этапа (не связанная с ninject, любая структура DI):

public class SomeObject
{
    private readonly IService _service;

    public SomeObject(IService service)
    {
        // constructor only captures dependencies
        _service = service;
    }

    public SomeObject Load(float someValue)
    {
        // real initialization goes here
        // ....

        // you can make this method return no value
        // but this makes it more convienient to use
        return this;
    }
}

и использование:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}
1
ответ дан Arek 21 August 2018 в 14:05
поделиться

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

Наш подход заключается в создании фабрики через D.I., и тогда метод Create Factory будет построен с использованием переданного в D.I. контейнер. Нам не нужно часто использовать этот шаблон, но когда мы это делаем, он делает продукт намного чище (поскольку он делает наши графики зависимостей меньше).

1
ответ дан Beep beep 21 August 2018 в 14:05
поделиться

В NInject, с которым вы отметили это, вы вводите автоматически созданный Factory в форме Func<parameters you wish to feed in,T>, используя FuncModule, как описано в этом сообщении .

Этот подход также доступен в autofac для одного.

Различные ответы Factory method рассматриваются в ответах на этот вопрос .

EDIT: NB Хотя это может быть интересно, воспользуйтесь решением @Remo Gloor (и критически советуем избегать такого решения)

1
ответ дан Community 21 August 2018 в 14:05
поделиться

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

Мое другое предложение состоит в том, что вы вместо прямого доступа к нему создаете фабрику, которую вы можете использовать для создания такого объекта. Затем вы регистрируете завод в своем контейнере и используете фабрику для создания своего экземпляра. Что-то вроде этого:

public class SomeObjectFactory : ISomeObjectFactory
{
    private IYourService _service;
    public SomeObjectFactory(IYourService service) 
    {
        _service = service;
    }

    public ISomeObject Create(float someValue)
    {
        return new SomeObject(_service, someValue);
    }
}

вы можете попробовать такой шаблон.

UPDATE: Обновлен код, чтобы отразить комментарии к улучшению.

0
ответ дан Tomas Jansson 21 August 2018 в 14:05
поделиться
  • 1
    У вас, похоже, жесткая зависимость от контейнера по вашему выбору в SomeObjectFactory. Это не рекомендуется. Вместо этого вставьте экземпляр IYourService в конструктор SomeObjectFactory и пусть conatiner разрешит эту зависимость в корне композиции. – Johann Gerell 4 August 2011 в 10:08
  • 2
    Конечно, мой код - всего лишь контур, это не окончательная реализация. Дело в том, что вместо этого он должен использовать фабрику. Но я обновил код в соответствии с тем, что, я думаю, вы имели в виду. – Tomas Jansson 4 August 2011 в 10:11
  • 3
    Намного лучше! :) – Johann Gerell 4 August 2011 в 12:47
1
ответ дан Community 1 November 2018 в 08:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: