Конструктор с параметрами vs метод Init -- стоит ли использо
От: Shmj Ниоткуда  
Дата: 31.03.16 11:46
Оценка: -2 :))
Собственно, вся прелесть конструктора -- вы можете быть уверенным, что если создан инстанс то определенное поле 100% имеет установленное значение (если установлено в конструкторе).

Зато конструктор имеет много минусов. К примеру, Generics не поддерживают создание инстанса если конструктор с параметрами. Так же для XML-сериализации требуется наличие конструктора без параметров, а значит у вас уже не будет той гарантии, ради которой конструкторы вообще задумывались.

Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов. Зато можно добавить новый метод Init.

И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).

Прихожу к пониманию что конструктор с параметрами не гибок и имеет больше минусов, чем плюсов. И лучшее решение всегда создавать конструктор без параметров + метод Init, если нужны параметры для инициализации класса. Если повторная инициализация запрещена -- это легко решается с помощью булевого поля _isInitialized.

Согласны?
Отредактировано 31.03.2016 13:28 Shmj . Предыдущая версия . Еще …
Отредактировано 31.03.2016 13:22 Shmj . Предыдущая версия .
Re[15]: Конструктор с параметрами vs метод Init -- стоит ли и
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 07.04.16 18:33
Оценка: 4 (1) +1 :)
Здравствуйте, Shmj, Вы писали:

S>А вы Unity использовали? По умолчанию (если при регистрации не укажите иное) в каждом из случаев будет создан новый объект.

Некорректный подход. Идея IoC в том, чтобы избавить пользовательский код от управления временем жизни сервисов. А у вас классический набор граблей — stateful сервисы + закладывание на определённое поведение Unity, одной из фишек которой является самостоятельное управление lifetime'ом сервисов (в том числе через конфиги, ага). В данном случае (если нельзя сервис сделать stateless, но всё ещё хочется отдать его на откуп IoC) правильным решением будет регистрация в IoC фабрики + ручное управление состоянием в прикладном коде (хм...да....).
[КУ] оккупировала армия.
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: Sinix  
Дата: 31.03.16 13:42
Оценка: 3 (1) +2
Здравствуйте, Shmj, Вы писали:

S>Согласны?

Нет. Это классический "я не хочу учиться готовить кошек, поэтому кошки не нужны". Проблема не в кошках, проблема в том, что 99% их не готовят.
Серьёзно, у вас половина вопросов — это готовая иллюстрация XY problem, переучивайтесь


S>К примеру, Generics не поддерживают создание инстанса если конструктор с параметрами.

factory method, можно автоматом кэшировать делегат на конструктор. Буквально минута гуглинга на обсуждение с workaround-ом.


S>Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов

factory method. Ну и про "добавить новый метод" говорить в принципе некорректно. Изучите, как оно работает.

S>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).

factory method / static interfaces proposal.
Re: Конструктор с параметрами vs метод Init -- стоит ли использовать конструктор
От: _NN_ www.nemerleweb.com
Дата: 31.03.16 12:10
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>Согласны?


Нет конечно
Обычно метод Init не нужен, потому как возникают сразу множество проблем контролирования состояния.
Что если два раза кто-нибудь вызовет ? Из двух потоков ? И т.п.

С обобщениями действительно проблема и есть предложение, однако не так часто нужно и всегда есть обходные пути.
Насчёт сериализации, то для объектов-данных (data object) вообще не нужен конструктор и логика.

Есть предложение о добавления конструктора и расширения класса методами, но это не повод создавать всегда двухуровневую иницализацию.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: Конструктор с параметрами vs метод Init -- стоит ли и
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.03.16 23:35
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>и получаете тот, который ранее зарегистрировали.


Ну и дальше надо смотреть как в Unity позвать конструктор с параметром. Еслли никак — выкинуть его в мусорку, IoC контейнеров как грязи (и все уродские, но это отдельный разговор).
Беглый гуглинг подсказал что есть, во-первых, метод RegisterInstance, а во-вторых перегрузки с LifetimeManager в параметрах.

S>Т.е. разработка начинается с контракта.


А если у тестового сервиса какой нибудь специфичный параметр понадобится? К примеру, путь к наборам тестовых данных. Тогда что?

S>И вот у меня дилемма с настройками в примере выше. Включать ли в контракты сведения о настройках.


Какой вопрос — такой ответ: зависит от настроек и дизайна.

S>3. Фабричный метод который тут предлагали. Если я правильно понял, предлагается создать некий IFileServiceFactory с методом CreateFileService(Settings settings)


Часто достаточно просто Func<IFileService>.

S> а потом еще и реализацию для него. Реализацию так же получать через Unity, к примеру.


Зачем?

P.S. А вообще предлагаю начать с того зачем вообще тебе понадобился Unity.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[13]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Sinix  
Дата: 01.04.16 12:40
Оценка: +1 -1
Здравствуйте, Shmj, Вы писали:

S>>Здравствуй, гонки.

S>Ну смотрите, простое правило. Тот кто вызвал конструктор -- вызывает и Init. Никаких проблем.
Проблема в том, что вы не смотрите на реальные сценарии использования, и вместо этого предлагаете "простые правила".

Вот есть у вас две библиотеки плагинов. И ваш вариант с Init:
// assembly A:
var print = IOC.Resolve<IPintService>();
print.Init(initOptions1);

// assembly b:
var print2 = IOC.Resolve<IPintService>();
print2.Init(initOptions2);

Как между конфликт разруливать будете?
Re[23]: Конструктор с параметрами vs метод Init -- стоит ли
От: Strategy  
Дата: 09.04.16 08:38
Оценка: 6 (1)
S>По этому вы можете сделать Resolve<IMyService>, вместо Resolve<IMyServiceFactory>.CreateMyService(settings);

Пользователь службы не будет использовать ни Resolve<IMyService>, ни Resolve<IMyServiceFactory>.CreateMyService(settings) для получения экземпляра службы.

Вместо этого класс, которому требуется служба для выполнения своих функций, запросит в конструкторе

либо службу IMyService

class ServiceUser {

    public ServiceUser(IMyService MyService) {

        var File = MyService.Download(URL);
    }
}


либо фабрику службы IMyServceFactory, если хочет создавать много служб с разными настройками

class ServiceUser {

    public ServiceUser(IMyServiceFactory MyServiceFactory) {
    
        var MyService = MyServiceFactory.CreateMyService(Settings);

        var File = MyService.Download(URL);
     }
}


либо лямбда-функцию Func<Settings, IMyService>, если не хочет расписывать отдельный фабричный интерфейс

class ServiceUser {

    public ServiceUser(Func<Settings, IMyService> MyServiceFactory) {
    
        var MyService = MyServiceFactory(Settings);

        var File = MyService.Download(URL);
    }
}


И обязанность контейнера / загрузчика предоставить всё это именно в том виде, в котором запросит пользователь через конструктор.

Можешь считать фабрику IMyServiceFactory и Func<Settings, IMyService> тем самым "интерфейсом, который описывает конструктор с нужным набором параметров и заменяет Init".
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.04.16 02:28
Оценка: 4 (1)
Здравствуйте, Shmj, Вы писали:

S>Зато конструктор имеет много минусов. К примеру, Generics не поддерживают создание инстанса если конструктор с параметрами.


Описал коряво, но суть ясна. Это проблема не конструктора, а языка. В Немерле ее нет. В Шарпе тоже можно устранить.

S>Так же для XML-сериализации требуется наличие конструктора без параметров,


А это проблема XML-сериализатора.

S>Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов.


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

Какие на фиг методы-расширения, если их не к чему применять?

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

S>Зато можно добавить новый метод Init.


Куда?

S>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).


Это уже проблема интерфейсов. Если сделать статические интерфейсы, то в принципе можно сделать будет. Но интерфейс принципиально реализуется в объекта, а при создании объекта самого объекта еще нет.

Короче, еще один притянутый за уши "аргумент".

S>Прихожу к пониманию что конструктор с параметрами не гибок и имеет больше минусов, чем плюсов. И лучшее решение всегда создавать конструктор без параметров + метод Init, если нужны параметры для инициализации класса. Если повторная инициализация запрещена -- это легко решается с помощью булевого поля _isInitialized.


S>Согласны?


Согласен с оговоркой. Реализация конструкторов в Шарпе не гибка. Пути увеличения гибкости:
1. Вывод типов позволяющий обойтись без указания параметров типов (есть в Немерле).
2. Возможность использовать конструктор как функцию — передавать туда где требуется делегат (опять же реализовано в Немерле).
3. Можно позволить описывать виртуальные статические функции, т.е. виртуальные методы класса (а не экзмпляра). Это позволит гибче подменять конструктры (есть в Дельфи). По сути это инкапсуляция фабричного метода.
4. Вопрос малость спорный, но все же, еще можно позволить не указывать ключевое слово new. При описании иерархий объектов это дает более чистый код. Но это приводит к неоднозначностям которые должен решать типизатор (что замедляет вывод типов). Опять же есть в Немерле, но на счет этой фичи я не уверен. Проголосовал бы за нее, если был бы уверен, что есть четкие правила разрешение неоднозначностей.

К однозначным плюсам конструкторов относится то, что только в них можно назначать неизменяемые поля. Лично я часто использую неизменяемые объекты, а для них альтернативы конструкторам в донете нет (джит не даст).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[16]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 20:45
Оценка: -1
Здравствуйте, Sinix, Вы писали:

S>Как вы собираетесь гарантировать, что такое изменение (отдавали один экземпляр -> отдаём несколько) не поломает уже существующий код?

S>Откуда об этом изменении узнают разработчики плагинов?

Описать что после создания объекта его нужно единожды инициализировать
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: barn_czn  
Дата: 04.05.16 09:13
Оценка: +1
S>Согласны?

Нет. Универсальных решений нет. Я всегда за конструктор, если он возможен. И за readonly поля.
С конструктором есть еще одна проблема — async/await. Внутри конструктора нельзя сделать await.
Но даже эту проблему можно обойти:


public class MyClass
{
private MyClass()
{
}

private async Task Init()
{
    //await Any();
}

public static async Task<MyClass> New()
{
    var res = new MyClass();
    await res.Init();
    return res;
}

}


— вот и все! ваш Init() закрыт и никто не вызовет его 2жды.
Так что подумайте 10раз делая public Init.
Re[2]: Конструктор с параметрами vs метод Init -- стоит ли использовать конструк
От: Sharov Россия  
Дата: 31.03.16 12:20
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN> Из двух потоков ? И т.п.


Вот это для конструктора как раз не проблема. Там унутрях блокировка ставиться.
Кодом людям нужно помогать!
Re[2]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 31.03.16 13:25
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Нет конечно

_NN>Обычно метод Init не нужен, потому как возникают сразу множество проблем контролирования состояния.

А как насчет контрактов (interface). Как там описать конструктор?

_NN>Что если два раза кто-нибудь вызовет ? Из двух потоков ? И т.п.


По поводу двух раз -- уже написал, достаточно 1 поля для проверки.

По поводу потоков. Инстанс класса считается не потокобезопасным, если не указано иное. Если хотите сделать потокобезопасным -- то вам нужно озаботиться не только этим методом, но и всеми другими.

_NN>С обобщениями действительно проблема и есть предложение, однако не так часто нужно и всегда есть обходные пути.

_NN>Насчёт сериализации, то для объектов-данных (data object) вообще не нужен конструктор и логика.

А проверка того что все обязательные поля установлены?

_NN>Есть предложение о добавления конструктора и расширения класса методами, но это не повод создавать всегда двухуровневую иницализацию.


При использовании контрактов двухуровневой не избежать.
Отредактировано 31.03.2016 13:25 Shmj . Предыдущая версия .
Re[3]: Конструктор с параметрами vs метод Init -- стоит ли и
От: _NN_ www.nemerleweb.com
Дата: 31.03.16 13:35
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, _NN_, Вы писали:


_NN>>Нет конечно

_NN>>Обычно метод Init не нужен, потому как возникают сразу множество проблем контролирования состояния.

S>А как насчет контрактов (interface). Как там описать конструктор?

Тут конечно проблема. Пока нет такой возможности. Надеемся на светлое будущее.

_NN>>Что если два раза кто-нибудь вызовет ? Из двух потоков ? И т.п.


S>По поводу двух раз -- уже написал, достаточно 1 поля для проверки.

И проверять его в каждом методе, в каждом свойстве.

S>По поводу потоков. Инстанс класса считается не потокобезопасным, если не указано иное. Если хотите сделать потокобезопасным -- то вам нужно озаботиться не только этим методом, но и всеми другими.

У вас путаница.
Создание объекта через обычный конструктор это полностью потокобезопасно.
Вы не можете вызвать конструктор два раза и тем более из двух потоков, а вот метод Init запросто.

Кстати мы ещё не упомянули наследование, что если забудем вызвать базовый Init ? С конструкторами то ничего не забудем.

_NN>>С обобщениями действительно проблема и есть предложение, однако не так часто нужно и всегда есть обходные пути.

_NN>>Насчёт сериализации, то для объектов-данных (data object) вообще не нужен конструктор и логика.

S>А проверка того что все обязательные поля установлены?

Это разные вещи.
Данные это данные, а логика это логика.
Интерфейс ISerializable позволяет полностью всё проконтролировать если очень нужно.

_NN>>Есть предложение о добавления конструктора и расширения класса методами, но это не повод создавать всегда двухуровневую иницализацию.


S>При использовании контрактов двухуровневой не избежать.

Можно пример ?


Как вариант можете просто написать свой анализатор кода и всё контролировать извне.
Есть ещё вариант в виде библиотеки: https://github.com/default0/Introspect
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 31.03.16 14:59
Оценка:
Здравствуйте, _NN_, Вы писали:

S>>При использовании контрактов двухуровневой не избежать.

_NN>Можно пример ?

Упрощенный пример:

  Скрытый текст
using System;

namespace ConsoleApplication30
{
    class Program
    {
        class Settings
        {
            public string LocalFolder { get; set; }
        }

        interface IFileService
        {
            // Нужно иметь гарантию что установлены настройки
            void Init(Settings settings);

            void DownloadFile(string fileName);
        }

        class TestFileService : IFileService
        {
            private Settings _settings;

            public void Init(Settings settings)
            {
                _settings = settings;
            }

            public void DownloadFile(string fileName)
            {
                Console.WriteLine("Скачиваем файл " + fileName + " и сохраняем в папку " + _settings.LocalFolder);
            }
        }

        static void Main(string[] args)
        {
            // Получаем необходимую реализацию через unityContainer
            //IFileService fileService = unityContainer.Resolve<IFileService>()
            // Для упрощения заменим:
            IFileService fileService = new TestFileService();

            // Вместо Init можно было бы задействовать InjectionConstructor, но это никак бы не отразилось на контракте.
            // По этому в контракте нужно либо сделать свойство Settings, либо метод Init. Поскольку при инициализации не просто устанавливаем значение, а и производим действия -- то метод.
            fileService.Init(new Settings {LocalFolder = "C:\\temp"});

            fileService.DownloadFile("fileName.txt");
        }
    }
}


_NN>Как вариант можете просто написать свой анализатор кода и всё контролировать извне.

_NN>Есть ещё вариант в виде библиотеки: https://github.com/default0/Introspect

Я бы предпочел максимально простой вариант, без доп. библиотек, когда можно без них.
Отредактировано 31.03.2016 15:01 Shmj . Предыдущая версия .
Re[2]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 31.03.16 15:00
Оценка:
Здравствуйте, Sinix, Вы писали:

S>>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).

S>factory method / static interfaces proposal.

Вот вам пример упрощенный с описанием проблемы:

  Скрытый текст
using System;

namespace ConsoleApplication30
{
    class Program
    {
        class Settings
        {
            public string LocalFolder { get; set; }
        }

        interface IFileService
        {
            // Нужно иметь гарантию что установлены настройки
            void Init(Settings settings);

            void DownloadFile(string fileName);
        }

        class TestFileService : IFileService
        {
            private Settings _settings;

            public void Init(Settings settings)
            {
                _settings = settings;
            }

            public void DownloadFile(string fileName)
            {
                Console.WriteLine("Скачиваем файл " + fileName + " и сохраняем в папку " + _settings.LocalFolder);
            }
        }

        static void Main(string[] args)
        {
            // Получаем необходимую реализацию через unityContainer
            //IFileService fileService = unityContainer.Resolve<IFileService>()
            // Для упрощения заменим:
            IFileService fileService = new TestFileService();

            // Вместо Init можно было бы задействовать InjectionConstructor, но это никак бы не отразилось на контракте.
            // По этому в контракте нужно либо сделать свойство Settings, либо метод Init. Поскольку при инициализации не просто устанавливаем значение, а и производим действия -- то метод.
            fileService.Init(new Settings {LocalFolder = "C:\\temp"});

            fileService.DownloadFile("fileName.txt");
        }
    }
}


Напишите как правильно.
Отредактировано 31.03.2016 15:01 Shmj . Предыдущая версия .
Re[5]: Конструктор с параметрами vs метод Init -- стоит ли и
От: _NN_ www.nemerleweb.com
Дата: 31.03.16 15:12
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, _NN_, Вы писали:


S>>>При использовании контрактов двухуровневой не избежать.

_NN>>Можно пример ?

S>Упрощенный пример:


S>
  Скрытый текст
S>
S>using System;

S>namespace ConsoleApplication30
S>{
S>    class Program
S>    {
S>        class Settings
S>        {
S>            public string LocalFolder { get; set; }
S>        }

S>        interface IFileService
S>        {
S>            // Нужно иметь гарантию что установлены настройки
S>            void Init(Settings settings);

S>            void DownloadFile(string fileName);
S>        }

S>        class TestFileService : IFileService
S>        {
S>            private Settings _settings;

S>            public void Init(Settings settings)
S>            {
S>                _settings = settings;
S>            }

S>            public void DownloadFile(string fileName)
S>            {
S>                Console.WriteLine("Скачиваем файл " + fileName + " и сохраняем в папку " + _settings.LocalFolder);
S>            }
S>        }

S>        static void Main(string[] args)
S>        {
S>            // Получаем необходимую реализацию через unityContainer
S>            //IFileService fileService = unityContainer.Resolve<IFileService>()
S>            // Для упрощения заменим:
S>            IFileService fileService = new TestFileService();

S>            // Вместо Init можно было бы задействовать InjectionConstructor, но это никак бы не отразилось на контракте.
S>            // По этому в контракте нужно либо сделать свойство Settings, либо метод Init. Поскольку при инициализации не просто устанавливаем значение, а и производим действия -- то метод.
S>            fileService.Init(new Settings {LocalFolder = "C:\\temp"});

S>            fileService.DownloadFile("fileName.txt");
S>        }
S>    }
S>}

S>



Пример похоже слишком простой.
Неясно почему тот же TestFileServices не может в конструкторе принимать Settings.
И вообще какое дело интерфейсу до этого.

Интерфейс описывает желаемое поведение, а как оно задается это дело конкретного класса.
interface IFileService
{
  void DownloadFile(string fileName);
}

class TestFileService : IFileService
{
 public TestFilerService(Settings settings) { .. }
 public void DownloadFile(string fileName) { ... }
}


class AdvancedTestFileService : IFileService
{
 public AdvancedTestFileService(AdvancedSettings settings) { .. } // Тут требуем другие настройки
 public void DownloadFile(string fileName) { ... }
}


class DefaultFileService : IFileService
{
 public DefaultFileService() { .. } // Тут например не требуется ничего.
 public void DownloadFile(string fileName) { ... }
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[6]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 31.03.16 17:24
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Интерфейс описывает желаемое поведение, а как оно задается это дело конкретного класса.


А что возможность задавать свойства по ошибке в интерфейсы добавили? К примеру см. интерфейс IRelatedEnd. Там и свойства и методы, т.е. не только поведение.

Если я хочу чтобы для всех реализаций были изначально заданы настройки и произведены действия по инициализации с этими настройками. Код, который работает с контрактами ничего не знает о конкретных реализациях. И тем не менее конкретные реализации должны быть проинициализированы с настройками.
Отредактировано 31.03.2016 17:27 Shmj . Предыдущая версия .
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.03.16 21:14
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Зато конструктор имеет много минусов. К примеру, Generics не поддерживают создание инстанса если конструктор с параметрами. Так же для XML-сериализации требуется наличие конструктора без параметров, а значит у вас уже не будет той гарантии, ради которой конструкторы вообще задумывались.


А два конструктора, один с параметрами другой без — религия не позволяет иметь?

S>Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов. Зато можно добавить новый метод Init.


Тоже нельзя. Extensions ничего в классы не добавляют, это просто инфиксная форма записи обычного статического метода.

S>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).


Это не проблема, это фича. Если тебе нужен констракт на создание экземпляра — есть такой паттерн, фабрика.

S>Согласны?


Нет.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[2]: Конструктор с параметрами vs метод Init -- стоит ли использо
От: Shmj Ниоткуда  
Дата: 31.03.16 21:23
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>А два конструктора, один с параметрами другой без — религия не позволяет иметь?


Тогда теряется преимущество -- гарантия того что все обязательные поля проинициализированы. Ведь могут вызвать не тот конструктор.

S>>Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов. Зато можно добавить новый метод Init.


AVK>Тоже нельзя. Extensions ничего в классы не добавляют, это просто инфиксная форма записи обычного статического метода.


Да мне без разницы как оно внутри устроено. Я что компиляторы или решарперы пишу? Для бизнес-приложения выглядит так, как будто появился новый инстанциональный метод. И это красиво. Можно добавить Init с нужными параметрами а вот чтобы создать класс как бы новым конструктором -- нельзя.

S>>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).

AVK>Это не проблема, это фича. Если тебе нужен констракт на создание экземпляра — есть такой паттерн, фабрика.

Вот пример:

  Скрытый текст
using System;

namespace ConsoleApplication30
{
    class Program
    {
        class Settings
        {
            public string LocalFolder { get; set; }
        }

        interface IFileService
        {
            // Нужно иметь гарантию что установлены настройки
            void Init(Settings settings);

            void DownloadFile(string fileName);
        }

        class TestFileService : IFileService
        {
            private Settings _settings;

            public void Init(Settings settings)
            {
                _settings = settings;
            }

            public void DownloadFile(string fileName)
            {
                Console.WriteLine("Скачиваем файл " + fileName + " и сохраняем в папку " + _settings.LocalFolder);
            }
        }

        static void Main(string[] args)
        {
            // Получаем необходимую реализацию через unityContainer
            //IFileService fileService = unityContainer.Resolve<IFileService>()
            // Для упрощения заменим:
            IFileService fileService = new TestFileService();

            // Вместо Init можно было бы задействовать InjectionConstructor, но это никак бы не отразилось на контракте.
            // По этому в контракте нужно либо сделать свойство Settings, либо метод Init. Поскольку при инициализации не просто устанавливаем значение, а и производим действия -- то метод.
            fileService.Init(new Settings {LocalFolder = "C:\\temp"});

            fileService.DownloadFile("fileName.txt");
        }
    }
}


Покажите на этом примере как правильно.
Re[3]: Конструктор с параметрами vs метод Init -- стоит ли использо
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.03.16 21:40
Оценка:
Здравствуйте, Shmj, Вы писали:

AVK>>А два конструктора, один с параметрами другой без — религия не позволяет иметь?

S>Тогда теряется преимущество -- гарантия того что все обязательные поля проинициализированы. Ведь могут вызвать не тот конструктор.

Только тут проблема не в конструкторах, а в твоих требованиях

AVK>>Тоже нельзя. Extensions ничего в классы не добавляют, это просто инфиксная форма записи обычного статического метода.

S>Да мне без разницы как оно внутри устроено.

Оно устроено так снаружи.

S> Я что компиляторы или решарперы пишу? Для бизнес-приложения выглядит так, как будто появился новый инстанциональный метод.


Нет, не выглядит.

S> Можно добавить Init с нужными параметрами


Нельзя. Потому что Init имеет смысл только если он полиморфный. А статический метод полиморфным быть не может.

S>Покажите на этом примере как правильно.


Ничего не знаю про Unity, поэтому не покажу.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[4]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 31.03.16 22:41
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>Покажите на этом примере как правильно.

AVK>Ничего не знаю про Unity, поэтому не покажу.

В данном примере в Unity делаете регистрацию:

container.RegisterType<IFileService, TestFileService>();


А потом, когда требуется инстанс, пишите:

container.Resolve<IFileService>();


и получаете тот, который ранее зарегистрировали.

У меня парадигма такая:

1. Сначала пишутся контракты (interface) сервисов и бизнес-сущности (можно было бы тоже interface, но для простоты просто классы, т.к. там только поля).
2. Потом параллельно делается внедрение этих контрактов в рабочую библиотеку с тестовыми реализациями и одновременно их реализация (может разными людьми).
3. Замена тестовых заглушек на реальные происходит через Unity, т.е. в рабочем коде ничего изменять не нужно (только переконфигурировать все в 1 месте (там где RegisterType)).

Т.е. разработка начинается с контракта.

Нужно для распараллеливания процесса разработки, структуризации проекта и для упрощения тестирования.

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

Варианты такие:

1. Оставить метод Init(Settings settings). Тогда он берет на себя роль конструктора. Как бы ничего, но доп. проверки и нужно не забывать его вызывать.

2. Оставить в библиотеке контрактов класс Settings, но никак не отображать инициализацию в контрактах (т.е. убрать метод Init(Settings settings)). Тогда можно задействовать конструктор и фичу Unity InjectionConstructor. Можно создавать экземпляр класса через Unity Resolve<IFileService> и передавать нужное значение в конструктор. Тут проблема -- нужно не забывать передавать этот InjectionConstructor а так же требуется чтобы во всех реализациях интерфейса был конструктор с нужным параметром (просто помнить -- никакой подсказки от компилятора не будет).

3. Фабричный метод который тут предлагали. Если я правильно понял, предлагается создать некий IFileServiceFactory с методом CreateFileService(Settings settings) а потом еще и реализацию для него. Реализацию так же получать через Unity, к примеру. Однако же в таком случае нужно помнить что TestFileService/FileService нельзя создавать вручную -- только через реализацию IFileServiceFactory. И не ясно чем это так уж лучше Init.
Отредактировано 31.03.2016 22:42 Shmj . Предыдущая версия .
Re[6]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 10:12
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Часто достаточно просто Func<IFileService>.

AVK>P.S. А вообще предлагаю начать с того зачем вообще тебе понадобился Unity.

А вы что для реализации IoC никакого фреймворка не используете? Только свою Func<IFileService>?
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: v6  
Дата: 01.04.16 10:28
Оценка:
Здравствуйте, Shmj, Вы писали:

S>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).


Не уверен, что это проблема. Интерфейс определяет поведение объекта. А на момент вызова конструктора объекта еще нет. С точки зрения поведения объекта это некоторая точка сингулярности.
Чтобы это было непротиворечиво можно вводить какие-то статические интерфейсы, но ценность этого мне не очевидна.

S>Прихожу к пониманию что конструктор с параметрами не гибок и имеет больше минусов, чем плюсов. И лучшее решение всегда создавать конструктор без параметров + метод Init, если нужны параметры для инициализации класса. Если повторная инициализация запрещена -- это легко решается с помощью булевого поля _isInitialized.


S>Согласны?


Нет.
Re[7]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Sinix  
Дата: 01.04.16 10:30
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А вы что для реализации IoC никакого фреймворка не используете? Только свою Func<IFileService>?


Ну вот я знал же, что это очередной XY problem. Прям как по учебнику

Правило большого пальца: если что-то не получается, то надо не обвинять язык/фреймворк, а сесть и изучить матчасть.
Скорее всего за вас на все грабли уже понаступали и решение выложили, осталось только найти и усвоить.

Теперь собственно ответ:
Инициализация сервиса — это ответственность IOC-фреймворка, а не конечного потребителя.
Для Unity это емнип LifetimeManager + resolve with parameters.
также см
http://mikaelkoskinen.net/post/unity-passing-constructor-parameters-to-resolve
Re[2]: Конструктор с параметрами vs метод Init -- стоит ли использо
От: Shmj Ниоткуда  
Дата: 01.04.16 10:39
Оценка:
Здравствуйте, v6, Вы писали:

v6>Не уверен, что это проблема. Интерфейс определяет поведение объекта. А на момент вызова конструктора объекта еще нет. С точки зрения поведения объекта это некоторая точка сингулярности.


Ваша теория расходится с практикой. См. IRelatedEnd. Там и свойства и методы в одном интерфейсе.

v6>Чтобы это было непротиворечиво можно вводить какие-то статические интерфейсы, но ценность этого мне не очевидна.


Почему инициализация определенными параметрами не может быть частью контракта
Re[8]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 10:43
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Теперь собственно ответ:

S>Инициализация сервиса — это ответственность IOC-фреймворка, а не конечного потребителя.

Почему инициализация не может быть частью контакта

S>Для Unity это емнип LifetimeManager + resolve with parameters.

S>также см
S>http://mikaelkoskinen.net/post/unity-passing-constructor-parameters-to-resolve

Да проблема то не в этом. Вы описали мой вариант 2: http://rsdn.ru/forum/dotnet/6402172.1
Автор: Shmj
Дата: 01.04.16


Мне хочется чтобы инициализация была частью интерфейса и чтобы была гарантия что сервис проинициализирован конкретными данными.
Re[3]: Конструктор с параметрами vs метод Init -- стоит ли использо
От: v6  
Дата: 01.04.16 10:50
Оценка:
Здравствуйте, Shmj, Вы писали:

v6>>Не уверен, что это проблема. Интерфейс определяет поведение объекта. А на момент вызова конструктора объекта еще нет. С точки зрения поведения объекта это некоторая точка сингулярности.


S>Ваша теория расходится с практикой. См. IRelatedEnd. Там и свойства и методы в одном интерфейсе.


Не вижу противоречия, можно пояснить? Свойства они все равно относятся к конкретному экземпляру объекта. Заменяем мысленно на пару методов get_Prop set_Prop и вот в интерфейсе только методы.
А что будет делать конструктор в интерфейсе объекта? Создавать _новый_ объект? Ну так пусть идет в интерфейс фабрики, тут-то он зачем нужен?

v6>>Чтобы это было непротиворечиво можно вводить какие-то статические интерфейсы, но ценность этого мне не очевидна.


S>Почему инициализация определенными параметрами не может быть частью контракта


Может быть частью контракта. Просто это другой контракт получается.
Re[7]: Конструктор с параметрами vs метод Init -- стоит ли и
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 01.04.16 10:57
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А вы что для реализации IoC никакого фреймворка не используете?


А вообще не реализую IoC контейнер без необходимости. Под необходимостью я понимаю исключительно наличие динамически подгружаемых расширений.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[9]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Sinix  
Дата: 01.04.16 11:00
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Почему инициализация не может быть частью контакта


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

Это всё равно что метод "выдай мне вот такое яблоко" считать частью контракта самого яблока


S>Мне хочется чтобы инициализация была частью интерфейса и чтобы была гарантия что сервис проинициализирован конкретными данными.

По рукам надо за такое бить. При таком дизайне чтобы огрести кучу головной боли достаточно отдать один и тот же экземпляр сервиса нескольким потребителям.
Re[10]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 12:22
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Кэп: потому что она часть другого контракта, по получению экземпляра сервиса.

S>Это всё равно что метод "выдай мне вот такое яблоко" считать частью контракта самого яблока

Не совсем. Это как вы покупаете новый телефон. Телефон -- это новый экземпляр.

А уже потом вы его инициализируете своим аккаунтом гугле или апппле. Причем делаете это 1 раз.

Инициализацию должен проводить пользователь. Так удобнее чем передавать в магазин данные вашего аккаунта, чтобы они сами прописали и дали уже вам готовый проинициализированный объект.

Когда в следующий раз будете покупать телефона -- вспомните мои слова. И так всю жизнь теперь будете вспоминать, особенно если сейчас будете возражать.

S>>Мне хочется чтобы инициализация была частью интерфейса и чтобы была гарантия что сервис проинициализирован конкретными данными.

S>По рукам надо за такое бить. При таком дизайне чтобы огрести кучу головной боли достаточно отдать один и тот же экземпляр сервиса нескольким потребителям.

Повторная попытка инициализации -- InvalidOperationException. Просто помнить что кто первым запросил -- тот и инициализирует.
Re[11]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Sinix  
Дата: 01.04.16 12:29
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Не совсем. Это как вы покупаете новый телефон. Телефон -- это новый экземпляр.

S>А уже потом вы его инициализируете своим аккаунтом гугле или апппле. Причем делаете это 1 раз.

Не работает такая аналогия с IOC. Почему — уже написал.
Для вашего случая надо или использовать варианты, что расписал выше,
или загонять в IOC фабрику, которая и будет создавать, настраивать и отдавать нужный вам экземпляр.

S>>По рукам надо за такое бить. При таком дизайне чтобы огрести кучу головной боли достаточно отдать один и тот же экземпляр сервиса нескольким потребителям.

S>Повторная попытка инициализации -- InvalidOperationException. Просто помнить что кто первым запросил -- тот и инициализирует.
Здравствуй, гонки.
Re[12]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 12:32
Оценка:
Здравствуйте, Sinix, Вы писали:

S>>Повторная попытка инициализации -- InvalidOperationException. Просто помнить что кто первым запросил -- тот и инициализирует.

S>Здравствуй, гонки.

Ну смотрите, простое правило. Тот кто вызвал конструктор -- вызывает и Init. Никаких проблем.
Re[14]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 01.04.16 14:08
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вот есть у вас две библиотеки плагинов. И ваш вариант с Init:

S>
S>// assembly A:
S>var print = IOC.Resolve<IPintService>();
S>print.Init(initOptions1);

S>// assembly b:
S>var print2 = IOC.Resolve<IPintService>();
S>print2.Init(initOptions2);
S>

S>Как между конфликт разруливать будете?

А вы Unity использовали? По умолчанию (если при регистрации не укажите иное) в каждом из случаев будет создан новый объект.

Если вы нехотите чтобы создавался новый объект при каждом Resolve, то регистрируйте уже готовый объект, уже проинициализированный.
Re[15]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Sinix  
Дата: 01.04.16 14:17
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А вы Unity использовали?

Вы вопрос не поняли.
Как вы собираетесь гарантировать, что такое изменение (отдавали один экземпляр -> отдаём несколько) не поломает уже существующий код?
Откуда об этом изменении узнают разработчики плагинов?
Re[3]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Strategy  
Дата: 01.04.16 15:02
Оценка:
S>Напишите как правильно.

1. Если путь загрузки не меняется на протяжении жизни службы файлов, то можно сделать интерфейс ISettingsProvider со свойством LocalFolder и передавать его в конструктор класса TestFileService.

2. Если нужно менять путь для конкретной загрузки, то лучше переделать метод загрузки в DownloadFile(FileName, LocalFolder).

3. Если нужно менять путь для конкретной загрузки, но не хочется переделывать метод загрузки, то можно использовать фабрику службы файлов, которая будет каждый раз перед загрузкой файла создавать IFileService для указанного пути или набора настроек.
Re[16]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 07.04.16 19:54
Оценка:
Здравствуйте, koandrew, Вы писали:

K>В данном случае (если нельзя сервис сделать stateless, но всё ещё хочется отдать его на откуп IoC) правильным решением будет регистрация в IoC фабрики + ручное управление состоянием в прикладном коде (хм...да....).


Именно к такому решению я и пришел. Вы первый кто об этом написали.
Re[17]: Конструктор с параметрами vs метод Init -- стоит ли и
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 07.04.16 20:36
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Именно к такому решению я и пришел. Вы первый кто об этом написали.


Откровенно говоря, это решение не особенно лучше, ибо сейчас вы закладываетесь на определённое поведение фабрики (в частности, что она каждый раз будет возвращать новый экземпляр объекта). То есть вы де-факто "прибиваете" пользовательский код к конкретной реализации фабрики. Осталось сделать последний логичный шаг — вместо лукапа в контейнере по интерфейсу фабрики сделать прямой вызов фабрики. Ну или сделать лукап конкретного класса фабрики в контейнере — если сама фабрика имеет зависимости, которые резолвятся IoC. Я в последний раз использовал Юнити несколько лет назад, и тогда она умела так делать. Как сейчас — не в курсе, но не думаю, что эту фичу открутили.
То есть смысл в том, что если уж есть зависимость от конкретной имплементации, то лучше выразить эту зависимость явно в коде путём прямого референса. Так будет меньше граблей при поддержке. Из своего опыта — я пробовал обе крайности, и оптимальным с точки зрения и разработки, и дальнейшей поддержки (особенно если поддерживать будут другие люди, а не изначальная команда) оказался комбинированный подход — то есть там, где клиентский код не завязан на детали реализации сервиса, используем интерфейс, там же, где такая зависимость есть (хоть такого по идее и не должно быть, но все мы знаем, что "в теории практика не должна отличаться от теории, на практике же обычно отличается" (С)) — использовать явный референс.
[КУ] оккупировала армия.
Re[17]: Конструктор с параметрами vs метод Init -- стоит ли
От: Berill Азербайджан  
Дата: 07.04.16 21:59
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Sinix, Вы писали:


S>>Как вы собираетесь гарантировать, что такое изменение (отдавали один экземпляр -> отдаём несколько) не поломает уже существующий код?

S>>Откуда об этом изменении узнают разработчики плагинов?

S>Описать что после создания объекта его нужно единожды инициализировать

Странный у вас подход... Значит правило "после создания объекта его нужно единожды инициализировать" вы можете описать, а правило "прежде чем передать в конструктор сервиса настройки — создай их и проверь корректность" вы не можете... т.е. один тип соглашений вас устраивает, а другой нет...
Отредактировано 07.04.2016 22:00 Berill . Предыдущая версия .
Re[18]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 07.04.16 23:15
Оценка:
Здравствуйте, Berill, Вы писали:

B>Странный у вас подход... Значит правило "после создания объекта его нужно единожды инициализировать" вы можете описать, а правило "прежде чем передать в конструктор сервиса настройки — создай их и проверь корректность" вы не можете... т.е. один тип соглашений вас устраивает, а другой нет...


Проблема в том что конструктор не описывается интерфейсами и х.з. что в него передавать. Нужно каждый раз описывать все параметры, которые передавать при unityContainer.Resolve

А то что нужно единожды вызвать Init после отказа от концепции конструкторов с параметрами -- это правило можно применять для всего проекта и описать/запомнить 1 раз.
Re[19]: Конструктор с параметрами vs метод Init -- стоит ли
От: Berill Азербайджан  
Дата: 08.04.16 08:23
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Berill, Вы писали:


B>>Странный у вас подход... Значит правило "после создания объекта его нужно единожды инициализировать" вы можете описать, а правило "прежде чем передать в конструктор сервиса настройки — создай их и проверь корректность" вы не можете... т.е. один тип соглашений вас устраивает, а другой нет...


S>Проблема в том что конструктор не описывается интерфейсами и х.з. что в него передавать. Нужно каждый раз описывать все параметры, которые передавать при unityContainer.Resolve


S>А то что нужно единожды вызвать Init после отказа от концепции конструкторов с параметрами -- это правило можно применять для всего проекта и описать/запомнить 1 раз.


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

С другой стороны, имхо, стоит задуматься над следующим. Если каждый раз когда вам требуется объект сервиса, вы проделываете unityContainer.Resolve, то принципиально это ничем не отличается от вызова фабричного метода. В свою очередь, это намекает на то, что вы, скорее всего, протаскиваете свой объект юнити-контейнера туда, где вы его используете. Лично у меня складывается такое впечатление. И тут встает закономерный вопрос, а нужен ли этот юнити вообще?!

Если посмотреть на код метода Init (который вы приводите), то опять же, он вам не гарантирует корректность переданных настроек. Если вам еще и это надо проверять, то спросите себя: а должен ли ваш объект сервиса этим заниматься?!
Если у вас задача: создать объект сервиса с корректными настройками — то я бы разделил это на 2 подзадачи:
1. Проверка корректности настроек
2. Передача корректных настроек сервису либо при его создании (если эти же настройки используются не только для одного метода), либо в сам метод, где эти настройки используются

Все это вам описали в первом сообщении 4-ой страницы:

От: Strategy
Дата: 01.04.16 19:02
S>Напишите как правильно.

1. Если путь загрузки не меняется на протяжении жизни службы файлов, то можно сделать интерфейс ISettingsProvider со свойством LocalFolder и передавать его в конструктор класса TestFileService.

2. Если нужно менять путь для конкретной загрузки, то лучше переделать метод загрузки в DownloadFile(FileName, LocalFolder).

3. Если нужно менять путь для конкретной загрузки, но не хочется переделывать метод загрузки, то можно использовать фабрику службы файлов, которая будет каждый раз перед загрузкой файла создавать IFileService для указанного пути или набора настроек.


Определитесь с задачей сперва, и уже оттуда нужно плясать.
Re[18]: Конструктор с параметрами vs метод Init -- стоит ли и
От: Shmj Ниоткуда  
Дата: 08.04.16 08:57
Оценка:
Здравствуйте, koandrew, Вы писали:

K>Откровенно говоря, это решение не особенно лучше, ибо сейчас вы закладываетесь на определённое поведение фабрики (в частности, что она каждый раз будет возвращать новый экземпляр объекта). То есть вы де-факто "прибиваете" пользовательский код к конкретной реализации фабрики. Осталось сделать последний логичный шаг — вместо лукапа в контейнере по интерфейсу фабрики сделать прямой вызов фабрики.


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

K>То есть смысл в том, что если уж есть зависимость от конкретной имплементации, то лучше выразить эту зависимость явно в коде путём прямого референса.


К конкретной реализации фабрики не привязано. Добавил интерфейс абстрактной фабрики, котоарая имеет 2 реализации: тестовую и реальную. Тестовая используется для целей тестирования/отладки. Выбор какую использовать задается в конфигурации.
Re[20]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 08.04.16 09:09
Оценка:
Здравствуйте, Berill, Вы писали:

B>В самом начале этой страницы(4-ой страницы) вам описали разные варианты. Вам также предложили, и не один раз, использовать фабрики для создания вашего сервиса. В том числе и то, что вы можете в контейнере резолвить саму фабрику, если вам так уже не нравятся конструкторы с параметрами.


Все эти варианты я описал еще на второй странице: http://rsdn.ru/forum/dotnet/6402172.1
Автор: Shmj
Дата: 01.04.16


Так что предлагали на 4 -- это громко сказано...

B>С другой стороны, имхо, стоит задуматься над следующим. Если каждый раз когда вам требуется объект сервиса, вы проделываете unityContainer.Resolve, то принципиально это ничем не отличается от вызова фабричного метода.


Разница в том что в unityContainer я регистрирую множество сервисов. Вы предлагаете передавать в конструктор сервиса по 5-6 фабричных методов? Чем это лучше?

B>В свою очередь, это намекает на то, что вы, скорее всего, протаскиваете свой объект юнити-контейнера туда, где вы его используете. Лично у меня складывается такое впечатление. И тут встает закономерный вопрос, а нужен ли этот юнити вообще?!


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

B>Если посмотреть на код метода Init (который вы приводите), то опять же, он вам не гарантирует корректность переданных настроек. Если вам еще и это надо проверять, то спросите себя: а должен ли ваш объект сервиса этим заниматься?!


Init показывает какие обазательные данные необходимы для инициализации сервиса.

B>Если у вас задача: создать объект сервиса с корректными настройками — то я бы разделил это на 2 подзадачи:

B>1. Проверка корректности настроек

Про проверку корректности вопроса не было. Вопрос в том что в интерфейсе нельзя описать параметры конструктора.

B>2. Передача корректных настроек сервису либо при его создании (если эти же настройки используются не только для одного метода), либо в сам метод, где эти настройки используются


Настройки используются для всех методов. Можно, конечно, в каждый метод их передавать в качестве первого параметра, но не хотелось бы замусоливать, ведь достаточно один раз передать в конструктор или в метод Init.
Re[19]: Конструктор с параметрами vs метод Init -- стоит ли
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 08.04.16 09:35
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Проблема в том что конструктор не описывается интерфейсами


Зато интерфейсом или делегатом описывается фабрика.

S>А то что нужно единожды вызвать Init после отказа от концепции конструкторов с параметрами -- это правило можно применять для всего проекта и описать/запомнить 1 раз.


Контролировать вот только не получится.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[21]: Конструктор с параметрами vs метод Init -- стоит ли
От: Berill Азербайджан  
Дата: 08.04.16 10:46
Оценка:
Здравствуйте, Shmj, Вы писали:

B>>С другой стороны, имхо, стоит задуматься над следующим. Если каждый раз когда вам требуется объект сервиса, вы проделываете unityContainer.Resolve, то принципиально это ничем не отличается от вызова фабричного метода.


S>Разница в том что в unityContainer я регистрирую множество сервисов. Вы предлагаете передавать в конструктор сервиса по 5-6 фабричных методов? Чем это лучше?


B>>В свою очередь, это намекает на то, что вы, скорее всего, протаскиваете свой объект юнити-контейнера туда, где вы его используете. Лично у меня складывается такое впечатление. И тут встает закономерный вопрос, а нужен ли этот юнити вообще?!


S>В моем случае реальные сервисы отрабатывают достаточно долго, по этому для целей тестирования логики используются фейковые сервисы, возвращающие набор фейковых данных. Как вы предлагаете это реализовать? Передавать к конструктор одного сервиса по 5-6 фабрик на другие сервисы, которые он использует для своей работы?


Параметритизированный фабричный метод вам не подходит для этого?

B>>Если посмотреть на код метода Init (который вы приводите), то опять же, он вам не гарантирует корректность переданных настроек. Если вам еще и это надо проверять, то спросите себя: а должен ли ваш объект сервиса этим заниматься?!


S>Init показывает какие обазательные данные необходимы для инициализации сервиса.


Параметр в конструкторе делает абсолютно тоже самое. И правильнее всего это делать именно в конструкторе, т.к. это зависимость. Init можно и забыть вызвать, а вот конструктор не получится забыть. Если вы один все разрабатываете, то вы может и не забудете. А если у вас команда, то вы и не проконтролируете все и везде.

B>>Если у вас задача: создать объект сервиса с корректными настройками — то я бы разделил это на 2 подзадачи:

B>>1. Проверка корректности настроек

S>Про проверку корректности вопроса не было. Вопрос в том что в интерфейсе нельзя описать параметры конструктора.



B>>2. Передача корректных настроек сервису либо при его создании (если эти же настройки используются не только для одного метода), либо в сам метод, где эти настройки используются


S>Настройки используются для всех методов. Можно, конечно, в каждый метод их передавать в качестве первого параметра, но не хотелось бы замусоливать, ведь достаточно один раз передать в конструктор или в метод Init.


В этом случае настройки — обязательная зависимость. Это еще один аргумент в пользу того, чтобы передавать эту зависимость через конструктор. Но вы тут опять упретесь, что нельзя описать конструктор в интерфейсе (а надо ли?!). Что, по вашему мнению, приводит к сложностям использования юнити-контейнера. Хотя для нормального DI(IoC)-контейнера это не представляет никакой сложности. А для юнити это есть и подавно.
тут автор показывает как это делается в версии 2.0
Dependency Injection with Unity

Разве ваша проблема не решается? Что вам еще нужно?
Re[20]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 08.04.16 13:18
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>Проблема в том что конструктор не описывается интерфейсами

AVK>Зато интерфейсом или делегатом описывается фабрика.

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

S>>А то что нужно единожды вызвать Init после отказа от концепции конструкторов с параметрами -- это правило можно применять для всего проекта и описать/запомнить 1 раз.


AVK>Контролировать вот только не получится.


А с фабрикой вы сможете контролировать только время жизни самой фабрики.
Re[19]: Конструктор с параметрами vs метод Init -- стоит ли и
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 08.04.16 13:21
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Все зависимости должны быть в одном месте. Если я хочу вместо реального сервиса, который работает длительное время, подставить фейковый (для целей тестирования) -- то вы предлагаете по всему коду проекта зависимости искать? Сейчас все в одном файле и быстро можно переконфигурировать для тестов. Причем одновременно может существовать несколько конфигураций (одна тестовая, другая реальная).

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

S>К конкретной реализации фабрики не привязано. Добавил интерфейс абстрактной фабрики, котоарая имеет 2 реализации: тестовую и реальную. Тестовая используется для целей тестирования/отладки. Выбор какую использовать задается в конфигурации.

Ну а если какой-нить "оптимизатор" через полгода решил "оптимизировать" фабрику, и всегда отдавать один и тот же закешированный экземпляр сервиса? Упс. Я вообще стараюсь все сервисы делать stateless, то есть в твоём случае я бы обернул инициализацию и использование сервиса внутрь другого сервиса. В этом случае он станет stateless, и вероятность граблей в дальнейшем уменьшится на много порядков.
[КУ] оккупировала армия.
Re[22]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 08.04.16 13:24
Оценка:
Здравствуйте, Berill, Вы писали:

B>Параметритизированный фабричный метод вам не подходит для этого?


Это дополнительная абстрация -- по сути ничем не отличается от Init.

B>Параметр в конструкторе делает абсолютно тоже самое. И правильнее всего это делать именно в конструкторе, т.к. это зависимость. Init можно и забыть вызвать, а вот конструктор не получится забыть. Если вы один все разрабатываете, то вы может и не забудете. А если у вас команда, то вы и не проконтролируете все и везде.


Когда вы создаете объект посредством IoC-фреймворка, то передача параметров в конструктор происходит в виде нетипизированного массива ResolverOverride. Т.е. там нет абсолютно никакой информации о типе и количестве параметров. По этому можно и забыть передать, как и забыть вызвать метод Init. Во время исполнения и та и другая ошибка обнаруживаются примерно одинаково.
B>В этом случае настройки — обязательная зависимость. Это еще один аргумент в пользу того, чтобы передавать эту зависимость через конструктор. Но вы тут опять упретесь, что нельзя описать конструктор в интерфейсе (а надо ли?!). Что, по вашему мнению, приводит к сложностям использования юнити-контейнера. Хотя для нормального DI(IoC)-контейнера это не представляет никакой сложности. А для юнити это есть и подавно.
B>тут автор показывает как это делается в версии 2.0
B>Dependency Injection with Unity

B>Разве ваша проблема не решается? Что вам еще нужно?


Unity для этих случаев использует передачу параметров при Resolve в виде массива ResolverOverride. Никаких данных о типе и количестве параметров использующий видеть не будет.

Остается либо Init либо воздание дополнительной абстракции в виде фабрики. Ну или в каждый метод передавать настройки отдельно.
Re[21]: Конструктор с параметрами vs метод Init -- стоит ли
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 08.04.16 13:53
Оценка:
Здравствуйте, Shmj, Вы писали:

S>>>Проблема в том что конструктор не описывается интерфейсами

AVK>>Зато интерфейсом или делегатом описывается фабрика.
S>Верно, но это еще одна дополнительная абстракция.

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

S> Получается для каждого сервиса с параметром должна быть фабрика.


Не для каждого, а только для того, для которого нужен контракт на создание экземпляра.

S> И все равно нужно помнить что сервисы создаются посредством фабрики


Помнить не нужно. Делаешь реализацию приватной и кроме как через фабрику ее экземпляр не создашь.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[22]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 08.04.16 14:27
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Помнить не нужно. Делаешь реализацию приватной и кроме как через фабрику ее экземпляр не создашь.


Но мы же доверили создание экземпляров Unity.

У меня из проекта видны только интерфейсы (контракты). С какими конкретно реализациями работаете -- нет информации. Вызвать конструктор сервиса не возможно напрямую, т.к. реализации не видны.

По этому вы можете сделать Resolve<IMyService>, вместо Resolve<IMyServiceFactory>.CreateMyService(settings);
Re[23]: Конструктор с параметрами vs метод Init -- стоит ли
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 08.04.16 15:09
Оценка:
Здравствуйте, Shmj, Вы писали:

AVK>>Помнить не нужно. Делаешь реализацию приватной и кроме как через фабрику ее экземпляр не создашь.

S>Но мы же доверили создание экземпляров Unity.

Не мы, а вы.

S>У меня из проекта видны только интерфейсы (контракты). С какими конкретно реализациями работаете -- нет информации. Вызвать конструктор сервиса не возможно напрямую, т.к. реализации не видны.


И это логично. Хотя бы потому что у разных реализаций сервиса могут быть разные параметры конструкторов.

S>По этому вы можете сделать Resolve<IMyService>, вместо Resolve<IMyServiceFactory>.CreateMyService(settings);


Как не ресолвить фабрики в юнити тебе ссылку уже дали.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[24]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 08.04.16 15:40
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>По этому вы можете сделать Resolve<IMyService>, вместо Resolve<IMyServiceFactory>.CreateMyService(settings);

AVK>Как не ресолвить фабрики в юнити тебе ссылку уже дали.

Вы имеете в виду передавать в Resolve параметры в нетипизированном массиве ResolverOverride Или я что-то пропустил?
Re[24]: Конструктор с параметрами vs метод Init -- стоит ли
От: Shmj Ниоткуда  
Дата: 09.04.16 22:17
Оценка:
Здравствуйте, Strategy, Вы писали:

S>Пользователь службы не будет использовать ни Resolve<IMyService>, ни Resolve<IMyServiceFactory>.CreateMyService(settings) для получения экземпляра службы.


S>Вместо этого класс, которому требуется служба для выполнения своих функций, запросит в конструкторе


Уже писали -- это удобно когда сервис вызывает внутри себя 1-2 других сервиса. Если же их больше -- то замусоливать конструктор 5-7 параметрами сервисов, которые внутри себя вызывают другие сервисы -- не подходит.

Ваш пример можно показать студентам в качестве идеального примера и все будут хлопать, так как выглядит красиво. Но в жизни все несколько сложнее, по этому Resolve нужно вызывать явно внутри сервиса.
Re: Конструктор с параметрами vs метод Init -- стоит ли использо
От: bazis1 Канада  
Дата: 10.04.16 03:46
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Собственно, вся прелесть конструктора -- вы можете быть уверенным, что если создан инстанс то определенное поле 100% имеет установленное значение (если установлено в конструкторе).

Конструктор, как и многие другие языковые средства, решает одну единственную задачу. Он позволяет средствами языка описать, что делать можно, а что нельзя. Т.е. вместо неявного "надо вызвать init(), а то будет падать" есть совершенно явное "инстанс можно получить, только вызвав конструктор с параметрами". И не надо искать в документации, как называется метод init, можно ли его вызывать повторно, и какие из методов работают без него. Язык Вам просто не даст ничего сделать без вызова соответствующего конструктора.

S>Зато конструктор имеет много минусов. К примеру, Generics не поддерживают создание инстанса если конструктор с параметрами. Так же для XML-сериализации требуется наличие конструктора без параметров, а значит у вас уже не будет той гарантии, ради которой конструкторы вообще задумывались.

Откройте для себя ObsoleteAttribute с параметром error=true

S>Еще минус, вы не можете добавить новый конструктор в класс с помощью Extensions-методов. Зато можно добавить новый метод Init.

S>И наверное самая большая проблема -- конструктор нельзя описать с помощью контрактов (interface).
Откройте для себя паттерн object factory.

S>Прихожу к пониманию что конструктор с параметрами не гибок и имеет больше минусов, чем плюсов. И лучшее решение всегда создавать конструктор без параметров + метод Init, если нужны параметры для инициализации класса. Если повторная инициализация запрещена -- это легко решается с помощью булевого поля _isInitialized.

S>Согласны?
Согласен, что на основе выборки частных случаев выводить глобальное правило и безусловно ему потом следовать — не очень умная стратегия. Есть случаи, когда конструктор удобней (с тем же readonly), есть случаи, когда init() будет лучше (скажем, хочется дергать event-ы из процедуры инициализации и по каким-то причинам нельзя объединить их в интерфейс). Для простых вещей вообще можно return new Object{Field1=xxx,FieldB=yyy}; делать и не жужжать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.