Ін'єкція залежності від статичних методів


21

Я провів сьогодні цікаву дискусію з іншим розробником про те, як наблизитись до класу методом, який приймає рядок та виводить рядок.

Уявіть щось на зразок наступного, яке повністю складено з метою прикладу

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

Функція, яка має певну логіку, засновану на введенні рядка, додається до проекту, використовуючи DI, і на ньому є контейнер DI. Чи додали б ви цьому новому класу інтерфейс і ввели його там, де потрібно, чи зробили би його статичним класом? Які плюси і мінуси у кожного? Чому ви хочете (або ні) хочете зробити це чимось, що використовується для конструкторської інжекції, а не просто звертатися, коли потрібно де-небудь.


1
@Ewan Для допоміжних методів, які не використовуються для абстрагування. Приклад: Apache FileUtils.
Вальфрат

3
@Ewan: статичні методи без побічних ефектів є найкращим методом, тому що вони прості для розуміння та прості для тестування.
ЖакБ

1
але вони унеможливлюють тестування тестових речей, які залежать від них
Еван

3
@Ewan Eh, не дуже. Чи поганий Math.Max ​​(), оскільки він статичний? Якщо ви перевірите свій статичний метод, і він працює, ви можете без проблем використовувати його на інших методах. Якщо статичний збій не вдасться, його тест застане його.
Т. Сар - Відновіть Моніку

1
якщо "статичний" гарантував відсутність побічних ефектів, я можу побачити аргумент. але це не.
Еван

Відповіді:


25

Немає причин, чому це потрібно вводити ін’єкційно. Це просто функція, вона не має залежностей, тому просто називайте її. Це навіть може бути статичним, якщо ви хочете, щоб виглядати чистим. Проти цього можна писати одиничні тести без особливих труднощів. Якщо він використовується в інших класах, одиничні тести все ще можуть бути написані.

Не потрібно абстрагувати відібрані функції без залежностей, це надмірно.

Якщо це стає складніше, можливо, передача інтерфейсу в конструктор або метод є обґрунтованим. Але я не пішов би цією дорогою, якби не склалася складна GetStringPartлогіка на основі розташування тощо.


2
Це правильна відповідь! На погану вантажну культову відповідь було прийнято.
ЖакБ

3
що функція не має залежностей, хіба справа. Проблема полягає в тому, коли інші речі залежать від функції і тісно зв'язані з нею
Еван

2
Що робити, якщо функція зміниться через 6 місяців і не є такою чистою, але вона вже використовується в багатьох місцях. Це не розширюється як статичний, і це також не те, що можна виділити із споживання тестів класів. Якщо є навіть невеликий шанс на зміну, я б пішов на його введення. Це означає, що я відкритий для прослуховування протилежних аргументів, в цьому і полягає суть цієї публікації. Які мінуси у цьому введенні та позитиві статики?
Джеймс

1
Взагалі кажучи, люди, які обговорюють ін'єкцію залежності на цьому веб-сайті, не потребують введення наркоманії. Звідси упередженість до зайвої складності.
Френк Хілеман

2
@James Якщо функція стає менш чистою, ви робите щось не так, і функція, ймовірно, була недостатньо визначена з самого початку. Обчислення площі квадрата не повинно раптом потребувати виклику бази даних або розгортання візуального оновлення для інтерфейсу користувача. Чи можете ви уявити сценарій, коли метод "Підрядка" сортування раптом стає нечистим?
Т. Сар - Відновіть Моніку

12

Ось чому

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

 

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

Якби ви пройшли статичний метод, не було б способу змінити поведінку, GetStringPartне руйнуючи стару поведінку або забруднюючи її умовною логікою. Це правда, що статистика є злими глобальними масками, але те, що вони відключають поліморфізм, - це моя головна скарга на них. Статичні методи - це не перший клас у мовах OOP. Надаючи методу об'єкт для проживання навіть тому, що не має стану, ми робимо метод переносним. Його поведінку можна передавати приблизно як значення змінної.

Тут я уявив систему, яка повинна поводитися дещо інакше, коли розгортатися в Європі, а тоді, коли розгортається в США. Замість того, щоб змусити будь-яку систему містити код, необхідний лише іншим, ми можемо змінити поведінку, контролюючи, який об’єкт розбору порядку вводиться клієнтам. Це дозволяє стримувати деталізацію регіону. Це також дозволяє легко додавати OrderParserCanada, не торкаючись існуючих парсерів.

Якщо це для вас нічого не означає, то насправді для цього немає гарного аргументу.

До речі, GetStringPartжахлива назва.


* Cringe у дужках стилю коду * Я здогадуюсь, що ви програміст Java, який намагається написати код C #? Береться, щоб знати одну, яку я думаю. :)
Ніл

Питання не стосується мови, докладніше про дизайн. Моя проблема зі статичними методами полягає в тому, що вони запобігають ізоляції коду для тестування. Розробник, з яким я говорив, думає, що я занадто сильно покладаюся на DI, а іноді методи розширення / статичні методи є актуальними. Я думаю, що можливо в рідкісних випадках, але не як загальна мета. Постійне обговорення, яке я вважав, було гарним обговорювати далі. Я також згоден з вашою справою щодо поліморфізму.
Джеймс

Тестування є причиною, але є і більше. Мені не подобається багато в чому звинувачувати це в тестуванні, оскільки це просто починає критикувати тестування людей. Простий факт - процедурним програмістам це не подобається, якщо ви не програмуєте процедурно.
candied_orange

Чи не могли ви просто сказати static getStringPartEU()? Ваш приклад має сенс лише в тому випадку, якщо в цьому класі є інші методи, які також потребують спеціалізованого лікування в ЄС, і їх потрібно розглядати як єдину одиницю.
Роберт Харві

2
Ви напевно сперечаєтесь на користь зайвої складності. Все може додати до нього все більше коду. Речі, які потрібно змінити, повинні легко змінювати, але не слід намагатися передбачити, що має змінитись у майбутньому. Статичні поля можуть бути ідентичними глобальним змінним, але статичні методи можуть бути чистими, найкращим можливим типом методу.
Френк Хілеман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.