Як уникнути зайвої перевантаження методом?


16

У вихідному коді нашої програми є досить багато місць, де в одному класі є багато методів з однаковими іменами та різними параметрами. У цих методах завжди є всі параметри попереднього методу плюс ще один.

Це результат тривалої еволюції (застарілий код) і такого мислення (я вважаю):

" Існує метод M, який робить щось А. Мені потрібно зробити A + B. Добре, я знаю ... Я додам новий параметр до M, створіть новий метод для цього, пересуньте код з M на новий метод з ще одним параметром, зробіть A + B там і зателефонуйте новому методу від M зі значенням нового параметра за замовчуванням. "

Ось приклад (на мові, подібній до Java):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

Я відчуваю, що це неправильно. Мало того, що ми не можемо назавжди додавати такі параметри, як цей, але код важко розширити / змінити через всі залежності між методами.

Ось кілька способів зробити це краще:

  1. Введіть об'єкт параметра:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. Встановіть параметри DocumentHomeоб'єкта перед тим, як викликатиcreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. Розділіть роботу на різні методи та зателефонуйте їм за потребою:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

Мої запитання:

  1. Чи справді описана проблема є проблемою?
  2. Що ви думаєте про запропоновані рішення? Якому б ви віддали перевагу (виходячи зі свого досвіду)?
  3. Чи можете ви придумати якесь інше рішення?

1
На яку мову ви орієнтуєтесь чи це просто що-небудь генеральне?
Кнерд

Немає конкретної мови, просто загальна. Не соромтеся показати, як функції інших мов можуть допомогти у цьому.
Ітусь

як я вже сказав тут, програмісти.stackexchange.com/ questions/ 235096/… C # і C ++ мають деякі особливості.
Кнерд

Цілком очевидно, що це питання стосується кожної мови, яка підтримує такий вид перевантаження.
Док Браун

1
@DocBrown нормально, але не кожна мова підтримує однакові альтернативи;)
Knerd

Відповіді:


20

Може, спробувати шаблон для побудови ? (зауважте: досить випадковий результат Google :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

Я не можу дати повний пропуск, чому я віддаю перевагу програмісту над варіантами, які ви надаєте, але ви визначили велику проблему з великою кількістю коду. Якщо ви вважаєте, що вам потрібні більше двох параметрів для методу, ви, мабуть, неправильно структуровані вашим кодом (а дехто міркує!).

Проблема з об'єктом params полягає в тому, що (якщо об'єкт, який ви створюєте, якимось чином не є реальним), ви просто підштовхуєте проблему до рівня, і ви отримуєте кластер непов'язаних параметрів, що утворюють "об'єкт".

Ваші інші спроби дивляться на мене як на те, що хтось тягнеться до моделі будівельника, але не зовсім потрапляє туди :)


Дякую. Ваша відповідь може бути рішенням номер. 4, так. Я шукаю більше відповідей таким чином: "З мого досвіду це призводить до ... і може бути виправлено ...". або "Коли я бачу це в коді колеги, пропоную йому ... замість цього".
Ітусь

Я не знаю ... мені здається, що ти просто рухаєш проблему. Наприклад, якщо я можу створити документ на різних частинах програми, краще організувати та протестувати цю ізоляцію цієї конструкції документа на відокремленому класі (наприклад, a DocumentoFactory). Маючи в builderрізних місцях, важко контролювати майбутні зміни в конструкції документа (наприклад, додавання нового обов'язкового поля для документування, наприклад) та додавання додаткового кодового коду на тести, щоб просто задовольнити потреби в розробнику документів для класів, які використовують builder.
Дерік

1

Використання об'єкта параметра - хороший спосіб уникнути (надмірного) перевантаження методів:

  • він очищає код
  • відокремлює дані від функціоналу
  • робить код більш ретельним

Я б, однак, не надто переймався цим.

Перевантажити тут і там не погано. Він підтримується мовою програмування, тому використовуйте його на вашу користь.

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

Всього мої 2 копійки.


0

Я, чесно кажучи, не бачу великої проблеми з кодом. У C # і C ++ ви можете використовувати необов'язкові параметри, що було б альтернативою, але, наскільки я знаю, Java не підтримує такі параметри.

У C # ви можете зробити всі перевантаження приватними, і один метод із необов'язковими параметрами - це загальнодоступний виклик матеріалу.

Щоб відповісти на ваше питання, частина 2, я б взяв об'єкт параметра або навіть словник / HashMap.

Так:

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

Як застереження я спочатку програміст C # і JavaScript, а потім програміст Java.


4
Це рішення, але я не вважаю його гарним рішенням (принаймні, не в контексті, коли очікується безпека типу).
Док Браун

Це вірно. Через подібні випадки мені подобаються перевантажені методи або необов'язкові параметри.
Кнерд

2
Використання словника як параметра - це простий спосіб зменшити кількість видимих ​​параметрів до методу, але він затьмарює фактичні залежності методу. Це змушує абонента шукати в іншому місці, що саме має бути у словнику, чи це у коментарях, інших викликах до методу, чи самій реалізації методу.
Майк Партрідж

Використовуйте словник лише для дійсно необов'язкових параметрів, тому основні випадки використання не повинні читати занадто багато документації.
user949300

0

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

У вашому випадку у мене буде конструктор із такими методами:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

Потім ви можете використовувати будівельник таким чином:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

Зі сторони, я не заперечую проти декількох простих перевантажень - до біса, .NET Framework їх використовує в усьому світі разом із HTML-помічниками. Однак я б переоцінив те, що роблю, якщо мені доведеться передати більше двох параметрів кожному методу.


0

Я думаю, що builderрішення може працювати на більшій частині сценаріїв, але в більш складних випадках ваш будівельник також буде складним у налаштуванні , оскільки легко вчинити деякі помилки під час порядку методів, які методи потрібно виставляти чи ні і т. д. Отже, багато хто з нас віддасть перевагу найпростішому рішенню.

Якщо ви просто створили простий конструктор для складання документа і розповсюдження цього коду по різних частинах (класах) програми, буде важко:

  • організуйте : у вас буде багато класів, що складають документ по-різному
  • підтримайте : будь-яка зміна документації (наприклад, додавання нової обов'язкової поданої заявки) призведе вас до хірургічної стрільби
  • тест : якщо ви тестуєте клас, що створює документ, вам потрібно буде додати код кодової таблиці просто для задоволення ідентифікації документа.

Але це не відповідає на питання ОП.

Альтернатива перевантаженню

Деякі варіанти:

  • Перейменуйте ім'я методу : якщо те ж ім'я методу створює деяку плутанину, спробуйте перейменувати методи , щоб створити значуще ім'я краще , ніж createDocument, як: createLicenseDriveDocument, createDocumentWithOptionalFieldsі т.д. Звичайно, це може привести вас до гігантських іменах методів, так що це не рішення для всіх випадків.
  • Використовуйте статичні методи . Цей підхід є на зразок подібного, якщо порівнювати з першою альтернативою вище. Ви можете використовувати осмислені імена для кожного випадку і екземпляр документа від статичного методу на Document, як: Document.createLicenseDriveDocument().
  • Створіть загальний інтерфейс : ви можете створити єдиний метод, який називається, createDocument(InterfaceDocument interfaceDocument)та створити різні реалізації для InterfaceDocument. За приклад: createDocument(new DocumentMinPagesCount("name")). Звичайно, вам не потрібна одна реалізація для кожного випадку, оскільки ви можете створити більше одного конструктора для кожної реалізації, згрупувавши деякі поля, що мають сенс для цієї реалізації. Ця закономірність називається телескопічними конструкторами .

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

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