Дизайн класу - кілька дзвінків з одного методу або одного дзвінка з кількох методів?

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

  1. Отримати дані з CMS (у нас є DAL, який я використовую, тому це, по суті, просто вказування того, які дані з CMS я хочу - немає логіки з'єднання або щось подібне)
  2. Мап розібраних даних на мої власні [C #] об'єкти

Існує, по суті, два способи, до яких я можу підійти до цього:

Один дзвінок з кількох методів

public void MainMethodWhereIDoStuff()
{
    IEnumerable myObjects = GetMyObjects();
   //Do other stuff with myObjects
}

private static IEnumerable GetMyObjects()
{
    IEnumerable cmsDataItems = GetCmsDataItems();
    List mappedObjects = new List();
   //do stuff to map the CmsDataItems to MyObjects
    return mappedObjects;
}

private static IEnumerable GetCmsDataItems()
{
    List cmsDataItems = new List();
   //do stuff to get the CmsDataItems I want
    return cmsDataItems;
}

Кілька викликів із одного методу

public void MainMethodWhereIDoStuff()
{
    IEnumerable cmsDataItems = GetCmsDataItems();
    IEnumerable myObjects = GetMyObjects(cmsDataItems);
   //do stuff with myObjects
}

private static IEnumerable GetMyObjects(IEnumerable itemsToMap)
{
   //...
}

private static IEnumerable GetCmsDataItems()
{
   //...
}

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

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

Чи є один із цих методів правильним, а інший - неправильним? Або це просто питання переваги чи контексту залежать?

2
Не відповідь, а просто порада. Якщо ви збираєтеся створити клас, який поширює одну з загальних класів колекції, додайте інтерфейс IEnumerable у свій клас і займіть деякий час для реалізації необхідних методів. Таким чином, ви зможете переглядати свою власну колекцію стандартною/передбачуваною формою (наприклад, використовуючи foreach).
додано Автор Cristián Romo, джерело

6 Відповіді

Ключ "ніколи не буде використовуватися за межами класу"

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

Громадські учасники/інтерфейси OTOH, робити важливі. Коли ви відкриваєте учасника публічно/внутрішньо, ви повинні мати чіткі причини для цього, тому що хтось може/буде залежати від нього в майбутньому. Це розділювальна лінія між застосуванням та розробкою API. У керуванні API контролю доступу стає дуже важливим, оскільки будь-який елемент/інтерфейс, від якого люди залежать, є тим, який неможливо змінити/видалити, не порушуючи хтось код.

По суті:

Ваша приватна частина - це ніхто не бізнес, а ваш власний

3
додано
@BenBrocka Я чув, що це звучить ріжком.
додано Автор Cristián Romo, джерело
@Lucas Використовувані імена є чисто суб'єктивними. Справа в тому, що допоміжні методи ніколи не будуть відкриті публічно - як вони ніколи не будуть використовуватися за межами визначення класу - так що схема іменування повинна мати сенс тільки для розробника (ів), що реалізують клас. Просте правило полягає в тому, що занадто багато нав'язування імен/структур, коли справа доходить до коду приватного/допоміжного коду.
додано Автор Cristián Romo, джерело
@BenBrocka Я знаю, що це старе, як пекло, але цей коментар заслуговує певної медалі.
додано Автор Airhead, джерело
Коли GetCmsDataItems() є і буде методом, який використовується лише GetMyObjects (..) , то я думаю, що його слід називати GetMyObjects .
додано Автор johndodo, джерело
Технічно в C ++ друзі можуть все-таки торкнутися ваших прихильників (але, хоча, ймовірно, не слід)
додано Автор Ben Brocka, джерело
Популярна думка вважає це досить неприйнятним
додано Автор Ben Brocka, джерело

Колишній приклад, який ви надаєте більш ретельно, ізолює розділ "основного" коду (який, на мою думку, бачить найбільший трафік розробника), викликає сторонні проблеми, тому я вважаю, що це найкращий підхід. Однак, як і ви, мені не подобається мати багато допоміжних методів, які засмічують мої основні класи. Щоб зробити це трохи більш чистим, розгляньте розділення допоміжних методів на допоміжний клас. Ви можете зробити це за допомогою окремого класу internal або, якщо ви хочете, щоб клас був приватним для інкапсуляції, створіть файл з частковим класом і надайте своєму помічнику приватний вкладене клас. Подібно до цього:

// helper class definition file
public partial class MyClass
{
   //private class modifier only works on nested classes like this one
   //only the parent class MyClass will be able to see this class
    private static class MyHelper
    {
        public static IEnumerable GetMyObjects() { ... }
        private static IEnumerable GetCmsDataItems() { ... }
    }
}

// normal, high traffic file
public partial class MyClass
{
    public void MainMethodWhereIDoStuff()
    {
        IEnumerable myObjects = MyHelper.GetMyObjects();
    }

   //removed side-concerns of getting/transforming data into MyObjects
}

Щось подібне не є строго необхідним, але це може допомогти з'ясувати проблеми, якщо це важливо для вас.

Як прикмете, для ваших перетворень даних (наприклад, з CmsDataItem до MyObject), ви можете розглянути можливість використання бібліотеки відображення, як-от AutoMapper (доступний через NuGet в Visual Studio). Незважаючи на те, що вам все одно доведеться підтримувати трансформації відображення, це зробить ваш код семантичним яскравішим, що ви просто відстежуєте властивості від одного об'єкта до іншого. Плюс AutoMapper зробить багато простих відображеннях автоматично.

2
додано

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

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

Важливо, це розглянути питання про те, чи застосовується вами методика відповідає задаче, і чи буде це впливати на інтерфейс класу, до якого належить цей метод. Хоча найближча мета полягає в задоволенні вимоги, інші цілі можуть також включати розширення та низькі витрати на обслуговування. Досвід роботи з вашими конкретними програмами навчить вас, коли виступати за одну техніку за іншою.

1
додано

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

Наприклад, у першому випадку getMyObjects та getCMSObjects зчеплені таким чином, що єдиними об'єктами, які getMyObjects зможе картувати, це об'єкти, що були знову повернені з getCmsObjects, тоді як на другому прикладі метод працює на будь-який аргумент, який він отримує.

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

1
додано
Дякую за вхід .. Я теж подумав про це з точки зору зв'язку, але потім я думав, що я можу трохи покінчити з керуванням, говорять про відокремлення приватних методів у класі.
додано Автор kcrisman, джерело

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

0
додано
  • Буде практично неможливо дати вам "правильну відповідь", поки ви не перейменуєте свої методи та не надасте їм кращого семантичного значення, а просто "DoStuff".

  • Тільки тоді ми зможемо визначити відповідальність та розділити їх відповідно.

0
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug