Làm thế nào để cấu trúc đúng một dự án trong winform?


26

Cách đây một thời gian, tôi bắt đầu tạo ra một ứng dụng winform và tại thời điểm đó nó còn nhỏ và tôi không nghĩ gì về cách cấu trúc dự án.

Kể từ đó tôi đã thêm các tính năng bổ sung khi tôi cần và thư mục dự án ngày càng lớn hơn và bây giờ tôi nghĩ đã đến lúc cấu trúc dự án theo một cách nào đó, nhưng tôi không chắc đâu là cách thích hợp, vì vậy tôi có vài câu hỏi.

Làm thế nào để cơ cấu lại đúng thư mục dự án?

Hiện tại tôi đang nghĩ về một cái gì đó như thế này:

  • Tạo thư mục cho biểu mẫu
  • Tạo thư mục cho các lớp Utility
  • Tạo thư mục cho các lớp chỉ chứa dữ liệu

Quy ước đặt tên khi thêm các lớp là gì?

Tôi cũng nên đổi tên các lớp để chức năng của chúng có thể được xác định bằng cách chỉ nhìn vào tên của chúng? Ví dụ đổi tên tất cả các lớp biểu mẫu, để tên của chúng kết thúc bằng Biểu mẫu . Hoặc điều này là không cần thiết nếu các thư mục đặc biệt cho chúng được tạo ra?

Phải làm gì để không phải tất cả mã cho biểu mẫu chính kết thúc trong Form1.cs

Một vấn đề khác mà tôi gặp phải là vì biểu mẫu chính ngày càng lớn hơn với mỗi tính năng tôi thêm vào, tệp mã (Form1.cs) đang trở nên rất lớn. Tôi có ví dụ về một TabControl và mỗi tab có một loạt các điều khiển và tất cả các mã kết thúc trong Form1.cs. Làm thế nào để tránh điều này?

Ngoài ra, bạn có biết bất kỳ bài viết hoặc cuốn sách nào giải quyết những vấn đề này không?

Câu trả lời:


24

Có vẻ như bạn đã rơi vào một số cạm bẫy phổ biến, nhưng đừng lo lắng, chúng có thể được sửa chữa :)

Trước tiên, bạn cần xem xét ứng dụng của mình một chút khác biệt và bắt đầu chia nó thành nhiều phần. Chúng ta có thể chia các phần theo hai hướng. Trước tiên, chúng ta có thể tách logic điều khiển (Quy tắc kinh doanh, mã truy cập dữ liệu, mã quyền người dùng, tất cả các loại nội dung đó) khỏi mã UI. Thứ hai, chúng ta có thể chia mã UI thành nhiều phần.

Vì vậy, chúng ta sẽ thực hiện phần sau trước, chia nhỏ UI thành nhiều phần. Cách dễ nhất để làm điều này là có một hình thức lưu trữ duy nhất mà bạn soạn UI của mình với usercontrols. Mỗi kiểm soát người dùng sẽ phụ trách một khu vực của biểu mẫu. Vì vậy, hãy tưởng tượng ứng dụng của bạn có một danh sách người dùng và khi bạn nhấp vào người dùng, một hộp văn bản bên dưới nó chứa đầy các chi tiết của họ. Bạn có thể có một điều khiển người dùng quản lý hiển thị danh sách người dùng và điều khiển thứ hai quản lý hiển thị chi tiết của người dùng.

Bí quyết thực sự ở đây là cách bạn quản lý giao tiếp giữa các điều khiển. Bạn không muốn 30 điều khiển người dùng trên biểu mẫu tất cả các tham chiếu giữ ngẫu nhiên cho nhau và gọi các phương thức trên chúng.

Vì vậy, bạn tạo một giao diện cho mỗi điều khiển. Giao diện chứa các hoạt động mà điều khiển sẽ chấp nhận và bất kỳ sự kiện nào nó phát sinh. Khi bạn nghĩ về ứng dụng này, bạn không quan tâm nếu lựa chọn danh sách hộp danh sách thay đổi, bạn quan tâm đến thực tế một người dùng mới đã thay đổi.

Vì vậy, bằng cách sử dụng ứng dụng ví dụ của chúng tôi, giao diện đầu tiên cho điều khiển lưu trữ hộp danh sách người dùng sẽ bao gồm một sự kiện có tên UserChanged để chuyển đối tượng người dùng ra ngoài.

Điều này thật tuyệt vời bởi vì bây giờ nếu bạn cảm thấy nhàm chán với hộp danh sách và muốn điều khiển mắt ma thuật zoomy 3d, bạn chỉ cần mã nó vào cùng một giao diện và cắm nó vào :)

Ok, vậy là phần hai, tách logic UI khỏi logic miền. Chà, đây là một con đường mòn và tôi khuyên bạn nên xem mẫu MVP ở đây. Nó thực sự đơn giản.

Mỗi điều khiển hiện được gọi là Chế độ xem (V trong MVP) và chúng tôi đã đề cập đến hầu hết những gì cần thiết ở trên. Trong trường hợp này, điều khiển và giao diện cho nó.

Tất cả chúng tôi thêm là mô hình và người trình bày.

Mô hình chứa logic quản lý trạng thái ứng dụng của bạn. Bạn biết nội dung, nó sẽ vào cơ sở dữ liệu để lấy người dùng, ghi vào cơ sở dữ liệu khi bạn thêm người dùng, v.v. Ý tưởng là bạn có thể kiểm tra tất cả những điều này trong sự cô lập hoàn toàn với mọi thứ khác.

Người trình bày là một chút khó khăn hơn để giải thích. Đây là một lớp nằm giữa mô hình và View. Nó được tạo bởi khung nhìn và khung nhìn tự chuyển vào người trình bày bằng giao diện mà chúng ta đã thảo luận trước đó.

Người thuyết trình không cần phải có giao diện riêng, nhưng dù sao tôi cũng muốn tạo một giao diện. Làm cho những gì bạn muốn người trình bày làm rõ ràng.

Vì vậy, người trình bày sẽ hiển thị các phương thức như ListOfAllUsers mà Chế độ xem sẽ sử dụng để lấy danh sách người dùng của mình, thay vào đó, bạn có thể đặt phương thức AddUser là Xem và gọi phương thức đó từ người trình bày. Tôi thích cái sau. Bằng cách đó, người trình bày có thể thêm người dùng vào hộp danh sách khi nào họ muốn.

Người thuyết trình cũng sẽ có các thuộc tính như CanEditUser, sẽ trả về đúng nếu người dùng được chọn có thể được chỉnh sửa. Chế độ xem sau đó sẽ truy vấn mà mỗi khi cần biết. Bạn có thể muốn những cái có thể chỉnh sửa màu đen và chỉ đọc những màu xám. Về mặt kỹ thuật, đó là một quyết định cho Chế độ xem vì nó tập trung vào giao diện người dùng, cho dù người dùng có thể chỉnh sửa được ngay từ đầu hay không là dành cho Người thuyết trình. Người trình bày biết vì nó nói chuyện với Người mẫu.

Vì vậy, tóm lại, sử dụng MVP. Microsoft cung cấp một cái gì đó gọi là SCSF (Nhà máy phần mềm khách hàng thông minh) sử dụng MVP theo cách tôi đã mô tả. Nó cũng làm rất nhiều thứ khác nữa. Nó khá phức tạp và tôi không thích cách họ làm mọi thứ, nhưng nó có thể giúp ích.


8

Cá nhân tôi thích tách các khu vực quan tâm khác nhau giữa một số hội đồng thay vì kết hợp mọi thứ lại với nhau thành một tập tin thực thi.

Thông thường, tôi thích giữ một số lượng mã tối thiểu tuyệt đối trong điểm vào của ứng dụng - Không logic nghiệp vụ, không có mã GUI và không truy cập dữ liệu (cơ sở dữ liệu / truy cập tệp / kết nối mạng / vv); Tôi thường giới hạn mã điểm nhập cảnh (tức là mã thực thi) ở một số thứ dọc theo dòng

  • Tạo và khởi tạo các thành phần ứng dụng khác nhau từ tất cả các hội đồng phụ thuộc
  • Định cấu hình bất kỳ thành phần bên thứ 3 nào mà toàn bộ ứng dụng phụ thuộc vào (ví dụ Log4Net cho đầu ra chẩn đoán)
  • Ngoài ra, tôi có thể bao gồm một loại mã "bắt tất cả các ngoại lệ và ghi lại mã loại theo dõi ngăn xếp" trong hàm chính, điều này sẽ giúp ghi lại các trường hợp của bất kỳ thất bại nghiêm trọng / nghiêm trọng nào.

Đối với bản thân các thành phần ứng dụng, tôi thường nhắm đến ít nhất ba trong một ứng dụng nhỏ

  • Lớp truy cập dữ liệu (kết nối cơ sở dữ liệu, truy cập tệp, v.v.) - tùy thuộc vào mức độ phức tạp của bất kỳ dữ liệu lưu trữ / lưu trữ nào được sử dụng bởi ứng dụng, có thể có một số hội đồng này - tôi có thể sẽ tạo một hội đồng riêng để xử lý cơ sở dữ liệu (Có thể thậm chí nhiều lần các hội đồng nếu tương tác với cơ sở dữ liệu liên quan đến bất kỳ điều gì phức tạp - ví dụ: nếu bạn không ổn định với cơ sở dữ liệu được thiết kế kém, bạn có thể cần xử lý các mối quan hệ DB trong mã, do đó, có thể có ý nghĩa khi viết nhiều mô-đun để chèn và truy xuất)

  • Lớp logic - "thịt" chính chứa tất cả các quyết định và thuật toán làm cho ứng dụng của bạn hoạt động. Các quyết định này hoàn toàn không biết gì về GUI (ai nói có GUI?) Và hoàn toàn không biết gì về cơ sở dữ liệu (Huh? Có cơ sở dữ liệu không? Tại sao không phải là tệp?). Một lớp logic được thiết kế tốt có thể hy vọng được "tách ra" và rơi vào một ứng dụng khác mà không cần phải biên dịch lại. Trong một ứng dụng phức tạp, có thể có cả đống cụm logic này (vì bạn có thể chỉ muốn tách ra 'các mảnh' mà không kéo theo phần còn lại của ứng dụng)

  • Lớp trình bày (tức là GUI); Trong một ứng dụng nhỏ, có thể chỉ có một "hình thức chính" duy nhất với một vài hộp thoại có thể đi vào một cụm duy nhất - trong một ứng dụng lớn hơn, có thể có các cụm riêng biệt cho toàn bộ các bộ phận chức năng của GUI. Các lớp ở đây sẽ làm ít hơn là làm cho tương tác người dùng hoạt động - nó sẽ ít hơn một lớp vỏ với một số xác thực đầu vào cơ bản, xử lý bất kỳ hoạt hình nào, v.v. Bất kỳ sự kiện / nhấp chuột nào mà "làm gì đó" sẽ được chuyển tiếp tới lớp logic (vì vậy lớp trình bày của tôi sẽ hoàn toàn không chứa logic ứng dụng nào, nhưng nó cũng sẽ không đặt gánh nặng của bất kỳ mã GUI nào lên lớp logic - vì vậy, bất kỳ thanh tiến trình hoặc nội dung ưa thích nào khác cũng sẽ nằm trong cụm trình bày / ies)

Lý do chính của tôi để tách các lớp Trình bày, Logic và Dữ liệu thành các cụm riêng biệt là: Tôi cảm thấy rằng có thể chạy logic ứng dụng chính mà không cần cơ sở dữ liệu hoặc GUI của bạn.

Theo một cách khác; nếu tôi muốn viết một tệp thực thi khác hoạt động giống hệt như ứng dụng của bạn, nhưng sử dụng giao diện dòng lệnh hoặc giao diện web; và hoán đổi bộ lưu trữ cơ sở dữ liệu để lưu trữ tệp (hoặc có thể là một loại cơ sở dữ liệu khác), sau đó tôi có thể làm như vậy mà không cần phải chạm vào logic ứng dụng chính - tất cả những gì tôi cần làm là viết một công cụ dòng lệnh nhỏ và một mô hình dữ liệu khác, sau đó "kết nối tất cả lại với nhau" và tôi đã sẵn sàng để đi.

Bạn có thể đang nghĩ "Chà, tôi sẽ không bao giờ muốn làm bất cứ điều gì vì vậy không thành vấn đề nếu tôi không thể trao đổi những thứ này xung quanh" - điểm thực sự là một trong những đặc điểm nổi bật của ứng dụng mô-đun là khả năng trích xuất 'chunk' (Không cần phải biên dịch lại bất cứ thứ gì) và sử dụng lại các đoạn đó ở nơi khác. Để viết mã như thế này, nó thường buộc bạn phải suy nghĩ lâu dài về các nguyên tắc thiết kế - Bạn sẽ cần suy nghĩ về việc viết nhiều giao diện hơn và suy nghĩ cẩn thận về sự đánh đổi của các nguyên tắc RẮN khác nhau (Tương tự theo cách bạn muốn cho Phát triển hành vi hoặc hướng phát triển hoặc TDD)

Đôi khi, việc đạt được sự phân tách này từ một khối mã nguyên khối hiện có là một chút đau đớn và đòi hỏi nhiều sự tái cấu trúc cẩn thận - điều đó ổn, bạn sẽ có thể làm điều đó tăng dần - thậm chí bạn có thể đạt đến điểm có quá nhiều tập hợp và bạn quyết định quay trở lại theo cách khác và bắt đầu bọc lại mọi thứ một lần nữa (đi quá xa theo hướng ngược lại có thể có tác dụng làm cho các hội đồng đó trở nên vô dụng theo cách riêng của chúng)


4

Theo cấu trúc thư mục, những gì bạn đề nghị là OK nói chung. Bạn có thể cần thêm thư mục cho tài nguyên (đôi khi mọi người tạo tài nguyên sao cho mỗi bộ tài nguyên được nhóm theo mã ngôn ngữ ISO để hỗ trợ nhiều ngôn ngữ), hình ảnh, tập lệnh cơ sở dữ liệu, tùy chọn người dùng (nếu không được xử lý dưới dạng tài nguyên), phông chữ , dlls bên ngoài, dlls địa phương, vv

Quy ước đặt tên khi thêm các lớp là gì?

Tất nhiên, bạn muốn tách từng lớp bên ngoài biểu mẫu. Tôi cũng muốn giới thiệu, một tệp cho mỗi lớp (mặc dù MS không làm điều đó trong mã được tạo cho EF chẳng hạn).

Nhiều người sử dụng danh từ ngắn có ý nghĩa ở số nhiều (ví dụ: Khách hàng). Một số ptrfer tên gần với tên số ít cho bảng cơ sở dữ liệu tương ứng (nếu bạn sử dụng ánh xạ 1-1 giữa các đối tượng và bảng).

Để đặt tên lớp có nhiều nguồn, ví dụ, hãy xem: .net Quy ước đặt tên và tiêu chuẩn lập trình - Thực tiễn tốt nhất và / hoặc Nguyên tắc mã hóa STOVF-C #

Phải làm gì để không phải tất cả mã cho biểu mẫu chính kết thúc trong Form1.cs

Mã trình trợ giúp nên đến một hoặc nhiều lớp trình trợ giúp. Các mã khác rất phổ biến đối với các điều khiển GUI như áp dụng tùy chọn người dùng vào lưới, có thể được xóa khỏi biểu mẫu và được thêm vào các lớp trợ giúp hoặc bằng cách phân lớp điều khiển trong câu hỏi và tạo các phương thức cần thiết ở đó.

Do tính chất hành động sự kiện của MS Windows Forms, không có gì tôi biết có thể giúp bạn lấy mã từ biểu mẫu chính mà không cần thêm sự mơ hồ và nỗ lực. Tuy nhiên, MVVM có thể là một lựa chọn (trong các dự án trong tương lai), xem ví dụ: MVVM cho Windows Forms .


2

Hãy coi MVP là một tùy chọn vì nó sẽ giúp bạn tổ chức logic trình bày, đó là tất cả mọi thứ trong các ứng dụng kinh doanh lớn. Trong thực tế, logic ứng dụng chủ yếu nằm trong cơ sở dữ liệu, do đó hiếm khi bạn có nhu cầu thực sự viết một lớp nghiệp vụ bằng mã gốc, chỉ để lại nó với chức năng trình bày có cấu trúc tốt.

Cấu trúc giống như MVP sẽ dẫn đến một tập hợp các trình bày hoặc bộ điều khiển nếu bạn thích, nó sẽ phối hợp với nhau và sẽ không trộn lẫn với UI hoặc mã phía sau công cụ. Bạn thậm chí có thể sử dụng các UI khác nhau với cùng một bộ điều khiển.

Cuối cùng, các tài nguyên tổ chức đơn giản, tách các thành phần và chỉ định các phụ thuộc với IoC và DI, cùng với cách tiếp cận MVP sẽ cung cấp cho bạn các chìa khóa để xây dựng một hệ thống tránh các lỗi phổ biến và phức tạp, được cung cấp kịp thời và thay đổi.


1

Cấu trúc của một dự án hoàn toàn phụ thuộc vào Dự án và kích thước của dự án, tuy nhiên bạn có thể thêm một vài thư mục, vd

  • Chung (Có các lớp, ví dụ: Tiện ích)
  • DataAccess (các lớp liên quan đến truy cập dữ liệu bằng sql hoặc bất kỳ máy chủ cơ sở dữ liệu nào khác mà bạn sử dụng)
  • Kiểu (Nếu bạn có bất kỳ tệp CSS nào trong dự án của bạn)
  • Tài nguyên (ví dụ: Hình ảnh, tệp tài nguyên)
  • WorkFlow (Các lớp liên quan đến quy trình làm việc nếu bạn có)

Bạn không cần đặt Biểu mẫu vào bất kỳ loại thư mục nào, chỉ cần đổi tên biểu mẫu của bạn cho phù hợp. Ý tôi là ý thức chung của nó không ai biết tên của bạn nên là hình thức của bạn tốt hơn sau đó chính bạn.

Quy ước đặt tên giống như nếu lớp của bạn in thông báo "Hello World" thì tên lớp phải là một cái gì đó liên quan đến nhiệm vụ và tên thích hợp của lớp nên HelloWorld.cs.

bạn cũng có thể tạo các vùng, vd

#region Hello và sau đó ra endregionở cuối.

Bạn có thể tạo các lớp cho các tab, khá chắc chắn là bạn có thể, và một điều cuối cùng là sử dụng lại mã của bạn khi có thể, cách tốt nhất là tạo phương thức và sử dụng lại chúng khi cần.

Sách ? erm.

Không có cuốn sách nào cho bạn biết cấu trúc của các dự án vì mỗi dự án là khác nhau, bạn học được những điều này bằng kinh nghiệm.

Hy vọng nó sẽ giúp!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.