Найпростіший спосіб написати логічно процедурне програмне забезпечення мовою ОО


12

Я інженер-електромонтажник, і я не знаю, що, до біса, роблю. Будь ласка, збережіть майбутніх утримувачів мого коду.

Останнім часом я працюю над низкою менших програм (в C #), функціональність яких логічно "процедурна". Наприклад, одна з них - це програма, яка збирає інформацію з різних баз даних, використовує цю інформацію для створення свого роду підсумкової сторінки, друкує її, а потім виходить.

Логіка, необхідна для всього цього, становить близько 2000 рядків. Я, звичайно, не хочу все це заповнювати в одне ціле, main()а потім «очищати» за допомогою #regions, як це робили попередні розробники (здригаються).

Ось деякі речі, які я вже спробував без особливого задоволення:

Створіть статичні утиліти для кожного грубого біта функціональності, наприклад, DatabaseInfoGetter, SummaryPageGenerator та PrintUtility. Оформлення основної функції виглядає так:

int main()
{
    var thisThing = DatabaseInfoGetter.GetThis();
    var thatThing = DatabaseInfoGetter.GetThat();
    var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

    PrintUtility.Print(summary);
}

Для однієї програми я навіть ходив з інтерфейсами

int main()
{
    /* pardon the psuedocode */

    List<Function> toDoList = new List<Function>();
    toDoList.Add(new DatabaseInfoGetter(serverUrl));
    toDoList.Add(new SummaryPageGenerator());
    toDoList.Add(new PrintUtility());

    foreach (Function f in toDoList)
        f.Do();
}

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

Який хороший спосіб структурувати такі речі?


2
Я не впевнений, що це суворо дублікат, але, будь ласка, прочитайте Single метод з багатьма параметрами проти багатьох методів, які потрібно викликати по порядку .


@Snowman, як хтось новачок у написанні великих фрагментів коду, заспокоює, що я не єдина людина, яка стикається з цими проблемами. Мені сподобалась ваша відповідь на це питання. Це допомагає.
Ломбард

@ Ломбард ідея зменшити складність до керованого рівня - це справді хороший навик розвитку. Ніхто не може зрозуміти велику базу коду, навіть Супермен (можливо, Бетмен). Пошук способів просто висловити ідеї та самодокументувати код має вирішальне значення.

"Останнім часом я працюю над низкою менших програм (в C #), функціональність яких логічно" процедурна ". Наприклад, одна з них - це програма, яка збирає інформацію з різних баз даних, використовує цю інформацію для створення свого роду резюме сторінки, роздруковує її, а потім закриває. ": Ви впевнені, що не переплутаєте" процедурне "з" імперативом "? Як процедурні, так і об'єктно-орієнтовані (можуть бути) імперативними, тобто вони можуть виражати операції, що виконуються в послідовності.
Джорджіо

Відповіді:


18

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

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

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

---- Постскрипт: Пастки дизайну ОО

Успішно працювати з програмуванням OO може бути складним. Навіть є деякі люди, які вважають всю модель хибною. Є дуже хороша книга, яку я використав, коли вперше вивчав програмування OO під назвою Thinking in Java (зараз у 4-му випуску). У цього ж автора є відповідна книга для C ++. Насправді є ще одне питання щодо програмістів, які мають загальні підводні камені в об'єктно-орієнтованому програмуванні .

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

Подібні помилки в дизайні можна знайти в Java Swing API. Вони використовують модель публікації-підписки для системи меню Swing. Це робить створення та налагодження меню в Swing повним кошмаром. Іронія полягає в тому, що вона абсолютно безглузда. Практично ніколи не буває ситуації, коли кілька функцій потребували б "підписатися" на натискання меню. Крім того, публікація-підписка була повною помилкою, оскільки система меню зазвичай завжди використовується. Це не так, як функції підписуються, а потім скасовуються підписки випадковим чином. Той факт, що "професійні" розробники в Sun зробили подібну помилку, просто показує, як легко навіть професіоналам зробити монументальні гвинти в OO-дизайні.

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


1
+1 для "чіткість набагато важливіша [ніж ефективність]"
Стівен

Не могли б ви вказати на посилання, яке описує, що є "погано написаною програмою ОО", або додати в редагуванні додаткову інформацію про це?
Ломбард

@ Ломбард Я це зробив.
Тайлер Дерден,

1

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

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

Якщо ви все ще боретеся зі складністю, вам, швидше за все, потрібно ще більше розбити свою програму. Створіть більше рівнів в ієрархії - можливо, DatabaseInfoGetterпотрібно посилатися на якісь інші класи на кшталт ProductTableEntryчи щось таке. Ви пишете процедурний код, але пам’ятайте, що ви використовуєте C #, і OOP надає купу інструментів для зменшення складності, наприклад:

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

Також будьте обережні зі статичними класами. Класи баз даних є хорошими кандидатами, якщо у вас зазвичай є одна база даних .. ви розумієте.

Зрештою, якщо ви думаєте, що математичні функції і C # не відповідають вашому стилю, спробуйте щось на зразок Scala, Haskell тощо. Вони теж круті.


0

Я відповідаю на 4 роки із запізненням, але коли ти намагаєшся робити процедури процедурною мовою ОО, то ти працюєш проти антипаттера. Це не те, що ти повинен робити! Я знайшов блог, який звертається до цієї антипатерні та показує її рішення, але для цього потрібно багато рефакторингу та зміни настрою. Див процедурного коду в об'єктно-орієнтованому коді для більш докладної інформації.
Коли ви згадали "це" і "те", ви в основному пропонуєте вам два різних класи. Це клас (неправильне ім’я!) Та клас That. І ви можете, наприклад, перекласти це:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

в це:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

Тепер було б цікаво переглянути ці 2 000+ рядків процесуального кодексу та визначити, як різні частини можна було б об'єднати в окремі класи та перемістити різні процедури вниз до цих класів.
Кожен клас повинен бути простим і зосередженим на тому, щоб робити лише одне. І мені здається, що в деяких частинах коду вже є суперкласи, які насправді є занадто великими, щоб бути корисними. Наприклад, DatabaseInfoGetter, звучить спосіб заплутати. Що це все-таки отримує? Це звучить занадто загально. Але SummaryPageGenerator надзвичайно специфічний! І PrintUtility знову стає загальним.
Отже, для початку вам слід уникати загальних імен для своїх класів і починати використовувати більш конкретні імена. Наприклад, клас PrintUtility - це більше клас друку з класом Header, Footer, класом TextBlock, класом Image і, можливо, певним чином вказати, як вони будуть перетікати в самому друку. Все це може бути в Простір імен PrintUtility , однак.
Для бази даних подібна історія. Навіть якщо ви використовуєте Entity Framework для доступу до бази даних, ви повинні обмежуватись ними, чим ви користуєтесь, і розділити їх на логічні групи.
Але мені цікаво, чи ви все ще стикаєтеся з цією проблемою через 4 роки. Якщо так, то це мислення, яке потрібно змінити від «процедурної концепції» та до «об'єктно-орієнтованої концепції». Що складно, якщо у вас немає досвіду ...


-3

На мою думку, ви повинні змінити свій код, щоб він був простим, послідовним і читабельним.

Я написав кілька дописів на цю тему на цю тему, і повірте, їх зрозуміти просто: що таке хороший код

Просто: Так само, як друкована плата містить різні фрагменти, кожен з яких несе відповідальність. Код слід розділити на менші, простіші частини.

Послідовно: використовуйте малюнки, стандарт для іменування елементів та речей. Вам подобаються інструменти, які весь час працюють однаково, і ви хочете знати, де їх знайти. Те саме з кодом.

Читання: Не використовуйте абревіатури, якщо вони широко використовуються та не відомі в домені, на яке націлено програмне забезпечення (mattnz), віддайте перевагу вимовляючим імен, зрозумілим і зрозумілим.


1
Скорочення є прийнятними, якщо вони широко використовуються та відомі в області, на яку спрямовано програмне забезпечення. Спробуйте написати програмне забезпечення для управління повітряним рухом без використання - у нас є абревіатури, складені з абревіатур - ніхто б не знав, про що ви говорили, чи використовували ви повне ім’я. Такі речі, як HTTP для роботи в мережі, ABS в автомобільній промисловості тощо, цілком прийнятні.
mattnz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.