Як правильно структурувати проект у формі winform?


26

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

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

Як правильно реструктурувати папку проекту?

На даний момент я думаю про щось подібне:

  • Створіть папку для форм
  • Створення папки для класів утиліти
  • Створіть папку для класів, що містять лише дані

Яка умова іменування при додаванні класів?

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

Що робити, щоб не весь код для основної форми опинився у Form1.cs

Ще одна проблема, з якою я зіткнувся, полягає в тому, що, оскільки основна форма стає все більш масовою з кожною функцією, яку я додаю, файл коду (Form1.cs) стає дуже великим. У мене є, наприклад, TabControl, і кожна вкладка має купу елементів керування, і весь код виявився у Form1.cs. Як цього уникнути?

Також, чи знаєте ви статті чи книги, які стосуються цих проблем?

Відповіді:


24

Схоже, ви потрапили в деякі загальні підводні камені, але не хвилюйтеся, їх можна виправити :)

Спочатку потрібно поглянути на свою програму трохи інакше і почати розбивати її на шматки. Ми можемо розділити шматки в двох напрямках. Спочатку ми можемо відокремити логіку управління (Правила бізнесу, код доступу до даних, код прав користувача, всі подібні речі) від коду інтерфейсу користувача. По-друге, ми можемо розбити код інтерфейсу на шматки.

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

Справжня хитрість тут полягає в тому, як ви керуєте зв’язком між елементами управління. Ви не хочете, щоб 30 користувальницьких елементів керували у формі, щоб усі випадковим чином містили посилання один на одного та методи виклику на них.

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

Отже, використовуючи наш прикладний додаток, перший інтерфейс для управління, що розміщує список списків користувачів, буде включати в себе подію під назвою UserChanged, яка передає користувацький об’єкт.

Це чудово, тому що зараз, якщо вам нудно списку списку і ви хочете 3d-масштабування магічного контролю очей, ви просто кодуєте його до того ж інтерфейсу і підключаєте до нього :)

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

Кожен елемент управління називається View (V в MVP), і ми вже висвітлювали більшість того, що потрібно вище. У цьому випадку управління та інтерфейс для нього.

Все, що ми додаємо, - це модель та презентатор.

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

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

Ведучий не повинен мати власного інтерфейсу, але я все одно люблю його створювати. Робить те, що ви хочете, щоб презентатор мав ясний текст.

Таким чином, презентатор може викрити такі методи, як ListOfAllUsers, які перегляд буде використовувати для отримання списку користувачів, або ви можете поставити AddUser метод Перегляд і викликати його у презентатора. Я віддаю перевагу останньому. Таким чином презентатор може додавати користувача до списку, коли він захоче.

Презентатор також матиме такі властивості, як CanEditUser, які повернуть істину, якщо обраний користувач зможе редагувати. Тоді View буде запитувати, що кожного разу, коли це потрібно знати. Можливо, ви хочете редагувати їх у чорному кольорі та читати лише сірі. Технічно це рішення для View, оскільки він орієнтований на користувальницький інтерфейс, незалежно від того, чи користувач може редагувати в першу чергу саме для Presenter. Ведучий знає, бо спілкується з Модель.

Отже, підсумовуючи, використовуйте MVP. Microsoft надає щось, що називається SCSF (Smart Client Software Factory), яке використовує MVP так, як я описав. Це робить і багато іншого. Це досить складно, і мені не подобається, як вони все роблять, але це може допомогти.


8

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

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

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

Що стосується самих компонентів програми, то я зазвичай прагну принаймні до трьох у невеликому додатку

  • Шар доступу до даних (підключення до бази даних, доступ до файлів тощо) - залежно від складності будь-яких постійних / збережених даних, що використовуються програмою, може бути декілька таких збірок - я, мабуть, створив би окрему збірку для обробки бази даних (можливо, навіть кілька Асамблеї, якщо взаємодія з базою даних передбачало щось складне - наприклад, якщо ви стикаєтесь із погано розробленою базою даних, вам може знадобитися обробляти відносини БД у коді, тому може бути доцільним написати декілька модулів для вставки та пошуку)

  • Логічний шар - основне "м'ясо", що містить усі рішення та алгоритми, завдяки яким ваша програма працює. Ці рішення не повинні абсолютно нічого знати про графічний інтерфейс (хто каже, що є графічний інтерфейс?), І не повинні знати абсолютно нічого про базу даних (так? Є база даних? Чому б не файл?). Добре розроблений логічний шар можна сподіватися "вирвати" та перенести в інший додаток без необхідності повторного складання. У складному додатку може бути ціла купа цих логічних зборок (тому що ви можете просто вирвати «шматки», не тягнучи за собою решту програми)

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

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

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

Ви можете думати: «Ну, я ніколи не хочу робити щось із цього, тому не має значення, чи не можу я обмінятись цими речами» - справжній момент полягає в тому, що однією з ознак модульної програми є здатність витягати «шматки» (не потрібно нічого перекомпілювати) та використовувати ці шматки в іншому місці. Для того, щоб писати такий код, це, як правило, змушує довго і важко замислюватися над принципами дизайну - Вам потрібно буде подумати над тим, щоб написати набагато більше інтерфейсів і ретельно продумати компроміси різних принципів SOLID (У тому ж так, як ви робите для розвитку, що керується поведінкою або TDD)

Іноді домогтися цього відриву від існуючого монолітного грудочки коду є мало болючим і вимагає багато ретельного рефакторингу - це нормально, ви повинні мати можливість робити це поступово - ви навіть можете досягти точки, коли занадто багато збірок, і ви вирішите повернутися в інший бік і знову почати згуртовувати речі (занадто далеко в зворотному напрямку може призвести до того, що ці збори будуть досить непотрібними самостійно)


4

Відповідно до структури папки, те, що ви запропонували, загалом у порядку. Можливо, вам потрібно буде додати папки для ресурсів (іноді люди створюють такі ресурси, щоб кожен набір ресурсів групувався під кодом мови ISO для підтримки декількох мов), зображення, сценарії бази даних, налаштування користувача (якщо не розглядаються як ресурси), шрифти , зовнішні, локальні і т.д.

Яка умова іменування при додаванні класів?

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

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

Для іменування класів існує багато джерел, наприклад, подивіться на: .net Іменування конвенцій та стандартів програмування - кращі практики та / або керівництво щодо кодування STOVF-C #

Що робити, щоб не весь код для основної форми опинився у Form1.cs

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

Зважаючи на характер події та дії MS Windows Forms, я не знаю нічого, що могло б допомогти вам забрати код з основної форми, не додаючи двозначності та зусиль. Однак MVVM може бути вибором (у майбутніх проектах), див. Наприклад: MVVM для Windows Forms .


2

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

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

Нарешті, просте упорядкування ресурсів, роз'єднання компонентів та встановлення залежностей від IoC та DI разом із підходом MVP надасть вам ключі для побудови системи, яка дозволяє уникнути поширених помилок та складності, доставляється в часі та відкрита для змін.


1

Структура проекту повністю залежить від проекту та його розміру, проте ви можете додати декілька папок, наприклад

  • Загальні (містять класи, наприклад, утиліти)
  • DataAccess (класи, пов'язані з доступом до даних за допомогою sql або будь-якого іншого сервера баз даних, який ви використовуєте)
  • Стилі (Якщо у вашому проекті є будь-які файли CSS)
  • Ресурси (наприклад, зображення, файли ресурсів)
  • WorkFlow (Класи, пов'язані з робочим процесом, якщо у вас є)

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

Конвенція про іменування виглядає так, як якщо ваш клас друкує повідомлення "Hello World", то ім'я класу повинно бути чимось пов'язаним із завданням, а відповідне ім'я класу має HelloWorld.cs.

Ви також можете створювати регіони, наприклад,

#region Hello а потім endregionу кінці.

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

Книги? ерм.

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

Сподіваюся, це допомогло!

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